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



局部极值提取算法

这是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()
时间: 2024-08-11 19:33:29

《opencv实战》 之 局部极值提取的相关文章

Opencv 各种特征点提取和匹配

opencv 特征点的提取和匹配 1. 当中的数据结构 KeyPoint这数据结构中有如下数据结构: class KeyPoint { Point2f pt; //坐标 float size; //特征点邻域直径 float angle; //特征点的方向,值为[零,三百六十),负值表示不使用 float response; int octave; //特征点所在的图像金字塔的组 int class_id; //用于聚类的id angle:角度,表示关键点的方向,SIFT算法通过对关键点周围邻域

《opencv实战》 之 中轴线提取

中轴线算法 这是http://www.imagepy.org/的作者原创,我只是对其理解之后改进和说明,欢迎大家使用这个小软件! 首先上效果图: 算法的流程: 第一步: 距离变换 第二步: 把距离变换的图像进行像素值的排列(排列返回像素的位置信息) 第二步: 从小到大进行像素的查表操作 注释:这里为什么叫中轴线提取?因为提取的过程是绝对的按照对称来的,距离变换的结果就是前景到背景的距离,所以结果是绝对的集合中心. 代码: 1 import numpy as np 2 from skimage.d

[OpenCV] 3、直线提取 houghlines

>_<" 发现一个好的链接,是一个讲openCV的网站:http://www.opencv.org.cn/opencvdoc/2.3.2/html/index.html >_<" 这次主要是houghlines变换来提取直线~ 1 #include "opencv2/highgui/highgui.hpp" 2 #include "opencv2/imgproc/imgproc.hpp" 3 #include <io

MFC+opencv+高斯背景前景提取图像显示问题

最近做项目遇到一个问题,再用混合高斯背景模型提取前景图显示到MFC窗口上的时候,出现了一个问题,图像显示不正常.显示效果如下图: 我是使用Mat类型图像和imshow进行显示的,出现这个状况出乎意料.因为其他都能正常显示. 之后开始在网上查找问题所在,可是经过多方查找,发现网上根本没有此类问题.尴尬. 最后无奈开始各方求助,问答啊,论坛帖子啊都试过却没人解答...最后幸好通过指导老师认识了一个研三学长,学长说由于opencv与MFC图像格式不一样所以才会出现这个问题,需要将Mat导成二进制流再转

《opencv实战》 之 车牌定位

目标: 提取车牌所在的区域   PS:现在还没学习文字检测,以后再来补充~~ 思路: 1.利用形态学+梯度+轮廓检测,但是这个形态学要求比较高,得调节到适当的参数. 2.利用HSV颜色分离+梯度+形态学+轮廓检测,这个方法的适应度比较高,行比较上面的方法较难理解. 本文使用第二种方法实现: PS:不要问为什么用这种方法,没有为什么的方法,只有解决问题的方法,手动操作一下就懂了. 先上效果图: 梯度检测图(1-1) S空间图(1-2) S空间分离蓝色图(1-3) (HSV图像分离+梯度)结合图(1

OpenCV中特征点提取和匹配的通用方法

OpenCV在新版本中把很多C语言的代码都重新整理成了C++代码,让我们在使用的时候更加方便灵活.其中对于特征点的提取和匹配,充分体现了C++的强大.下面直接用例子来说明.假设我们有两幅图:1.bmp和2.bmp,要从中提取体征点并匹配,代码如下: // Load image from file IplImage *pLeftImage = cvLoadImage("1.bmp", CV_LOAD_IMAGE_GRAYSCALE);IplImage *pRightImage = cvL

【OpenCV函数】轮廓提取;轮廓绘制;轮廓面积;外接矩形

FindContours 在二值图像中寻找轮廓 int cvFindContours( CvArr* image, CvMemStorage* storage, CvSeq** first_contour, int header_size=sizeof(CvContour), int mode=CV_RETR_LIST, int method=CV_CHAIN_APPROX_SIMPLE, CvPoint offset=cvPoint(0,0) ); image  输入的 8-比特.单通道图像.

opencv 学习之 车牌提取

车牌识别分两步,一是车牌提取,而是字符识别. 下面是车牌提取. VS2010. OpenCV249. //载入图像 char * path = "d:\\picture\\06.jpg"; IplImage * frame = cvLoadImage(path); if(!frame) return 0; cvNamedWindow("frame", 1); cvShowImage("frame", frame); //均值滤波 cvSmooth

opencv 形态学操作应用-提取水平与垂直线

adaptiveThreshold(~gray_src, binImg, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2); #include <opencv2/opencv.hpp> #include <iostream> using namespace cv; int main(int argc, char** argv) { Mat src, dst; src = imread("E:/opencv3.2/Tes