• 机器视觉原生算法入门


    本文以边缘检测为例,提供一个机器视觉原生算法的入门案例。效果如下图,左边是源图片,右边是检测结果:

          

    基本思路:逐行扫描探测边缘;对每行的边缘位置做直线拟合;使用方差剔除缺损点。

    边缘探测算法:使用类似【2, 2, 2, -2, -2, -2】的卷积算子,在边缘位置,卷积值最大,利用最大值两边的次大值作比例均衡,提高精度。

    废话不多说了,上代码:

      1 class CDetectVHLine
      2 {
      3 public:
      4     CDetectVHLine(BYTE* image, int width, int height, int stride,
      5         int left, int right, int bottom, int top,
      6         int halfEdge, bool horizontal, bool blackToWhite)
      7     {
      8         m_image = image;
      9         m_width = width;
     10         m_height = height;
     11         m_stride = stride;
     12 
     13         m_left = left;
     14         m_right = right;
     15         m_bottom = bottom;
     16         m_top = top;
     17 
     18         m_halfEdge = halfEdge;
     19         m_horizontal = horizontal;
     20         m_blackToWhite = blackToWhite;
     21 
     22         // 生成检测算子
     23         m_factor.resize(m_halfEdge);
     24         for (int i = 0; i < m_halfEdge; ++i){
     25             m_factor[i] = 2;
     26         }
     27 
     28         // 卷积区间要去除边缘
     29         if (m_horizontal) {
     30             m_bottom += m_halfEdge;
     31             m_top -= m_halfEdge;
     32         }
     33         else {
     34             m_left += m_halfEdge;
     35             m_right -= m_halfEdge;
     36         }
     37     }
     38     ~CDetectVHLine(){}
     39 
     40     bool Detect()
     41     {
     42         if (m_horizontal){
     43             vector<double>    edges;                    // 保存计算结果,每个扫描线的边缘Y坐标
     44             edges.resize(m_right - m_left + 1);
     45 
     46             // 逐列扫描边缘
     47             for (int i = m_left, idx = 0; i <= m_right; ++i, idx++){
     48                 edges[idx] = scanLineV(i, m_bottom, m_top);
     49             }
     50             if (!fitLine(m_left, edges, m_k, m_b)){
     51                 return false;
     52             }
     53             m_x1 = m_left;
     54             m_y1 = m_k * m_x1 + m_b;
     55             m_x2 = m_right + 1;
     56             m_y2 = m_k * m_x2 + m_b;
     57         }
     58         else{
     59             vector<double> edges;
     60             edges.resize(m_top - m_bottom + 1);
     61 
     62             // 逐行扫描边缘
     63             for (int i = m_bottom, idx = 0; i <= m_top; ++i, idx++){
     64                 edges[idx] = scanLineH(i, m_left, m_right);
     65             }
     66             if (!fitLine(m_bottom, edges, m_k, m_b)){
     67                 return false;
     68             }
     69             m_y1 = m_bottom;
     70             m_x1 = m_k * m_y1 + m_b;
     71             m_y2 = m_top + 1;
     72             m_x2 = m_k* m_y2 + m_b;
     73         }
     74 
     75         return true;
     76     }
     77 
     78     // 检测结果
     79     double m_x1, m_y1, m_x2, m_y2;
     80     double m_k, m_b;
     81 
     82 private:
     83     BYTE*    m_image;
     84     int        m_width, m_height;                            // 图像尺寸
     85     int        m_stride;                                    // 扫描行字节数
     86     int        m_left, m_right, m_bottom, m_top;            // 探测区域
     87     int        m_halfEdge;                                    // 边缘宽度的一半:像素
     88     bool    m_horizontal;                                // true:水平线;false:竖直线
     89     bool    m_blackToWhite;                                // true:左黑右白/下黑上白;false:左白右黑/下白上黑
     90     vector<int>    m_factor;                                // 对称边缘检测算子正半部分
     91 
     92     const    int    c_productThresh = 100;                    // 边缘卷积阈值,小于该值则不认为是边缘
     93 
     94     // 水平方向扫描检测边缘
     95     double scanLineH(int y, int left, int right)
     96     {
     97         int maxL = 0, maxM = 0, maxR = 0, maxX;
     98         int currL = 0, currM = 0, currR = 0;
     99         BYTE* pData = m_image + m_stride * y + left;
    100         for (int i = left; i <= right; ++i){
    101             currR = m_blackToWhite ? sumProductH(pData) : -sumProductH(pData);
    102             if (currM >= currL && currM >= currR){            // 判定极值点
    103                 if (currM > maxM){
    104                     maxL = currL; maxM = currM; maxR = currR; maxX = i - 1;
    105                 }
    106             }
    107             currL = currM; currM = currR;
    108             pData++;
    109         }
    110 
    111         if (maxM < c_productThresh){
    112             return -1;
    113         }
    114 
    115         return maxX + (double)(maxM - maxL) / (maxM - maxL + maxM - maxR) - 0.5;
    116     }
    117 
    118     // 垂直方向扫描检测边缘
    119     double scanLineV(int x, int bottom, int top)
    120     {
    121         int maxB = 0, maxM = 0, maxT = 0, maxY;
    122         int currB = 0, currM = 0, currT = 0;
    123         BYTE* pData = m_image + m_stride * bottom + x;
    124         for (int i = bottom; i <= top; ++i){
    125             currT = m_blackToWhite ? sumProductV(pData) : -sumProductV(pData);
    126             if (currM >= currB && currM >= currT){            // 判定极值点
    127                 if (currM > maxM){
    128                     maxB= currB; maxM = currM; maxT = currT; maxY = i - 1;
    129                 }
    130             }
    131             currB = currM; currM = currT;
    132             pData += m_stride;
    133         }
    134 
    135         if (maxM < c_productThresh){
    136             return -1;
    137         }
    138 
    139         return maxY + (double)(maxM - maxB) / (maxM - maxB + maxM - maxT) - 0.5;
    140     }
    141 
    142     // 计算水平扫描卷积值,pData对应算子中心
    143     int    sumProductH(const BYTE* pData)
    144     {
    145         int product = 0;
    146         const BYTE* pDataN = pData - 1;
    147         for (size_t i = 0; i < m_factor.size(); ++i){
    148             product += (m_factor[i] * pData[0] - m_factor[i] * pDataN[0]);
    149             pData++;
    150             pDataN--;
    151         }
    152 
    153         return product;
    154     }
    155 
    156     // 计算垂直扫描卷积值,pData对应算子中心
    157     int    sumProductV(const BYTE* pData)
    158     {
    159         int product = 0;
    160         const BYTE* pDataN = pData - m_stride;
    161         for (size_t i = 0; i < m_factor.size(); ++i){
    162             product += (m_factor[i] * pData[0] - m_factor[i] * pDataN[0]);
    163             pData += m_stride;
    164             pDataN -= m_stride;
    165         }
    166 
    167         return product;
    168     }
    169 
    170     // 直线拟合
    171     bool fitLine(int start, vector<double> &points, double &k, double &b)
    172     {
    173         // 统计有效点数目
    174         int validCount = 0;
    175         for (size_t i = 0; i < points.size(); ++i){
    176             if (points[i] > 0){
    177                 validCount++;
    178             }
    179         }
    180         if (validCount < 2)
    181             return false;
    182 
    183         // 根据方差迭代
    184         const int iterCount = 3;    // 迭代次数
    185         for (int iterator = 0; iterator < iterCount; ++iterator){
    186             double sigmaX = 0, sigmaY = 0, sigmaX2 = 0, sigmaXY = 0;
    187             double X = start - 0.5;
    188             for (size_t i = 0; i < points.size(); ++i){
    189                 X += 1;
    190                 if (points[i] < 0){
    191                     continue;
    192                 }
    193                 sigmaX += X;
    194                 sigmaY += points[i];
    195                 sigmaXY += X * points[i];
    196                 sigmaX2 += X * X;
    197             }
    198             double denominator = (validCount * sigmaX2 - sigmaX * sigmaX);
    199             k = (validCount * sigmaXY - sigmaX * sigmaY) / denominator;
    200             b = (sigmaX2 * sigmaY - sigmaX * sigmaXY) / denominator;
    201 
    202             // 方差
    203             double e = 0;
    204             X = start - 0.5;
    205             for (size_t i = 0; i < points.size(); ++i){
    206                 X += 1;
    207                 if (points[i] < 0){
    208                     continue;
    209                 }
    210                 e += (points[i] - k * X - b) * (points[i] - k * X - b);
    211             }
    212             e /= validCount;
    213             if (e < 2){
    214                 break;
    215             }
    216             e = sqrt(e);
    217 
    218             // 剔除误差过大的点
    219             X = start - 0.5;
    220             for (size_t i = 0; i < points.size(); ++i){
    221                 X += 1;
    222                 if (points[i] < 0){
    223                     continue;
    224                 }
    225                 if (abs(points[i] - k * X - b) > e){
    226                     points[i] = -1;
    227                     validCount--;
    228                 }
    229             }
    230             if (validCount < 2){
    231                 break;
    232             }
    233         }
    234 
    235         return true;
    236     }
    237 };
  • 相关阅读:
    jQuery 选择器
    Linux vs Window
    Git是什么?
    JS三大经典变量命名法
    常用html、CSS、javascript前端命名规范
    Ajax中Get请求与Post请求的区别
    HTTP 请求方式: GET和POST的比较
    poj2195 Going Home
    bzoj1059 [ZJOI2007]矩阵游戏
    bzoj1191 [HNOI2006]超级英雄Hero
  • 原文地址:https://www.cnblogs.com/zhuyingchun/p/13641742.html
Copyright © 2020-2023  润新知