.. _Drawing_2: 随机数发生器&绘制文字 ************************************* 目的 ====== 本节你将学到: .. container:: enumeratevisibleitemswithsquare * 使用 *随机数发生器类* (:rng:`RNG <>`) 并得到均匀分布的随机数。 * 通过使用函数 :put_text:`putText <>` 显示文字。 代码 ===== .. container:: enumeratevisibleitemswithsquare * 在之前的章节中 (:ref:`Drawing_1`) 我们绘制过不同的几何图形, 我提供了一些绘制参数,比如 coordinates(坐标) (在绘制点 :point:`Points <>` 的时候 ), color(颜色), thickness(线条-粗细,点-大小), 等等... ,你会发现我们给出了这些参数明确的数值。 * 在本章中, 我们会试着赋予这些参数 *random随机* 的数值。 并且, 我们会试图在图像上绘制大量的几何图形. 因为我们将用随机的方式初始化这些图形, 这个过程将很自然的用到 *loops循环* . * 本代码在OpenCV的sample文件夹下,如果招不到,你可以从这里 `here 得到它: `_ . 说明 ============ #. 让我们检视 *main* 函数。我们发现第一步是实例化一个 *Random Number Generator(随机数发生器对象)* (RNG): .. code-block:: cpp RNG rng( 0xFFFFFFFF ); RNG的实现了一个随机数发生器。 在上面的例子中, *rng* 是用数值 *0xFFFFFFFF* 来实例化的一个RNG对象。 #. 然后我们初始化一个 *0* 矩阵(代表一个全黑的图像), 并且指定它的宽度,高度,和像素格式: .. code-block:: cpp /// 初始化一个0矩阵 Mat image = Mat::zeros( window_height, window_width, CV_8UC3 ); /// 把它会知道一个窗口中 imshow( window_name, image ); #. 然后我们开始疯狂的绘制。看过代码时候你会发现它主要分八个部分,正如函数定义的一样: .. code-block:: cpp /// 现在我们先画线 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; /// Displaying the big end! c = Displaying_Big_End( image, window_name, rng ); 所有这些范数都遵循相同的模式,所以我们只分析其中的一组,因为这适用于所有。 #. 查看函数 **Drawing_Random_Lines**: .. code-block:: cpp int Drawing_Random_Lines( Mat image, char* window_name, RNG rng ) { int lineType = 8; 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; } 我们可以看到: * *for* 循环将重复 **NUMBER** 次。 并且函数 :line:`line <>` 在循环中, 这意味着要生成 **NUMBER** 条线段。 * 线段的两个端点分别是 *pt1* 和 *pt2*. 对于 *pt1* 我们看到: .. code-block:: cpp pt1.x = rng.uniform( x_1, x_2 ); pt1.y = rng.uniform( y_1, y_2 ); * 我们知道 **rng** 是一个 *随机数生成器* 对象。在上面的代码中我们调用了 **rng.uniform(a,b)** 。这指定了一个在 **a** 和 **b** 之间的均匀分布(包含 **a**, 但不含 **b**)。 * 由上面的说明,我们可以推断出 *pt1* 和 *pt2* 将会是随机的数值,因此产生的线段是变幻不定的,这会产生一个很好的视觉效果(从下面绘制的图片可以看出)。 * 我们还可以发现, 在 :line:`line <>` 的参数设置中,对于 *color* 的设置我们用了: .. code-block:: cpp randomColor(rng) 让我们来看看函数的实现: .. code-block:: cpp static Scalar randomColor( RNG& rng ) { int icolor = (unsigned) rng; return Scalar( icolor&255, (icolor>>8)&255, (icolor>>16)&255 ); } 正如我们看到的,函数的返回值是一个用三个随机数初始化的 *Scalar* 对象,这三个随机数代表了颜色的 *R*, *G*, *B* 分量。所以,线段的颜色也是随机的! #. 上面的解释同样适用于其它的几何图形,比如说参数 *center(圆心)* 和 *vertices(顶点)* 也是随机的。 #. 在结束之前,我们还应该看看函数 *Display_Random_Text* 和 *Displaying_Big_End*, 因为它们有一些有趣的特征: #. **Display_Random_Text:** .. code-block:: cpp 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; } 这些看起来都很熟悉,但是这一句: .. code-block:: cpp 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); 函数 :put_text:`putText <>` 都做了些什么?在我们的例子中: .. container:: enumeratevisibleitemswithsquare * 在 **image** 上绘制文字 **"Testing text rendering"** 。 * 文字的左下角将用点 **org** 指定。 * 字体参数是用一个在 :math:`[0, 8>` 之间的整数来定义。 * 字体的缩放比例是用表达式 **rng.uniform(0, 100)x0.05 + 0.1** 指定(表示它的范围是 :math:`[0.1, 5.1>`)。 * 字体的颜色是随机的 (记为 **randomColor(rng)**)。 * 字体的粗细范围是从 1 到 10, 表示为 **rng.uniform(1,10)** 。 因此, 我们将绘制 (与其余函数类似) **NUMBER** 个文字到我们的图片上,以位置随机的方式。 #. **Displaying_Big_End** .. code-block:: cpp int Displaying_Big_End( Mat image, char* window_name, RNG rng ) { Size textsize = getTextSize("OpenCV forever!", CV_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, CV_FONT_HERSHEY_COMPLEX, 3, Scalar(i, i, 255), 5, lineType ); imshow( window_name, image2 ); if( waitKey(DELAY) >= 0 ) { return -1; } } return 0; } 除了 **getTextSize** (用于获取文字的大小参数), 我们可以发现在 *for* 循环里的新操作: .. code-block:: cpp image2 = image - Scalar::all(i) **image2** 是 **image** 和 **Scalar::all(i)** 的差。事实上,**image2** 的每个像素都是 **image** 的每个像素减去 **i** (对于每个像素,都是由R,G,B三个分量组成,每个分量都会独立做差)的差。 我们还要知道,减法操作 *总是* 保证是 **合理** 的操作, 这表明结果总是在合理的范围内 (这个例子里结果不会为负数,并且保证在 0~255的合理范围内)。 结果 ======== 正如你在代码部分看到的, 程序将依次执行不同的绘图函数,这将: #. 首先 *NUMBER* 条线段将出现在屏幕上,正如截图所示: .. image:: images/Drawing_2_Tutorial_Result_0.jpg :alt: Drawing Tutorial 2 - Final Result 0 :align: center #. 然后,一个新的图形,这次是一些矩形: #. 现在,一些弧线会出现,每一个弧线都有随机的位置,大小,边缘的粗细和弧长: .. image:: images/Drawing_2_Tutorial_Result_2.jpg :alt: Drawing Tutorial 2 - Final Result 2 :align: center #. 现在,带有三个参数的 *polylines(折线)* 将会出现在屏幕上,同样以随机的方式: .. image:: images/Drawing_2_Tutorial_Result_3.jpg :alt: Drawing Tutorial 2 - Final Result 3 :align: center #. 填充的多边形 (这里是三角形) 会出现. #. 最后出现的图形:圆 .. image:: images/Drawing_2_Tutorial_Result_5.jpg :alt: Drawing Tutorial 2 - Final Result 5 :align: center #. 在结尾处,文字 *"Testing Text Rendering"* 将会以不同的字体,大小,颜色和位置出现在屏幕上。 #. 最后 (这也顺便表达了OpenCV的宗旨): .. image:: images/Drawing_2_Tutorial_Result_7.jpg :alt: Drawing Tutorial 2 - Final Result 7 :align: center 翻译者 ================= zxzx74147@ `OpenCV中文网站 `_