首先介绍图像的异或操作,其可以用八个字概括:相同为假,相异为真。
如下:
A=array([[ True, True, True],
[ True, **False**, True],
[ True, True, True]], dtype=bool)
B=array([[ True, True, True],
[ True, **True**, True],
[ True, True, True]], dtype=bool)
A^B=array([[False, False, False],
[False, **True**, False],
[False, False, False]], dtype=bool)
在找图像局部最大值的时候,可分为以下几步:
1、 对图像进行最大值滤波(scipy 中的函数有最大值滤波函数),然后使得其等于原图,其得到一个bool 类型的矩阵。此时,最大值邻域都是True—-local_maxinum
2、 要想得到peaks,还得从以上操作中移除图像背景,即是利用异或操作,background^ local_maxinum
3、 对于2中的结果,寻找其等于True的列与行就是对应的局部最大值坐标
例子如下—齿轮定位:
本打算采用找轮廓算法来确定每个零件的中心和半径,但是由于零件之间由重叠,找轮廓效果见下图
所以笔者采用的方法是
1、 二值化(注意反转,齿轮式黑色)
def process_img(img):
if img.ndim==3:
img=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
img=cv2.medianBlur(img,3)
_,thresh=cv2.threshold(img,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
return thresh
2、 填洞,注意的是cv2.floodFill()这个mask 的宽高等于binary image 宽高加2
def fillhole(thresh):
h,w=thresh.shape[:]
mask=np.zeros((h+2,w+2),np.uint8)
holes=cv2.floodFill(thresh.copy(),mask,(0,0),255)
s=cv2.bitwise_not(holes[1])
full_thresh=s|thresh
return full_thresh
3、 距离变换,也成EDM(距离映射),是指每个像素到背景像素的距离,越亮的地方离背景像素越远。距离变换有很多应用,如骨架提取,半径提取等。
distance=cv2.distanceTransform(full_threshh,2,5)
4、 寻找局部最大值,笔者的找局部最大不稳定
def detect_peaks(img):#2d local maxinum
neighborhood=generate_binary_structure(2,2)
local_peaks=maximum_filter(img,footprint=neighborhood)==img
## 此时,local_peaks是bool值,False代表局部最大值
##但由于背景的存在,必须从local_peaks移除背景
background=(img==0)
erode_background=binary_erosion(background,structure=neighborhood,border_value=1)
peaks=local_peaks^erode_background
return peaks
5、 笔者找到局部最大指后(cols,rows),用半径过滤,其半径就是对应距离变换后的值
peaks=detect_peaks(distance)
cols_and_rows=[[j,i] for i,j in zip(*np.where(peaks==True))]
#判断半径
true_pos=[]
for t in cols_and_rows:
if distance[t[1],t[0]]>50:
true_pos.append(t)
cv2.putText(im,"pos=(%g,%g),r=%g"%(t[0],t[1],distance[t[1],t[0]]),(t[0],t[1]),cv2.FONT_HERSHEY_SIMPLEX,0.5,[0,0,255],2)