OpenCV計算機視覺開發實踐:基于Qt C++ - 商品搜索 - 京東
OpenCV中除了提供繪制各種圖形的函數外,還提供了一個特殊的繪制函數,用于在圖像上繪制文字。這個函數是putText(),它是命名空間cv中的函數,其聲明如下:
void cv::putText(cv::Mat& img, ???????? // 待繪制的圖像const string& text, ?? // 待繪制的文字cv::Point origin, ???? // 文本框的左下角int fontFace, ???????? // 字體 (如cv::FONT_HERSHEY_PLAIN)double fontScale, ???? // 尺寸因子,值越大,文字就越大cv::Scalar color, ???? // 線條的顏色(RGB)int thickness = 1, ??? // 線條寬度int lineType = 8, ???? // 線型(4鄰域或8鄰域,默認為8鄰域)bool bottomLeftOrigin = false // true='origin at lower left'
);
這個函數可以簡單地在圖像上繪制一些文字,由text指定的文字將在以左上角為原點的文字框中以color指定的顏色繪制出來,除非bottomLeftOrigin標志設置為真,這種情況以左下角為原點,使用的字體由fontFace參數決定。常用的字體宏是FONT_HERSHEY_SIMPLEX(普通大小無襯線字體)和FONT_HERSHEY_PLAIN(小號無襯線字體)。任何一個字體都可以和CV::FONT_ITALIC 組合使用(通過“或”操作,或操作符號是|)來得到斜體。每種字體都有一個“自然”大小,當fontScale不是1.0時,在文字繪制之前字體大小將由這個數來縮放。
這里解釋一下襯線。襯線指的是字母結構筆畫之外的裝飾性筆畫。有襯線的字體叫襯線體(Serif),沒有襯線的字體叫無襯線體(Sans-Serif)。襯線體的特征是在字的筆畫開始、結束的地方有額外的裝飾,而且筆畫的粗細會有所不同。襯線體很容易識別,它強調了每個字母筆畫的開始和結束,因此易讀性比較高。中文字體中的宋體就是一種標準的襯線體。無襯線體(Sans-Serif Font)沒有額外的裝飾,而且筆畫的粗細差不多。這類字體通常是機械的和統一線條的,它們往往擁有相同的曲率、筆直的線條和銳利的轉角。無襯線體與漢字字體中的黑體相對應。
另外,在實際繪制文字之前,還可以使用cv::getTextSize()接口先獲取待繪制文本框的大小,以方便放置文本框。getTextSize函數可以獲取字符串的寬度和高度,該函數聲明如下:
Size cv::getTextSize(const string& text,cv::Point origin,int fontFace,double fontScale,int thickness,int* baseLine);
其中參數text表示輸入的文本文字;fontFace表示文字字體類型;fontScale表示字體縮放系數;thickness表示字體筆畫線寬;baseLine是一個輸出參數,表示文字最底部的y坐標。函數返回值中包含文本框的大小。
【例4.12】繪制文字
?? 新建一個控制臺工程,工程名是test。
?? 打開main.cpp,輸入如下代碼:
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include<opencv2\imgproc.hpp>
using namespace std;
using namespace cv;int main()
{string text = "Funny text inside the box";// int fontFace = FONT_HERSHEY_SCRIPT_SIMPLEX; // 手寫風格字體int fontFace = FONT_HERSHEY_SCRIPT_COMPLEX;double fontScale = 2; // 字體縮放比int thickness = 3;Mat img(600, 800, CV_8UC3, Scalar::all(0));int baseline = 0;Size textSize = getTextSize(text, fontFace, fontScale, thickness, &baseline);baseline += thickness;// center the textPoint textOrg((img.cols - textSize.width) / 2, (img.rows - textSize.height) / 2);// draw the boxrectangle(img, textOrg + Point(0, baseline), textOrg + Point(textSize.width, -textSize.height), Scalar(0, 0, 255));line(img, textOrg + Point(0, thickness), textOrg + Point(textSize.width, thickness), Scalar(0, 0, 255));putText(img, text, textOrg, fontFace, fontScale, Scalar::all(255), thickness, 8);imshow("text", img);waitKey(0);return 0;
}
在上述代碼中,我們通過getTextSize函數獲取包含字體的文本框的大小,并畫線顯示在矩形中。最后通過文本繪制函數putText畫出一段字符串“Funny text inside the box”。
?? 保存工程并運行,結果如圖4-8所示。
圖4-8
下面我們通過一個比較綜合的例子來實現隨機畫圖,比如隨機畫線,隨機畫圓,隨機繪制文本等。
【例4.13】綜合實例:隨機畫圖
?? 新建一個控制臺工程,工程名是test。
?? 打開main.cpp,輸入如下代碼:
#include <opencv2/opencv.hpp>
using namespace cv;
#include<iostream>
using namespace std;
#define FALSE 0
#define TRUE 1#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>
#include <stdio.h>
using namespace cv;
const int NUMBER = 100;
const int DELAY = 5;
const int window_width = 900;
const int window_height = 600;
int x_1 = -window_width / 2;
int x_2 = window_width * 3 / 2;
int y_1 = -window_width / 2;
int y_2 = window_width * 3 / 2;
static Scalar randomColor(RNG& rng);
int Drawing_Random_Lines(Mat image, char* window_name, RNG rng);
int Drawing_Random_Rectangles(Mat image, char* window_name, RNG rng);
int Drawing_Random_Ellipses(Mat image, char* window_name, RNG rng);
int Drawing_Random_Polylines(Mat image, char* window_name, RNG rng);
int Drawing_Random_Filled_Polygons(Mat image, char* window_name, RNG rng);
int Drawing_Random_Circles(Mat image, char* window_name, RNG rng);
int Displaying_Random_Text(Mat image, char* window_name, RNG rng);
int Displaying_Big_End(Mat image, char* window_name, RNG rng);
int main(void)
{int c;char window_name[] = "Drawing_2 Tutorial";RNG rng(0xFFFFFFFF);Mat image = Mat::zeros(window_height, window_width, CV_8UC3); // 創建一個初始化為零的矩陣imshow(window_name, image);waitKey(DELAY);// 然后我們開始隨機畫圖c = Drawing_Random_Lines(image, window_name, rng);if (c != 0) return 0;c = Drawing_Random_Rectangles(image, window_name, rng);if (c != 0) return 0;c = Drawing_Random_Ellipses(image, window_name, rng);if (c != 0) return 0;c = Drawing_Random_Polylines(image, window_name, rng);if (c != 0) return 0;c = Drawing_Random_Filled_Polygons(image, window_name, rng);if (c != 0) return 0;c = Drawing_Random_Circles(image, window_name, rng);if (c != 0) return 0;c = Displaying_Random_Text(image, window_name, rng);if (c != 0) return 0;c = Displaying_Big_End(image, window_name, rng);if (c != 0) return 0;waitKey(0);return 0;
}
static Scalar randomColor(RNG& rng)
{int icolor = (unsigned)rng;return Scalar(icolor & 255, (icolor >> 8) & 255, (icolor >> 16) & 255);
}
int Drawing_Random_Lines(Mat image, char* window_name, RNG rng)
{Point pt1, pt2;for (int i = 0; i < NUMBER; i++){pt1.x = rng.uniform(x_1, x_2);pt1.y = rng.uniform(y_1, y_2);pt2.x = rng.uniform(x_1, x_2);pt2.y = rng.uniform(y_1, y_2);line(image, pt1, pt2, randomColor(rng), rng.uniform(1, 10), 8);imshow(window_name, image);if (waitKey(DELAY) >= 0){return -1;}}return 0;
}
int Drawing_Random_Rectangles(Mat image, char* window_name, RNG rng)
{Point pt1, pt2;int lineType = 8;int thickness = rng.uniform(-3, 10);for (int i = 0; i < NUMBER; i++){pt1.x = rng.uniform(x_1, x_2);pt1.y = rng.uniform(y_1, y_2);pt2.x = rng.uniform(x_1, x_2);pt2.y = rng.uniform(y_1, y_2);rectangle(image, pt1, pt2, randomColor(rng), MAX(thickness, -1), lineType);imshow(window_name, image);if (waitKey(DELAY) >= 0){return -1;}}return 0;
}
int Drawing_Random_Ellipses(Mat image, char* window_name, RNG rng)
{int lineType = 8;for (int i = 0; i < NUMBER; i++){Point center;center.x = rng.uniform(x_1, x_2);center.y = rng.uniform(y_1, y_2);Size axes;axes.width = rng.uniform(0, 200);axes.height = rng.uniform(0, 200);double angle = rng.uniform(0, 180);ellipse(image, center, axes, angle, angle - 100, angle + 200,randomColor(rng), rng.uniform(-1, 9), lineType);imshow(window_name, image);if (waitKey(DELAY) >= 0){return -1;}}return 0;
}
int Drawing_Random_Polylines(Mat image, char* window_name, RNG rng)
{int lineType = 8;for (int i = 0; i < NUMBER; i++){Point pt[2][3];pt[0][0].x = rng.uniform(x_1, x_2);pt[0][0].y = rng.uniform(y_1, y_2);pt[0][1].x = rng.uniform(x_1, x_2);pt[0][1].y = rng.uniform(y_1, y_2);pt[0][2].x = rng.uniform(x_1, x_2);pt[0][2].y = rng.uniform(y_1, y_2);pt[1][0].x = rng.uniform(x_1, x_2);pt[1][0].y = rng.uniform(y_1, y_2);pt[1][1].x = rng.uniform(x_1, x_2);pt[1][1].y = rng.uniform(y_1, y_2);pt[1][2].x = rng.uniform(x_1, x_2);pt[1][2].y = rng.uniform(y_1, y_2);const Point* ppt[2] = { pt[0], pt[1] };int npt[] = { 3, 3 };polylines(image, ppt, npt, 2, true, randomColor(rng), rng.uniform(1, 10), lineType);imshow(window_name, image);if (waitKey(DELAY) >= 0){return -1;}}return 0;
}
int Drawing_Random_Filled_Polygons(Mat image, char* window_name, RNG rng)
{int lineType = 8;for (int i = 0; i < NUMBER; i++){Point pt[2][3];pt[0][0].x = rng.uniform(x_1, x_2);pt[0][0].y = rng.uniform(y_1, y_2);pt[0][1].x = rng.uniform(x_1, x_2);pt[0][1].y = rng.uniform(y_1, y_2);pt[0][2].x = rng.uniform(x_1, x_2);pt[0][2].y = rng.uniform(y_1, y_2);pt[1][0].x = rng.uniform(x_1, x_2);pt[1][0].y = rng.uniform(y_1, y_2);pt[1][1].x = rng.uniform(x_1, x_2);pt[1][1].y = rng.uniform(y_1, y_2);pt[1][2].x = rng.uniform(x_1, x_2);pt[1][2].y = rng.uniform(y_1, y_2);const Point* ppt[2] = { pt[0], pt[1] };int npt[] = { 3, 3 };fillPoly(image, ppt, npt, 2, randomColor(rng), lineType);imshow(window_name, image);if (waitKey(DELAY) >= 0){return -1;}}return 0;
}
int Drawing_Random_Circles(Mat image, char* window_name, RNG rng)
{int lineType = 8;for (int i = 0; i < NUMBER; i++){Point center;center.x = rng.uniform(x_1, x_2);center.y = rng.uniform(y_1, y_2);circle(image, center, rng.uniform(0, 300), randomColor(rng),rng.uniform(-1, 9), lineType);imshow(window_name, image);if (waitKey(DELAY) >= 0){return -1;}}return 0;
}
int Displaying_Random_Text(Mat image, char* window_name, RNG rng)
{int lineType = 8;for (int i = 1; i < NUMBER; i++){Point org;org.x = rng.uniform(x_1, x_2);org.y = rng.uniform(y_1, y_2);putText(image, "Testing text rendering", org, rng.uniform(0, 8),rng.uniform(0, 100)*0.05 + 0.1, randomColor(rng), rng.uniform(1, 10), lineType);imshow(window_name, image);if (waitKey(DELAY) >= 0){return -1;}}return 0;
}
int Displaying_Big_End(Mat image, char* window_name, RNG)
{Size textsize = getTextSize("OpenCV forever!", FONT_HERSHEY_COMPLEX, 3, 5, 0);Point org((window_width - textsize.width) / 2, (window_height - textsize.height) / 2);int lineType = 8;Mat image2;for (int i = 0; i < 255; i += 2){image2 = image - Scalar::all(i);putText(image2, "OpenCV forever!", org, FONT_HERSHEY_COMPLEX, 3,Scalar(i, i, 255), 5, lineType);imshow(window_name, image2);if (waitKey(DELAY) >= 0){return -1;}}return 0;
}
從上面代碼可以看到,在主函數中,要做的第一件事是創建一個隨機數生成器對象:RNG rng(0xFFFFFFFF);。在本例中,RNG使用值0xFFFFFFFF來進行初始化。
然后創建一個初始化為零的矩陣image(這意味著它將顯示為黑色),并指定其高度、寬度和類型。
接著開始隨機畫圖。查看代碼可以看到,這里主要是8個自定義函數。這些函數都遵循相同的模式,因此我們只分析其中幾個函數,因為相同的解釋適用于所有函數。比如函數Drawing_Random_Lines用來在隨機坐標處畫線,因此畫線函數line的首尾點pt1和pt2的坐標都是隨機生成的。同樣地,函數Drawing_Random_Circles用來隨機畫圓;函數Displaying_Random_Text用來繪制文本,并且文本的位置、顏色等也是隨機產生的;函數Displaying_Big_End用來繪制程序結束時所顯示的文本“OpenCV forever!”。
?? 保存工程并運行,結果如圖4-9所示。
圖4-9