码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2017-08-23 10:29:43      阅读:469      评论:0      收藏:0      [点我收藏+]

标签:org   几何   index   pts   time()   else   filter   排除   ges   

 


 

局部极值提取算法

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

 

 


 

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

技术分享

 

    第一步:    

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

 

技术分享

    第二步:

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

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

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

 

技术分享

    第三步:

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

          •   首先对找到的最大值进行排序--从大到小
          •   按照从大到小的顺序去寻找每个最大值的领地
          •   领地按照“不重合”、“不包括”、“领主大于等于领地”这三个原则             

        下面以图为例进一步说明三个原则:

            不重合原则:

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

技术分享

            不包括原则:

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

技术分享

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

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

 

 技术分享

 

 技术分享

 

 

 

 程序有待改进:

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

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

 

技术分享

技术分享

技术分享

技术分享

 

  1 import scipy.ndimage as ndimg
  2 import numpy as np
  3 from numba import jit
  4 import cv2
  5 
  6 
  7 # 获得邻居像素坐标,相当于C++的核(支持多维度)
  8 def neighbors(shape):
  9     dim = len(shape)
 10     block = np.ones([3] * dim)
 11     block[tuple([1] * dim)] = 0
 12     idx = np.where(block > 0)
 13     idx = np.array(idx, dtype=np.uint8).T
 14     idx = np.array(idx - [1] * dim)
 15     acc = np.cumprod((1,) + shape[::-1][:-1])
 16     return np.dot(idx, acc[::-1])
 17 
 18 
 19 @jit  # trans index to r, c...
 20 #坐标转换,一维-->>二维
 21 def idx2rc(idx, acc):
 22     rst = np.zeros((len(idx), len(acc)), dtype=np.int16)
 23     for i in range(len(idx)):
 24         for j in range(len(acc)):
 25             rst[i, j] = idx[i] // acc[j]
 26             idx[i] -= rst[i, j] * acc[j]
 27     return rst
 28 
 29 
 30 #@jit  # fill a node (may be two or more points)
 31 #这里和寻找山脊线一个思路,不过这里是填充相同水平位置
 32 #不懂的可以看看我注释的山脊线算法
 33 def fill(img, msk, p, nbs, buf):
 34     msk[p] = 3
 35     buf[0] = p
 36     back = img[p]
 37     cur = 0
 38     s = 1
 39     while cur < s:
 40         p = buf[cur]
 41         for dp in nbs:
 42             cp = p + dp
 43             if img[cp] == back and msk[cp] == 1:#对周围相等的像素进行扩充赋值为3
 44                 msk[cp] = 3
 45                 buf[s] = cp
 46                 s += 1
 47                 #防止buf太大越界
 48                 if s == len(buf):
 49                     buf[:s - cur] = buf[cur:]
 50                     s -= cur
 51                     cur = 0
 52         cur += 1
 53     #msk[p] = 3
 54 
 55 
 56 #@jit  # my mark
 57 #此时的图片边界四个边界为0,中间是1
 58 #图片结果是:背景3,前景1,最大值3
 59 def mark(img, msk, buf, mode):  # mark the array use (0, 1, 2)
 60     omark = msk     #查看演示的步骤,因为是浅拷贝所以不需要再去升维度
 61     nbs = neighbors(img.shape)
 62     idx = np.zeros(1024 * 128, dtype=np.int64)
 63     img = img.ravel()  # 降维
 64     msk = msk.ravel()  # 降维
 65     s = 0
 66     for p in range(len(img)):
 67         if msk[p] != 1: continue  # 防止越界
 68         flag = False              # 这里改进,找平原和最高峰(背景和最大值)
 69         for dp in nbs:
 70             if mode and img[p + dp] > img[p]: #这里改进
 71                 flag = True
 72                 break
 73             elif not mode and img[p + dp] < img[p]:
 74                 flag = True
 75                 break
 76         if flag : continue
 77         else    : fill(img, msk, p, nbs, buf)
 78         idx[s] = p
 79         s += 1
 80         if s == len(idx): break
 81     plt.imshow(omark, cmap=gray)
 82     return idx[:s].copy()
 83 
 84 
 85 #@jit  # 3 最大值标记 2 最终殖民地标记 4 临时殖民地标记
 86 def filter(img, msk, idx, bur, tor, mode):
 87     omark = msk  # 查看演示的步骤,因为是浅拷贝所以不需要再去升维度
 88     nbs = neighbors(img.shape)
 89     acc = np.cumprod((1,) + img.shape[::-1][:-1])[::-1]
 90     img = img.ravel()
 91     msk = msk.ravel()
 92 
 93     arg = np.argsort(img[idx])[::-1 if mode else 1] #最大值(大到小)和最小值(小到大)
 94 
 95     for i in arg:
 96         if msk[idx[i]] != 3:    #如果被其他占领那就GG
 97             idx[i] = 0
 98             continue
 99         cur = 0
100         s = 1
101         bur[0] = idx[i] #存储第一个位置,后面会自动存储,其作用是存储占领的地盘
102         while cur < s:
103             p = bur[cur]
104             if msk[p] == 2:     #如果当前殖民地和别人有冲突,那就GG,因为先占领的峰值更高
105                 idx[i] = 0
106                 break
107 
108             for dp in nbs:
109                 cp = p + dp
110                 if msk[cp] == 0 or cp == idx[i] or msk[cp] == 4: continue
111                 if mode and img[cp] < img[idx[i]] - tor: continue
112                 if not mode and img[cp] > img[idx[i]] + tor: continue
113                 bur[s] = cp
114                 s += 1
115                 if s == 1024 * 128:
116                     cut = cur // 2
117                     msk[bur[:cut]] = 2
118                     bur[:s - cut] = bur[cut:]
119                     cur -= cut
120                     s -= cut
121 
122                 if msk[cp] != 2: msk[cp] = 4    #临时标记占领的地盘为4
123             cur += 1
124         msk[bur[:s]] = 2    #占领的地盘标记为2
125         #plt.imshow(omark, cmap=‘gray‘)
126 
127     return idx2rc(idx[idx > 0], acc)
128 
129 
130 def find_maximum(img, tor, mode=True):
131     msk = np.zeros_like(img, dtype=np.uint8)  # 返回img大小的零矩阵
132     msk[tuple([slice(1, -1)] * img.ndim)] = 1  # slice是切片数据,slice*img.ndim就是在二维或者三维上面进行切片,这里是让边缘为0,内部填充为1
133     buf = np.zeros(1024 * 128, dtype=np.int64)
134     omark = msk
135     idx = mark(img, msk, buf, mode)
136     plt.imshow(msk, cmap=gray)
137     idx = filter(img, msk, idx, buf, tor, mode)
138     return idx
139 
140 
141 if __name__ == __main__:
142     from scipy.misc import imread
143     from scipy.ndimage import gaussian_filter
144     from time import time
145     import matplotlib.pyplot as plt
146 
147     img = cv2.imread(123.jpg)
148     img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
149     ret2, img = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
150     img[:] = ndimg.distance_transform_edt(img)
151     pts = find_maximum(img, 20, True)
152     start = time()
153     pts = find_maximum(img, 10, True)
154     print(time() - start)
155     plt.imshow(img, cmap=gray)
156     plt.plot(pts[:, 1], pts[:, 0], y.)
157     plt.show()

 

 

 

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

标签:org   几何   index   pts   time()   else   filter   排除   ges   

原文地址:http://www.cnblogs.com/wjy-lulu/p/7416135.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!