文档结构  
翻译进度:已翻译     翻译赏金:0 元     ¥ 我要打赏

目标

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

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

代码

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

解释

  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 中的每个像素点。

第 2 段(可获 2 积分)

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

同时

内部执行一个

饱和

一个运算:得到的结果总是在允许的范围内(非负,而且我们的例程中取值在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
第 3 段(可获 2 积分)

文章评论