对象发现
不知道什么原因博客园在markdown编辑器上无法上传图片。需要看源码和图片复原实验的可以去我的github
近段时间,做了一些关于对象发现的工作。主要内容是从图片中识别出液滴,并统计其数量。在这个过程中遇到了一些问题,也发现了几种相关的解决方案,在这里与大家分享一下。
python中用来处理图像的不得不说CV2 了,这是一个工业级的包。包含了几乎所有的图片处理方法,例如常见的找边界、膨胀、腐蚀、画矩形、画圆等。本次实践过程我使用到了三种方法,用来识别图像中的液滴。第一种是常规的处理方法,先对图片进行边界确定,然后膨胀、腐蚀,最后寻找液滴;第二种方法是由于第一中方法会将边缘勿识别为液滴,故在第一种方法的基础上加上边缘界定,想借此消除边界误差;第三种是用多个液滴模板,一次对图片进行匹配,如果相似度在阈值内则认为是目标液滴,否则不是。
实例背景:我们需要从一张显微镜拍下的分液图中寻找出液滴,并统计数量。
代码语言:javascript复制1. 常规方法--膨胀 腐蚀
2. 先界定边缘然后膨胀 腐蚀
3. 遮罩匹配
常规方法
常规方法中,关键在与图片处理的流程。在这里,我们的图片存在色差不明显和颜色偏淡的情况,为此,我们首先对图片做颜色增强操作。(此前对比了锐化操作,对结果没有明显的提升)接着开始做寻找图片中对象的操作。首先,使用cv2.cvtColor方法将图片转化为灰度图,使用cv2.Canny 方法找出对象的边界;然后,使用cv2.dilate 方法进行膨胀操作;紧接着,使用cv2.erode 方法进行腐蚀操作;最后,使用cv2.findContours 方法找出图片中的对象。在可视化部分,我们使用cv2.drawContours 方法绘出我们找到的对象,与实际情况对比,观察误差存在的区域以及特点。下面给出每一步操作的代码。
导入包 import numpy as np from PIL import ImageEnhance,Image import cv2 import os import sys
首先我们定义了颜色增强的方法
def EnhanceImage(image): #色度增强 enh_col = ImageEnhance.Color(image); color = 1.5; image_colored = enh_col.enhance(color); return np.array(image_colored);
image_enhance = EnhanceImage(image);
找到对象轮廓
image_to_process = image_enhance[:,:,:3]; image_gray = cv2.cvtColor(image_to_process, cv2.COLOR_BGR2GRAY); image_cannyEdged = cv2.Canny(image_gray, 50, 120);
膨胀
dilatekernel = cv2.getStructuringElement(cv2.MORPH_RECT,(3,3)); #定义膨胀核的形状 image_dilateEdged = cv2.dilate(image_cannyEdged, dilatekernel, iterations=2); #膨胀
腐蚀
errodekernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(8,8)); #定义腐蚀核形状 image_erodeEdged = cv2.erode(image_dilateEdged, errodekernel, iterations=1); #腐蚀
标记对象 因为图片中存在噪点,我们为了让对象查找更加精准,进行了对象筛选步骤。我们根据对象的面积来判断其是否是一个正常的目标点。
定义筛选函数 def ContourFilter(contours,min_area,max_area,rate): """ contours 轮廓位置坐标数组 min_area 轮廓围成的区域的最小面积 max_area 轮廓围成的区域的最大面积 轮廓先要满足min_area 和 max_area 条件,然后需要满足包含轮廓的最小矩形的长宽比小于rate """ con = []; for c in contours: a = cv2.contourArea(c); if((a > min_area) and (a < max_area)): min_rect = cv2.minAreaRect(c); #width 不一定比 height 小 width = min_rect[1][0]; height = min_rect[1][1]; if(not (width/height > rate or height/width > rate)): con.append(c); else: continue; return con;
寻找对象
contours, hierarchy = cv2.findContours(image_erodeEdged, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE);#cv2.findCountours 方法返回值的个数与cv2的版本有关。可能是2个也肯是3个,具体情况根据自身cv2版本调整 contours_filter = ContourFilter(contours,3.5,50,2.5); number = 0; for cnt in contours_filter: min_rect = cv2.minAreaRect(cnt); min_rect = np.int0(cv2.boxPoints(min_rect)); cv2.drawContours(image_contours,[min_rect],-1,(12,12,12),2); number = number 1;
从图中,我们可以看到,标记出的对象中存在一定的误差,边缘部分有噪点。用常规方法发现对象,在很大程度上依赖cv2.Canny 方法,及以来cv2 库中寻找边界的方法。如果,边界能被精确识别,那么对象发现将会是零误差。因为我们后续操作都是建立在边界轮廓上。从图片的边界图中我们发现,边缘的边界识别存在很大的误差,出现多层边界。边界不是一条封闭的曲线。内部边界也有类似的情况。由于边界识别出现误差,导致在膨胀和腐蚀时边界会出现不规则的形状。甚至将正常点分化为噪点。为解决该问题,我们尝试了新方法;
界定边缘 膨胀 腐蚀
定义边界发现函数 def segment_on_dt(a, img): border = cv2.dilate(img, None, iterations=5); border = border - cv2.erode(border, None); dt = cv2.distanceTransform(img, 2, 3); dt = ((dt - dt.min()) / (dt.max() - dt.min()) * 255).astype(np.uint8); _, dt = cv2.threshold(dt, 180, 255, cv2.THRESH_BINARY); lbl, ncc = label(dt); lbl = lbl * (255 / (ncc 1)); # Completing the markers now. lbl[border == 255] = 255 ; lbl = lbl.astype(np.int32); print(lbl.shape); cv2.watershed(a, lbl); lbl[lbl == -1] = 0; lbl = lbl.astype(np.uint8); return 255 - lbl; def fill(img,n=4): """ fill the edge default 4 neighborhood """ neighbor = get_neighbor(n); w,h = img.shape; img_copy = img.copy(); for i in range(1,w-1): for j in range(1,h-1): if(img[i][j] == 255): #白色点 for ne in neighbor: img_copy[i ne[0],j ne[1]] = 255; return img_copy; def get_neighbor(n): if(n==4): return [(-1,0),(1,0),(0,-1),(0,-1)]; if(n==8): return [(-1,0),(1,0),(0,-1),(1,0),(-1,-1),(1,1),(-1,1),(1,-1)]; return [];
从结果上看,图片中存在区块色差。为此我们任然无法精确识别边界,该方法最终被我们放弃。新的方法总会产生,经过查阅资料,发现了一种通过遮罩模板的方法直接匹配目标点。这个方法简单直接,缺点就是我们需要选出许多遮罩模板,对于没有出现过的目标点则显得无能为力。其中需要解决的问题有两个,1是选出合适的遮罩模板;2是需要将每个遮罩模板匹配出的结果合并。
遮罩匹配
该方法主要使用到cv2.matchTemplate 方法,匹配度计算的标准有方差,相关系数。详细用法可以参考cv2.matchTemplate 教程
template_result = cv2.matchTemplate(image, mask, cv2.TM_CCOEFF_NORMED); loc = np.where(template_result >= 0.75);
从图中我们看见,使用遮罩模板选出的对象点的正确率基本达到100%。当然,这是在图片不复杂的情况下,如果图片存在噪点较多,目标点的颜色多样,那么这时我们必须增加遮罩模板的数量与种类。 总的来说,对象发现问题还有需要值得研究的地方。项目的源码请看github