发 帖  
原厂入驻New
网上首套系列的9点标定教程《龙哥手把手教你学视觉-运动篇》——视觉工程师进阶必学!

[经验] 图像读取:机器视觉处理的测试程序

2019-6-19 08:30:00  187 机器视觉
分享
1
在机器视觉处理中,我们经常要对检测到的物体的方位特征进行评估。比如说,我们要 OCR 识别一个字符串。那么这个字符串与x轴的夹角就很重要,我们需要这个信息把这个字符串转正,然后才方便识别。
条形码识别也类似,尤其是当我们条形码不是很清晰时,首先将条形码转正,然后用各向异性的滤波器处理一下,可以让条形码变得更清晰易于读取。
这里给出一种基于统计参数的特征提取方法。这个方法已经有几十年历史了,算是个老方法,但是效果很不错,所以值得写篇文章来介绍介绍。
一片区域 R 的矩定义为:
当p 和q 都取 0 时,得到的就是这片区域的面积。也就是:
矩还可以归一化,也就是用上面的定义再除以面积 a。
表示的是这片区域的重心。可以用它来描述区域的位置。
归一化的矩回随区域在图像中的位置不同而变化,要去除这个影响,可以用中心矩,中心矩只反映区域本身的特征。
具体的方法是将这个区域当作一个椭圆区域,那么用上面5个参量就可以计算出椭圆的长短轴和旋转角度。具体公式如下:
椭圆的这几个参数的图形解释如下图:
利用这几个参数就可以确定区域的方位和尺寸了。
比如我们有下面的一幅测试图像。
用上面方法计算出的椭圆如下:
可以看出结果非常的好。尤其是旋转角度,计算的非常准确。
下面是我的测试代码,供参考。用到了些 Qt 的功能。
  1. #include
  2. #include
  3. #include
  4. #include
  5. #include "picturebox.h"
  6. #include

  7. QImage threshold(const QImage &image, quint8 th)
  8. {
  9. int height = image.height();
  10. int width = image.width();
  11. QImage ret(width, height, QImage::Format_Indexed8);
  12. ret.setColorCount(256);
  13. for(int i = 0; i < 256; i++)
  14. {
  15. ret.setColor(i, qRgb(i, i, i));
  16. }

  17. for(int i = 0; i < height; i ++)
  18. {
  19. const uchar *pSrc = (uchar *)image.constScanLine(i);
  20. uchar *pDest = (uchar *)ret.scanLine(i);
  21. for( int j = 0; j < width; j ++)
  22. {
  23. pDest[j] = (pSrc[j] > th)? 255: 0;
  24. }
  25. }
  26. return ret;
  27. }

  28. QImage toGray( const QImage &image )
  29. {
  30. int height = image.height();
  31. int width = image.width();
  32. QImage ret(width, height, QImage::Format_Indexed8);
  33. ret.setColorCount(256);
  34. for(int i = 0; i < 256; i++)
  35. {
  36. ret.setColor(i, qRgb(i, i, i));
  37. }

  38. qDebug () << image.format();
  39. switch(image.format())
  40. {
  41. case QImage::Format_Indexed8:
  42. case QImage::Format_Grayscale8:
  43. for(int i = 0; i < height; i ++)
  44. {
  45. const uchar *pSrc = (uchar *)image.constScanLine(i);
  46. uchar *pDest = (uchar *)ret.scanLine(i);
  47. memcpy(pDest, pSrc, width);
  48. }
  49. break;
  50. case QImage::Format_RGB32:
  51. case QImage::Format_ARGB32:
  52. case QImage::Format_ARGB32_Premultiplied:
  53. for(int i = 0; i < height; i ++)
  54. {
  55. const QRgb *pSrc = (QRgb *)image.constScanLine(i);
  56. uchar *pDest = (uchar *)ret.scanLine(i);

  57. for( int j = 0; j < width; j ++)
  58. {
  59. pDest[j] = qGray(pSrc[j]);
  60. }
  61. }
  62. break;
  63. }
  64. return ret;
  65. }

  66. QPointF center(const QImage &image, int value)
  67. {
  68. IF(image.isNull() || image.format() != QImage::Format_Indexed8)
  69. {
  70. return QPointF(-1, -1);
  71. }
  72. int width = image.width();
  73. int height = image.height();
  74. int x_mean = 0;
  75. int y_mean = 0;
  76. int count = 0;
  77. for(int j = 0; j < height; j ++)
  78. {
  79. const uchar * p = image.constScanLine(j);
  80. for(int i = 0; i < width; i++)
  81. {
  82. if( p[i] == value )
  83. {
  84. x_mean += i;
  85. y_mean += j;
  86. count++;
  87. }
  88. }
  89. }
  90. return QPointF((double)x_mean / count, (double)y_mean / count);

  91. }
  92. struct ELLIPSE_PARA
  93. {
  94. double x_mean; //椭圆的中心坐标 x
  95. double y_mean; //椭圆的中心坐标 y
  96. double r1; //椭圆的长轴半径
  97. double r2; //椭圆的短轴半径
  98. double theta; //椭圆的长轴与 x 轴的夹角(逆时针)
  99. };

  100. /**
  101. * [url=home.php?mod=space&uid=2666770]@Brief[/url] ellipseFit 将一片区域当作椭圆来估计五个几何参数
  102. * [url=home.php?mod=space&uid=3142012]@param[/url] image
  103. * @param value
  104. * @param para
  105. */
  106. bool ellipseFit(const QImage &image, int value, ELLIPSE_PARA * para)
  107. {
  108. if(image.isNull() || image.format() != QImage::Format_Indexed8)
  109. {
  110. return false;
  111. }
  112. QPointF c = center(image, value);

  113. int width = image.width();
  114. int height = image.height();
  115. double n01 = c.x();
  116. double n10 = c.y();

  117. double mu20 = 0.0;
  118. double mu02 = 0.0;
  119. double mu11 = 0.0;

  120. int count = 0;
  121. for(int row = 0; row < height; row ++)
  122. {
  123. const uchar * p = image.constScanLine(row);
  124. for(int col = 0; col < width; col++)
  125. {
  126. if( p[col] == value )
  127. {
  128. mu02 += (col - n01) * (col - n01);
  129. mu20 += (row - n10) * (row - n10);
  130. mu11 += (col - n01) * (row - n10);
  131. count ++;
  132. }
  133. }
  134. }
  135. if(count == 0)
  136. {
  137. return false;
  138. }
  139. mu20 /= count;
  140. mu02 /= count;
  141. mu11 /= count;

  142. double t1 = mu20 + mu02;
  143. double t2 = mu20 - mu02;
  144. double t3 = sqrt(t2 * t2 + 4 * mu11 * mu11);
  145. double r1 = sqrt(2 * ( t1 + t3) );
  146. double r2 = sqrt(2 * ( t1 - t3) );

  147. double theta = - atan2(2 * mu11, mu02 - mu20) / 2.0;

  148. para->r1 = r1;
  149. para->r2 = r2;
  150. para->theta = theta;
  151. para->x_mean = n01;
  152. para->y_mean = n10;

  153. return true;
  154. }

  155. int main(int argc, char *argv[])
  156. {
  157. QApplicaTIon a(argc, argv);
  158. QImage image("D:/test55.png");
  159. QImage imageGray = toGray(image);
  160. //imageGray = threshold(imageGray, 128);
  161. ELLIPSE_PARA para;
  162. ellipseFit(imageGray, 0, ¶);

  163. qDebug() << para.r1;
  164. qDebug() << para.r2;
  165. qDebug() << para.theta * 180 / 3.14159;
  166. QPointF c(para.x_mean, para.y_mean);
  167. qDebug() << c;
  168. QPainter painter(&image);
  169. painter.setPen(Qt::red);
  170. painter.translate(c);
  171. painter.rotate(-para.theta * 180 / 3.14159);
  172. painter.drawEllipse(QPointF(0, 0), para.r1, para.r2 );

  173. PictureBox box;
  174. box.seTImage(image);
  175. box.show();
  176. return a.exec();
  177. }
复制代码


评论

高级模式
您需要登录后才可以回帖 登录 | 注册

发经验
课程
    关闭

    站长推荐 上一条 /10 下一条

    快速回复 返回顶部 返回列表