原文译文操作

Goals

In this tutorial you will learn how to:

  • Use the Random Number generator class (RNG) and how to get a random number from a uniform distribution.
  • Display text on an OpenCV window by using the function putText

Code

  • In the previous tutorial (Basic Drawing) we drew diverse geometric figures, giving as input parameters such as coordinates (in the form of Points), color, thickness, etc. You might have noticed that we gave specific values for these arguments.
  • In this tutorial, we intend to use random values for the drawing parameters. Also, we intend to populate our image with a big number of geometric figures. Since we will be initializing them in a random fashion, this process will be automatic and made by using loops .
  • This code is in your OpenCV sample folder. Otherwise you can grab it from here .

目标

在这篇教程中,你将会学习到:

  • 掌握使用随机数发生器类(RNG),以及如何从均匀分布中得到一个随机数字。
  • 通过使用函数putText,在OpenvCV的窗口中显示文本。

代码

  • 在以往的教程中(Basic Drawing),我们教大家绘画了不同的几何图形,是通过提供输入参数进行绘画的。参数例如坐标(以 Points类的形式提供)、颜色、粗细程度等。你可能会注意到,我们给出了的这些参数,都是具体的数值。
  • 在这篇的教程中,我们打算使用随机值去绘制我们所需要提供的参数。同时,我们也打算利用大量的几何图形去填充我们的图像。因为我们将要利用随机的方式进行初始化,所以这个过程将会是自动使用循环。
  • 本教程代码在你的OpenCV的例程文件夹内,可以找到。或者,你也可以从这里获得 (here)。
纠正翻译

Explanation

  1. Let’s start by checking out the main function. We observe that first thing we do is creating a Random Number Generator object (RNG):

    RNG rng( 0xFFFFFFFF );
    

    RNG implements a random number generator. In this example, rng is a RNG element initialized with the value 0xFFFFFFFF

  2. Then we create a matrix initialized to zeros (which means that it will appear as black), specifying its height, width and its type:

    /// Initialize a matrix filled with zeros
    Mat image = Mat::zeros( window_height, window_width, CV_8UC3 );
    
    /// Show it in a window during DELAY ms
    imshow( window_name, image );
    
  3. Then we proceed to draw crazy stuff. After taking a look at the code, you can see that it is mainly divided in 8 sections, defined as functions:

    /// Now, let's draw some lines
    c = Drawing_Random_Lines(image, window_name, rng);
    if( c != 0 ) return 0;
    
    /// Go on drawing, this time nice rectangles
    c = Drawing_Random_Rectangles(image, window_name, rng);
    if( c != 0 ) return 0;
    
    /// Draw some ellipses
    c = Drawing_Random_Ellipses( image, window_name, rng );
    if( c != 0 ) return 0;
    
    /// Now some polylines
    c = Drawing_Random_Polylines( image, window_name, rng );
    if( c != 0 ) return 0;
    
    /// Draw filled polygons
    c = Drawing_Random_Filled_Polygons( image, window_name, rng );
    if( c != 0 ) return 0;
    
    /// Draw circles
    c = Drawing_Random_Circles( image, window_name, rng );
    if( c != 0 ) return 0;
    
    /// Display text in random positions
    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 );
    

    All of these functions follow the same pattern, so we will analyze only a couple of them, since the same explanation applies for all.

  4. Checking out the function Drawing_Random_Lines:

    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;
    }
    

    We can observe the following:

    • The for loop will repeat NUMBER times. Since the function line is inside this loop, that means that NUMBER lines will be generated.

    • The line extremes are given by pt1 and pt2. For pt1 we can see that:

      pt1.x = rng.uniform( x_1, x_2 );
      pt1.y = rng.uniform( y_1, y_2 );
      
      • We know that rng is a Random number generator object. In the code above we are calling rng.uniform(a,b). This generates a randomly uniformed distribution between the values a and b (inclusive in a, exclusive in b).

      • From the explanation above, we deduce that the extremes pt1 and pt2 will be random values, so the lines positions will be quite impredictable, giving a nice visual effect (check out the Result section below).

      • As another observation, we notice that in the line arguments, for the color input we enter:

        randomColor(rng)
        

        Let’s check the function implementation:

        static Scalar randomColor( RNG& rng )
          {
          int icolor = (unsigned) rng;
          return Scalar( icolor&255, (icolor>>8)&255, (icolor>>16)&255 );
          }
        

        As we can see, the return value is an Scalar with 3 randomly initialized values, which are used as the R, G and B parameters for the line color. Hence, the color of the lines will be random too!

  5. The explanation above applies for the other functions generating circles, ellipses, polygons, etc. The parameters such as center and vertices are also generated randomly.

  6. Before finishing, we also should take a look at the functions Display_Random_Text and Displaying_Big_End, since they both have a few interesting features:

  7. Display_Random_Text:

    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;
    }
    

    Everything looks familiar but the expression:

    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);
    

    So, what does the function putText do? In our example:

    • Draws the text “Testing text rendering” in image
    • The bottom-left corner of the text will be located in the Point org
    • The font type is a random integer value in the range: [0, 8>.
    • The scale of the font is denoted by the expression rng.uniform(0, 100)x0.05 + 0.1 (meaning its range is: [0.1, 5.1>)
    • The text color is random (denoted by randomColor(rng))
    • The text thickness ranges between 1 and 10, as specified by rng.uniform(1,10)

    As a result, we will get (analagously to the other drawing functions) NUMBER texts over our image, in random locations.

  8. Displaying_Big_End

    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;
    }
    

    Besides the function getTextSize (which gets the size of the argument text), the new operation we can observe is inside the foor loop:

    image2 = image - Scalar::all(i)
    

    So, image2 is the substraction of image and Scalar::all(i). In fact, what happens here is that every pixel of image2 will be the result of substracting every pixel of image minus the value of i (remember that for each pixel we are considering three values such as R, G and B, so each of them will be affected)

解释

  1. 让我们从主函数main函数开始研究。我们可以看到,第一件我们要做的事就是创建一个随机数发生器对象(RNG):

    RNG rng( 0xFFFFFFFF );
    

    RNG 实现了一个随机数字发生器。在这个例程中,rng 就是一个以 0xFFFFFFFF 值初始化的RNG元素。

  2. 然后,我们创建一个零矩阵(元素全为0,意味着以黑色显示),指定它的高度、宽度以及类型:

    /// 创建并初始化一个零矩阵
    Mat image = Mat::zeros( window_height, window_width, CV_8UC3 );
    
    /// 在DELAY ms 时间内,将其显示在窗口中。
    imshow( window_name, image );
    
  3. 然后我们要继续去画一些疯狂的东西!对代码概览后,你能够看到程序主要分为8个部分,各部分都被定义成函数:

    /// 现在,让我们画写线条
    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;
    
    /// 现在画一些折线Now some polylines
    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 );
    

    所有的这些函数,都是使用模式都相同,所以我们将只去分析其中的一两个,因为应用于其他函数也同理。

  4. 研究函数Drawing_Random_Lines:

    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 函数在循环内部,所以意味着会生成NUMBER 条直线。

    • 直线的两端坐标,通过pt1 和 pt2 给定。研究其中的pt1 ,我们可以看到:

      pt1.x = rng.uniform( x_1, x_2 );
      pt1.y = rng.uniform( y_1, y_2 );
      
      • 我们知道rng 是一个随机数字发生器对象。在上面这段代码中,我们调用了rng.uniform(a,b)。这能够根据以值ab为边界(包括a,不包括b)的均匀分布,随机产生一个数值。

      • 通过上面的解释,我们推断出直线两端顶点 pt1 和 pt2 将会是随机的数值,所以直线的位置将会是不可预测的,结果会得到一个很好的视觉效果(下面的章节会给出结果)。

      • 观察其他的部分,我们会发现在line 函数的参数中,我们所从输入的color参数:

        randomColor(rng)
        

        Let’s check the function implementation:

        static Scalar randomColor( RNG& rng )
          {
          int icolor = (unsigned) rng;
          return Scalar( icolor&255, (icolor>>8)&255, (icolor>>16)&255 );
          }
        

        我们可以看到,颜色的返回值是一个根据 R, G 和B 参数形式和以3个随机值初始化的Scalar ,以该颜色结果作为直线的颜色。因此,直线的颜色同样是随机的呢!

  5. 上面函数内容的解释同样适用于生成圆形、椭圆、多边形等的函数中。输入参数中的centervertices 等参数都同样是随机生成的。

  6. 在结束之前,我们还应该看一下函数Display_Random_Text 和 Displaying_Big_End,因为他们都具有一些有趣的特性:

  7. Display_Random_Text:

    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;
    }
    

    这一切,看起来好熟悉,但是表达式:

    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);
    

    所以,函数 putText到底干了什么呢?在我们的例子中:

    • 图像中,画出文本“Testing text rendering”
    • 文本的左下角将会位于坐标点org
    • 文本字体类型是一个随机整型值,随机范围为: [0, 8>.
    • 字体大小通过表达式 rng.uniform(0, 100)x0.05 + 0.1 进行表示  (代表字体大小的取值范围为: [0.1, 5.1>)
    • 文本颜色是随机的(通过randomColor(rng) 表示
    • 字体的粗细范围在1到10,通过 rng.uniform(1,10)进行指定

    最终,我们将会得到(其他绘画函数同理)NUMBER 个文本,按随机的位置,出现在我们的图像中。

  8. Displaying_Big_End

    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 (该函数得到参数文本大小),我们可以看到在foor 循环中的新的运算:

    image2 = image - Scalar::all(i)
    

    所以,image2image 和 Scalar::all(i) 相减运算的结果。事实上,这里所完成的任务是将image 中的每个像素点减去i值(注意:每个像素点我们都以3个数值来进行表示,分别代表R,G和B。所以这三个值他们都将会被影响)做为结果成为image2 中的每个像素点。

纠正翻译

Also remember that the substraction operation

always

performs internally a

saturate

operation, which means that the result obtained will always be inside the allowed range (no negative and between 0 and 255 for our example).

Result

As you just saw in the Code section, the program will sequentially execute diverse drawing functions, which will produce:

  1. First a random set of NUMBER lines will appear on screen such as it can be seen in this screenshot:

    Drawing Tutorial 2 - Final Result 0
  2. Then, a new set of figures, these time rectangles will follow.

  3. Now some ellipses will appear, each of them with random position, size, thickness and arc length:

    Drawing Tutorial 2 - Final Result 2
  4. Now, polylines with 03 segments will appear on screen, again in random configurations.

    Drawing Tutorial 2 - Final Result 3
  5. Filled polygons (in this example triangles) will follow.

  6. The last geometric figure to appear: circles!

    Drawing Tutorial 2 - Final Result 5
  7. Near the end, the text “Testing Text Rendering” will appear in a variety of fonts, sizes, colors and positions.

  8. And the big end (which by the way expresses a big truth too):

    Drawing Tutorial 2 - Final Result 7

此外,记得在执行减法操作的

同时

内部执行一个

饱和

一个运算:得到的结果总是在允许的范围内(非负,而且我们的例程中取值在0到255之间)。

结果

正如你在代码章节中所见,这个程序将会按顺序执行不同的绘画函数,将会得到以下结果:

  1. 首先,数量为NUMBER  的随机直线集合将会出现在屏幕上,比如从这张截图中可以看到效果:

    Drawing Tutorial 2 - Final Result 0
  2. 然后,会出现新的图形集合,这次接着出现矩形。

  3. 现在,一些椭圆形出现了,它们中的每一个都是以随机的位置、大小、粗细、和 arc 长度 显示的。

    Drawing Tutorial 2 - Final Result 2
  4. 现在,03段折线将出现在屏幕上,同样是使用随机的参数。

    Drawing Tutorial 2 - Final Result 3
  5. 接着是填充多边形(在这个例子中是三角形)

  6. 最后的几何图形出现,圆圈!

    Drawing Tutorial 2 - Final Result 5
  7. 接近尾声,“Testing Text Rendering” 文字将会以各种各样的字体形式、大小、颜色和不同位置出现。

  8. 最后的大谢幕(顺便说句,这话也表达了出了真理):

    Drawing Tutorial 2 - Final Result 7
纠正翻译