11、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,其坐标值xy均为随机值。

rng.uniform函数的原型为:

int cv::RNG::uniform(int 	a,
					 int 	b)	
  • 参数 a: 生成随机数的下边界,包含值a在内
  • 参数 b: 生成随机数的上边界,不包括值b在内

cv::RNG::uniform函数返回[a,b)范围内均匀分布的整数随机数。该方法使用MWC算法对状态进行转换,并从范围[a, b)中返回由输入参数类型推导出的下一个指定类型的均匀分布随机数。这个函数还有另外两种重载形式,与上述函数的唯一区别就在于接受参数的类型分别为 floatdouble 类型。下面的例子说明了其中的细微差别:

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_SMALLFONT_HERSHEY_COMPLEX的小版本
FONT_HERSHEY_SCRIPT_SIMPLEX手写风格的字体
FONT_HERSHEY_SCRIPT_COMPLEXFONT_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!
在这里插入图片描述

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值