《opencv实战》 之 局部极值提取

Posted 寂寞的小乞丐

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《opencv实战》 之 局部极值提取相关的知识,希望对你有一定的参考价值。

 


 

局部极值提取算法

这是http://www.imagepy.org/的作者原创,我只是对其理解之后改进和说明,欢迎大家使用这个小软件!

有需要C++的朋友,可有留邮箱,经测试和改进的版本! 

 


 

算法原理:(按照程序思路来,当然其中很多可以改进的地方)

 

    第一步:    

        先进行距离变换(这类问题都是要通过几何操作进行,距离变换就是几何距离)

 

    第二步:

        先找全局可能最大值,然后对图像进行标记

        全局可能最大值是基础,3X3核在图像上滑动去找全局可能最大值。标记是为了掩膜操作更方便,你看opencv很多函数都有掩膜,PS上面也有那个白色的膜。     

        图片结果是:背景和最大值标记3,前景标记1,图像边界标记0(为了防止越界)
       貌似下面的图片显示有问题,matploylib的显示有时候确实有问题,Debug的时候就和显示不一样,没关系这一步比较简单。

 

    第三步:

        对全局可能最大值进行排除,这一步是程序核心!这里以求最大值为例,最小值相反。

          •   首先对找到的最大值进行排序--从大到小
          •   按照从大到小的顺序去寻找每个最大值的领地
          •   领地按照“目标范围”和“目标深度”两个原则,具体表现形式为:“不重合”、“不包括”、“领主大于等于领地”             

          下面以图说明两个大原则:

            目标范围:

                下图A1这个最大最他所占领的范围由用户输入,假设半径为y,则 x1 = x2 = y ,x1和x2的范围就是A1的范围,P点虽然很低,但是不属于A1的范围,所以不去占领。

 

                 目标深度:

                 
                下图A1的深度也又用户输入,假设输入为y,则 y1 = y,高度确定之后,那么P点虽然也在山脊上,但是不会被A1所占领。

                

 

          下面以图为例进一步说明三个具体形式:

          注释:三个具体形式是建立在两个原则之上的,所以理解上面是结局算法的基础。

            不重合形式:

                下图A1和A2有领土纠纷,但是A1峰值高所以先进行领地划分,当A2进行领地划分的时候一旦接触A1的领地就立马被消灭,最后只剩A1的存在。

            不包括形式:

                       下图A1先进行领土划分,过程中直接把A2消灭了,不存在A2的领土划分

            领主大于等于领地形式,也可以叫同等峰值先来先得形式:

                  由于A1先进行领土划分,所以后面的都被消灭了,读到这里你会发现这个程序的BUG,后面再详细谈论这一点

 

 

                 目标深度原则

                要确定和你预定的深度是不是符合,比如你想要山峰之间的深度都是在10cm以上,那么有些不符合的就得去除,去除原则肯定矮的GG

注释:大佬的程序没有几何距离,还有部分Bug,部分经过修正了。

 

 

 

 

 程序有待改进:

         这部分可以通过处理---“同样的峰值且山脉相连”的一类极值点,对其平均、覆盖范围最多等操作取一个中间的值!

         由于python用的不熟练,而且现在时间太晚了脑子转不动,以后用C++写的时候再改进这一点。

 

 

2017.10.17更新:

 

      1.对上面第二步:可能最大值进行补充

        请看下面这个图,中间位置肯定不是最大值区域,而且通过上面的第三步过滤也是没办法过滤的,具体看上面原理就知道了,所以需要在第二步进行过滤!

        本程序利用如下方法:

          A.每一层的最大值都大于外面一层,sum[0]>=sum[1] && sum[1]>sum[2] && sum[2]>sum[3],这里使用到9X9的内核

          B.记录每一层最大值出现的位置X0和X1,然后最大值的距离(abs(x0.x - x1.x) <= 2 && abs(x0.y - x1.y) <= 2)小于等于2,这里不严谨,因为每层最大值不止一个。

          C.记录最大值出现的数量n_count >= 3

 

 1        float sum[4] = { 0 };//存储最大值进行对比、在这里说不清楚看博客2017.10.17更新
 2             sum[0] = img[3][j];
 3             Point x0 = Point(0, 0);
 4             Point x1 = Point(0, 0);
 5             uchar n_count = 0;
 6             for (int m = 2; m < 5; m++)
 7             {
 8                 for (int n = -1; n < 2; n++)
 9                 {
10                     if (m == 3 && n == 0) continue;
11                     sum[1] = sum[1] < img[m][j + n] ? img[m][j + n] : sum[1];
12                     x0 = sum[0] == img[m][j + n] ? Point(m, n) : x0;
13                     n_count = sum[0] == img[m][j + n] ? n_count+1 : n_count;
14                     //flag = img[3][j + 0] < img[m][j + n] ? true : flag;//如果目标像素小于周围任何一个像素就说明这个一定不是最大值
15                 }
16             }
17             for (int m = 1; m < 6; m++)
18             {
19                 for (int n = -2; n < 3; n++)
20                 {
21                     if (2 <= m && m <= 4 && -1 <= n && n <= 1) continue;
22                     sum[2] = sum[2] < img[m][j + n] ? img[m][j + n] : sum[2];
23                     x1 = sum[0] == img[m][j + n] ? Point(m, n) : x1;
24                     n_count = sum[0] == img[m][j + n] ? n_count+1 : n_count;
25                     //flag = img[3][j + 0] < img[m][j + n] ? true : flag;//如果目标像素小于周围任何一个像素就说明这个一定不是最大值
26                 }
27             }
28             for (int m = 0; m < 7; m++)
29             {
30                 for (int n = -3; n < 4; n++)
31                 {
32                     sum[3] = sum[3] < img[m][j + n] && !(1 <= m && m <= 5 && -2 <=n && n <= 2) ? img[m][j + n] : sum[3];
33                     //flag = img[3][j+0] < img[m][j + n] ? true : flag;//如果目标像素小于周围任何一个像素就说明这个一定不是最大值
34                 }
35             }
36             x1 = (x1.x == 0 && x1.y == 0) || n_count >= 3 ? x0 : x1;//判断是否存在5X5的最大值(和目标点相同)
37             int tmp = sum[0] >= sum[1] && sum[1] >= sum[2] && sum[2] >= sum[3] && (abs(x0.x - x1.x) <= 2 && abs(x0.y - x1.y) <= 2)
38                 ? 0 : FillBlock(src, mask_tmp, Point(j, i));//tmp没意义,就是为了调用后面的函数而已
39         }

 

 

 

       2.对第三步进行补充

        A.搜索过程遇到边界,那就把这个最大值点去除if (img[i][fill_point[count].x + j] == 2 || msk[i][fill_point[count].x + j] == 0) max_point[k].data = 1;

      3.效果图

        注释:满足一个条件就判定为最大值!

           1 Find_Max(img, mask,0, 5000); 

               1 Find_Max(img, mask,5000, 0); 

               1 Find_Max(img, mask,5000, 5000); 

               1 Find_Max(img, mask,10, 20); 

               1 Find_Max(img, mask,10, 50); 

 

 

  1 import scipy.ndimage as ndimg
  2 import numpy as np
  3 from numba import jit
  4 import cv2
  5 
  6 def neighbors(shape):
  7     dim = len(shape)
  8     block = np.ones([3] * dim)
  9     block[tuple([1] * dim)] = 0
 10     idx = np.where(block > 0)
 11     idx = np.array(idx, dtype=np.uint8).T
 12     idx = np.array(idx - [1] * dim)
 13     acc = np.cumprod((1,) + shape[::-1][:-1])
 14     return np.dot(idx, acc[::-1])
 15 
 16 
 17 @jit  # trans index to r, c...
 18 
 19 def idx2rc(idx, acc):
 20     rst = np.zeros((len(idx), len(acc)), dtype=np.int16)
 21     for i in range(len(idx)):
 22         for j in range(len(acc)):
 23             rst[i, j] = idx[i] // acc[j]
 24             idx[i] -= rst[i, j] * acc[j]
 25     return rst
 26 
 27 
 28 #@jit  # fill a node (may be two or more points)
 29 
 30 def fill(img, msk, p, nbs, buf):
 31     msk[p] = 3
 32     buf[0] = p
 33     back = img[p]
 34     cur = 0
 35     s = 1
 36     while cur < s:
 37         p = buf[cur]
 38         for dp in nbs:
 39             cp = p + dp
 40             if img[cp] == back and msk[cp] == 1:
 41                 msk[cp] = 3
 42                 buf[s] = cp
 43                 s += 1
 44                 if s == len(buf):
 45                     buf[:s - cur] = buf[cur:]
 46                     s -= cur
 47                     cur = 0
 48         cur += 1
 49     #msk[p] = 3
 50 
 51 
 52 #@jit  # my mark
 53 
 54 def mark(img, msk, buf, mode):  # mark the array use (0, 1, 2)
 55     omark = msk     
 56     nbs = neighbors(img.shape)
 57     idx = np.zeros(1024 * 128, dtype=np.int64)
 58     img = img.ravel()  # 降维
 59     msk = msk.ravel()  # 降维
 60     s = 0
 61     for p in range(len(img)):
 62         if msk[p] != 1: continue  
 63         flag = False             
 64         for dp in nbs:
 65             if mode and img[p + dp] > img[p]: 
 66                 flag = True
 67                 break
 68             elif not mode and img[p + dp] < img[p]:
 69                 flag = True
 70                 break
 71         
 72         if flag : continue
 73         else    : fill(img, msk, p, nbs, buf)
 74         idx[s] = p
 75         s += 1
 76         if s == len(idx): break
 77     plt.imshow(omark, cmap=\'gray\')
 78     return idx[:s].copy()
 79 
 80 
 81 
 82 def filter(img, msk, idx, bur, tor, mode):
 83     omark = msk  
 84     nbs = neighbors(img.shape)
 85     acc = np.cumprod((1,) + img.shape[::-1][:-1])[::-1]
 86     img = img.ravel()
 87     msk = msk.ravel()
 88 
 89     arg = np.argsort(img[idx])[::-1 if mode else 1] 
 90 
 91     for i in arg:
 92         if msk[idx[i]] != 3:    
 93             idx[i] = 0
 94             continue
 95         cur = 0
 96         s = 1
 97         bur[0] = idx[i] 
 98         while cur < s:
 99             p = bur[cur]
100             if msk[p] == 2:     
101                 idx[i] = 0
102                 break
103 
104             for dp in nbs:
105                 cp = p + dp
106                 if msk[cp] == 0 or cp == idx[i] or msk[cp] == 4: continue
107                 if mode and img[cp] < img[idx[i]] - tor: continue
108                 if not mode and img[cp] > img[idx[i]] + tor: continue
109                 bur[s] = cp
110                 s += 1
111                 if s == 1024 * 128:
112                     cut = cur // 2
113                     msk[bur[:cut]] = 2
114                     bur[:s - cut] = bur[cut:]
115                     cur -= cut
116                     s -= cut
117 
118                 if msk[cp] != 2: msk[cp] = 4    
119             cur += 1
120         msk[bur[:s]] = 2    
121         #plt.imshow(omark, cmap=\'gray\')
122 
123     return idx2rc(idx[idx > 0], acc)
124 
125 
126 def find_maximum(img, tor, mode=True):
127     msk = np.zeros_like(img, dtype=np.uint8)  
128     msk[tuple([slice(1, -1)] * img.ndim)] = 1  
129     buf = np.zeros(1024 * 128, dtype=np.int64)
130     omark = msk
131     idx = mark(img, msk, buf, mode)
132     plt.imshow(msk, cmap=\'gray\')
133     idx = filter(img, msk, idx, buf, tor, mode)
134     return idx
135 
136 
137 if __name__ == \'__main__\':
138     from scipy.misc import imread
139     from scipy.ndimage import gaussian_filter
140     from time import time
141     import matplotlib.pyplot as plt
142 
143     img = cv2.imread(\'123.png\')
144     img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
145     ret2, img = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
146     img[:] = ndimg.distance_transform_edt(img)
147     plt.imshow(img, cmap=\'gray\')
148     pts = find_maximum(img, 20, True)
149     start = time()
150     pts = find_maximum(img, 10, True)
151     print(time() - start)
152     plt.imshow(img, cmap=\'gray\')
153     plt.plot(pts[:, 1], pts[:, 0], \'y.\')
154     plt.show()

 

 C++版本

 老版本、不稳定,可以看思路

  1 //---fill black value
  2 int FillBlock(Mat src, Mat &mask, Point center)
  3 {
  4     uchar back = src.at<uchar>(center.y, center.x);
  5     vector<Point> fill_point;
  6     int count = 0, count_mount = 1;
  7     fill_point.push_back(center);
  8     while (count < count_mount)
  9     {
 10         vector<uchar*> img;
 11         vector<uchar*> msk;
 12         for (int i = -1; i < 2; i++)
 13         {
 14             img.push_back(src.ptr<uchar>(fill_point[count].y + i));
 15             msk.push_back(mask.ptr<uchar>(fill_point[count].y + i));
 16         }
 17         for (size_t i = 0; i < 3; i++)
 18         {
 19             for (int j = -1; j < 2; j++)
 20             {
 21                 if (img[i][fill_point[count].x + j] == back && !(j == 0 && i == 1) && msk[i][fill_point[count].x + j] == 255)
 22                 {
 23                     fill_point.push_back(Point(fill_point[count].x + j, fill_point[count].y + i - 1));
 24                     msk[i][fill_point[count].x + j] = 1;
 25                 }
 26             }
 27         }
 28         msk[1][fill_point[count].x] = 1;
 29         count_mount = fill_point.size() - 1;
 30         fill_point.erase(fill_point.begin());
 31     }
 32     return 0;
 33 }
 34 //---cal mask
 35 //---@_src        
 36 //---@mask        
 37 void MaskImage(InputArray _src, Mat &mask)
 38 {
 39     Mat src = _src.getMat(),mask_tmp = Mat::zeros(src.size(), CV_8UC1);
 40     mask_tmp.setTo(255);
 41     Mat rows = Mat::zeros(Size(src.cols, 1), CV_8UC1), cols = Mat::zeros(Size(1, src.rows), CV_8UC1);
 42     Mat src_rows_beg = mask_tmp.row(0);
 43     Mat src_rows_end = mask_tmp.row(src.rows - 1);
 44     Mat src_cols_beg = mask_tmp.col(0);
 45     Mat src_cols_end = mask_tmp.col(src.cols - 1);
 46     rows.copyTo(src_rows_beg); rows.copyTo(src_rows_end);
 47     cols.copyTo(src_cols_beg); cols.copyTo(src_cols_end);
 48     for (size_t i = 1; i < src.rows-1; i++)
 49     {
 50         uchar *img0 = src.ptr<uchar>(i - 1);
 51         uchar *img  = src.ptr<uchar>(i);
 52         uchar *img1 = src.ptr<uchar>(i + 1);
 53         uchar *msk  = mask_tmp.ptr<uchar>(i);    
 54         for (size_t j = 1; j < src.cols-1; j++)
 55         {
 56             bool flag = false;
 57             //msk[j] = img[j] == 0 ? 0 : msk[j];
 58             if (msk[j] != 255) continue;
 59             flag = (img[j] < img[j - 1] || img[j] < img[j + 1]
 60                 || img[j] < img0[j] || img[j] < img0[j - 1]
 61                 || img[j] < img0[j + 1] || img[j] < img1[j]
 62                 || img[j] < img1[j - 1] || img[j] < img1[j + 1])
 63                 ? true : false;
 64             int tmp = flag == true ? FillBlock(src, mask_tmp, Point(j, i)) : 0;
 65         }
 66     }
 67     mask = mask_tmp.clone();
 68 }
 69 //---filter parts max value
 70 //---@
 71 //---@
 72 //---@gap        
 73 //---@radius    
 74 
 75 vector<Point> Find_Max(InputArray _src, Mat&mask,int gap,int radius)
 76 {
 77     Mat src = _src.getMat();
 78     
 79     typedef struct MyStruct
 80     {
 81         Point position;
 82         float data;
 83     }MyStruct;
 84 
 85     MaskImage(src, mask);
 86     vector<MyStruct> max_point;
 87     for (size_t i = 0; i < src.rows; i++)
 88     {
 89         uchar *img = src.ptr<uchar>(i);
 90         uchar *msk = mask.ptr<uchar>(i);
 91         for (size_t j = 0; j < src.cols; j++)
 92         {
 93             if (msk[j] != 255) continue;
 94             MyStruct my_struct;
 95             my_struct.data = img[j];
 96             my_struct.position = Point(j, i);
 97             max_point.push_back(my_struct);
 98         }
 99     }
100     for (size_t i = 0; i < max_point.size(); i++)
101     {
102         for (size_t j = i; j < max_point.size(); j++)
103         {
104             MyS

以上是关于《opencv实战》 之 局部极值提取的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV-Python-图像梯度与边缘检测

《opencv实战》 之 中轴线提取

OpenCV 例程 300篇247. 特征检测之最大稳定极值区域(MSER)

OpenCV 例程 300篇247. 特征检测之最大稳定极值区域(MSER)

OpenCV入门教程-Mat类之选取图像局部区域

OpenCV ⚠️高手勿入! 半小时学会基本操作 13⚠️ 直线检测