OpenCV生成随机数、写文字
一、学习目标
- 学会使用OpenCV的RNG接口生成随机数
- 学会使用OpenCV在图像上书写文字
二、使用CV::RNG生成随机数
在之前的教程中,我们绘制了各种几何图形,其输入参数,如坐标(cv::Point的形式)、颜色、粗细等,我们为这些参数指定了特定的值。在本教程中,我们打算为绘图参数使用随机值。此外,我们打算用大量几何图形填充图像。因为我们将以随机方式初始化它们,这个过程将是自动的,并通过使用循环来完成。
首先,我们需要初始化一个随机数生成器对象(RNG):
RNG rng( 0xFFFFFFFF );
RNG实现了一个随机数生成器。在这个例子中,rng是一个初始值为0xFFFFFFFF的对象
RNG类有两个构造函数:
cv::RNG::RNG() // 无参构造函数
和:
cv::RNG::RNG(uint64 state) // 有参构造函数
第一种形式将state设置为一些预定义的值,在当前实现中等于232-1。第二种形式将状态设置为指定的值。如果传递state=0,构造函数将使用上面的默认值来代替,以避免由所有零组成的奇异随机数序列。
接下来,有了随机数生成器后,我们便可以调用生成器对象的接口生成随机数:
int min=0;
int max=100;
Point pt1;
pt1.x = rng.uniform( min, max);
pt1.y = rng.uniform( min, max);
对于点pt1,其坐标值x和y均为随机值。
rng.uniform函数的原型为:
int cv::RNG::uniform(int a,
int b)
- 参数 a: 生成随机数的下边界,包含值a在内
- 参数 b: 生成随机数的上边界,不包括值b在内
cv::RNG::uniform函数返回[a,b)范围内均匀分布的整数随机数。该方法使用MWC算法对状态进行转换,并从范围[a, b)中返回由输入参数类型推导出的下一个指定类型的均匀分布随机数。这个函数还有另外两种重载形式,与上述函数的唯一区别就在于接受参数的类型分别为 float 和 double 类型。下面的例子说明了其中的细微差别:
RNG rng;
// a的值一直是0,因为参数类型为整型,[0, 1)之间的整数取值为集合{0}
double a = rng.uniform(0, 1);
// a1的值是区间[0, 1)之间的均匀分布的double类型的小数
double a1 = rng.uniform((double)0, (double)1);
// b的值是区间[0, 1)之间的均匀分布的float类型的小数
float b = rng.uniform(0.f, 1.f);
// c的值是区间[0, 1)之间的均匀分布的double类型的小数
double c = rng.uniform(0., 1.);
// 由于参数类型的歧义性,下面的代码可能会产生编译错误
double d = rng.uniform(0, 0.999999);
三、在图像上书写文本
OpenCV提供了 cv::putText 函数作为接口,以便在图像上书写文本。其函数原型为:
void cv::putText(InputOutputArray img,
const String & text,
Point org,
int fontFace,
double fontScale,
Scalar color,
int thickness = 1,
int lineType = LINE_8,
bool bottomLeftOrigin = false)
- 参数 img: 书写文本的图像
- 参数 text: 要绘制的文本字符串
- 参数 org: 图像中文本字符串的左下角位置
- 参数 fontFace: 字体类型,枚举自HersheyFonts
- 参数 fontScale: 字体比例系数乘以特定字体的基础大小
- 参数 color: 字体的颜色
- 参数 thickness: 用于绘制文本的线条的粗细
- 参数 lineType: 字体的线条类型
- 参数 bottomLeftOrigin: 当为true时,图像数据坐标原点位于左下角。否则,它在左上角
函数cv::putText在图像中呈现指定的文本字符串,不能使用指定字体呈现的符号将被问号替换。字体的取值见下表:
参数取值 | 取值说明 |
---|---|
FONT_HERSHEY_SIMPLEX | 正常大小 |
FONT_HERSHEY_PLAIN | 小号无衬线字体 |
FONT_HERSHEY_DUPLEX | 正常大小无衬线字体(比FONT_HERSHEY_SIMPLEX更复杂) |
FONT_HERSHEY_COMPLEX | 正常大小的衬线字体 |
FONT_HERSHEY_TRIPLEX | 正常大小衬线字体(比FONT_HERSHEY_COMPLEX更复杂) |
FONT_HERSHEY_COMPLEX_SMALL | FONT_HERSHEY_COMPLEX的小版本 |
FONT_HERSHEY_SCRIPT_SIMPLEX | 手写风格的字体 |
FONT_HERSHEY_SCRIPT_COMPLEX | FONT_HERSHEY_SCRIPT_SIMPLEX的更复杂的变体 |
FONT_ITALIC | 斜体字体 |
其中,我们常用FONT_HERSHEY_SIMPLEX字体,其它字体可以在需要的时候查询即可。
使用实例:
Mat src = Mat::zeros...;
putText( src, "OpenCV forever!", Size(100,100), FONT_HERSHEY_COMPLEX,
3,Scalar(255, 255, 255), 5, LINE_8);
在某些应用中,我们可能需要搭配 cv::getTextSize 函数使用,其原型为:
Size cv::getTextSize(const String & text,
int fontFace,
double fontScale,
int thickness,
int * baseLine)
- 参数 text: 输入文本字符串
- 参数 fontFace: 使用的字体
- 参数 fontScale: 字体比例系数乘以特定字体的基础大小
- 参数 thickness: 文本的线条的粗细
- 参数 baseLine: 基线相对于最底部文本点的y坐标函数
cv::getTextSize计算并返回包含指定文本的框的大小。下面的代码实现了将文本绘制在图像的正中间,并在文本的周围紧贴了一个矩形框:
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
// 声明和初始化要显示的文本内容
String text = "Funny text inside the box";
// 定义字体、系数和字体线条的粗细
int fontFace = FONT_HERSHEY_SCRIPT_SIMPLEX;
double fontScale = 2;
int thickness = 3;
// 声明并初始化一个600*800的画布,背景颜色为黑色
Mat img(600, 800, CV_8UC3, Scalar::all(0));
int baseline = 0;
// 获取在指定文本、字体、系数和线条粗细的情况下的框体大小
Size textSize = getTextSize(text, fontFace, fontScale, thickness, &baseline);
baseline += thickness;
// 定义文本的位置,使文本框处于画布的正中间。注意结合理解:
// 在绘制文本时,用到的是文本框左下角的位置(不指定bottomLeftOrigin值为True的情况),要使文本框
// 处于画布中间,则需要让文本框的左下角位置偏移文本框大小一半的尺寸,正如下面公式所示
Point textOrg((img.cols - textSize.width) / 2, (img.rows + textSize.height) / 2);
// 由于文本线条的粗细占用了一定像素,在考虑偏移时需要注意
rectangle(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("demo", img);
waitKey(0);
system("pause");
return EXIT_SUCCESS;
}
四、完整代码示例
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
/// Global Variables
const int NUMBER = 100;
const int DELAY = 10;
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;
/// Function headers
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(int argc, char** argv)
{
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;
/// 在画布上随机绘制文本 Testing text rendering
c = Displaying_Random_Text(image, window_name, rng);
if (c != 0) return 0;
/// 在画布正中间绘制文本 OpenCV forever!
c = Displaying_Big_End(image, window_name, rng);
if (c != 0) return 0;
waitKey(0);
return EXIT_SUCCESS;
}
// 生成随机颜色
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;
}
首先,画布上开始随机绘制线条:
然后,画布上开始随机绘制矩形:
接下来,画布上随机绘制椭圆:
接下来,画布上随机绘制多边形轮廓:
接下来,画布上随机绘制填充多边形:
接下来,画布上随机绘制圆:
接下来,在画布上随机绘制文本:
最后,在画布正中间绘制文本 OpenCV forever! :