概述
在很多计算机视觉的应用里,都需要处理大量的数据,耗时费内存。为了减少处理时间,降低内存占用,需要使用物体的紧凑表示,也就是物体的骨架(skeleton)。
物体的骨架要求能够表示这个形状的结构,删除多余的像素点,下图是字母B的骨架。
这篇文章介绍一种使用OpenCV库计算生态骨架(morphological skeleton)的方法,这种方法简单实用。维基百科中有介绍说通过腐蚀和膨胀操作就可以获得morphological skeleton,伪代码如下:
img = ...;
while (not_empty(img))
{
skel = skel | (img & !open(img));
img = erosion(img);
}
说明一下算法的原理
- 每次迭代图像都会再次腐蚀,经过腐蚀后物体变得更窄细
- 对腐蚀后图像做开运算,经过开运算后处理后,图像有些像素会被删除,这些被删除的像素其实是骨架的一部分
- 将删除的像素,添加到骨架图上
- 当腐蚀后图像没有像素后后,就结束跌打,这时候或生成的骨架图也就完整了
python实现
import os
import numpy as np
import cv2
import sys
im_path = sys.argv[1]
im = cv2.imread(im_path, 0)
if im is None:
print im_path, " not exist"
sys.exit()
ret, im = cv2.threshold(im, 127, 255, cv2.THRESH_BINARY)
element = cv2.getStructuringElement(cv2.MORPH_CROSS, (3,3))
skel = np.zeros(im.shape, np.uint8)
temp = np.zeros(im.shape, np.uint8)
i = 0
while True:
cv2.imshow('im %d'%(i), im)
#取开运算过程中消失的像素,这些像素便是skeleton的一部分
temp = cv2.morphologyEx(im, cv2.MORPH_OPEN, element)
temp = cv2.bitwise_not(temp)
temp = cv2.bitwise_and(im, temp)
cv2.imshow('skeleton part %d'%(i,), temp)
#将删除的像素添加skeleton图中
skel = cv2.bitwise_or(skel, temp)
#再次腐蚀原图,为进一步寻找skeleton做准备
im = cv2.erode(im, element)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(im)
#print min_val, max_val, min_loc, max_loc
if max_val==.0:
break
i += 1
cv2.imshow('Skeleton', skel)
cv2.waitKey()
实验效果:
上图中im0 -4表示每轮迭代是经过腐蚀后的图片,skeleton part 0-4表示经过开源获得的骨架像素,最后这些像素组合在一起获得骨架图skeleton,很神奇的。
性能优化
之前的实现里,使用openning运算,开运算后有对原图做了erode操作。实际上opening运算是erode + dilate,所以进行了两次腐蚀,有重复计算,可以只使用一次。另外minMaxLoc()函数函数效率比countNonZero()要慢。优化后代码如下:
import os
import numpy as np
import cv2
import sys
im_path = sys.argv[1]
im = cv2.imread(im_path, 0)
if im is None:
print im_path, " not exist"
sys.exit()
ret, im = cv2.threshold(im, 127, 255, cv2.THRESH_BINARY)
element = cv2.getStructuringElement(cv2.MORPH_CROSS, (3,3))
skel = np.zeros(im.shape, np.uint8)
erode = np.zeros(im.shape, np.uint8)
temp = np.zeros(im.shape, np.uint8)
i = 0
while True:
cv2.imshow('im %d'%(i), im)
erode = cv2.erode(im,element)
temp = cv2.dilate(erode, element)
#消失的像素是skeleton的一部分
temp = cv2.subtract(im, temp)
cv2.imshow('skeleton part %d' %(i,), temp)
skel = cv2.bitwise_or(skel, temp)
im = erode.copy()
if cv2.countNonZero(im)== 0:
break;
i += 1
cv2.imshow('Skeleton', skel)
cv2.waitKey()
参考
http://felix.abecassis.me/2011/09/opencv-morphological-skeleton/