Python OpenCV图像物体检测与裁剪实战:从预处理到轮廓提取
本文介绍如何利用Python和OpenCV库实现图像中特定物体的自动检测与裁剪。通过灰度转换、Sobel梯度计算、低通滤波及二值化预处理,结合形态学腐蚀与膨胀去除噪声,最终利用cv2.findContours提取最大轮廓并计算最小外接矩形,完成目标区域的精准裁剪,适用于昆虫识别等背景复杂场景。
引言
介绍
在硕士阶段的毕业设计研究中,主要聚焦于昆虫图像分类任务。代码开发进行至中途时,导师提供了新的昆虫图片数据集。新数据集中存在大量大图,但其中的昆虫主体占比极小。为提升识别准确率,计划对图片进行预处理,通过裁剪去除无关背景,仅保留昆虫区域。
原图片举例(将红色矩形框部分裁剪出来):

图像预处理流程
第一步:加载图片,转成灰度图
image = cv2.imread("353.jpg")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
第二步:利用Sobel算子分别计算X轴和Y轴方向的梯度,随后将X方向梯度减去Y方向梯度。通过这一减法操作,保留高水平梯度而抑制垂直梯度的图像区域,从而突出昆虫的轮廓特征。
gradX = cv2.Sobel(gray, ddepth=cv2.cv.CV_32F, dx=1, dy=0, ksize=-1)
gradY = cv2.Sobel(gray, ddepth=cv2.cv.CV_32F, dx=0, dy=1, ksize=-1)
# subtract the y-gradient from the x-gradient
gradient = cv2.subtract(gradX, gradY)
gradient = cv2.convertScaleAbs(gradient)
执行完这一步,得到的图像如下:

第三步:去除图像噪声。首先应用低通滤波器(使用9x9内核)对图像进行平滑处理,以抑制高频噪声。低通滤波旨在降低图像像素强度的变化率,具体做法是将每个像素替换为其周围像素的平均值,从而平滑强度变化剧烈的区域。
接着,对模糊后的图像进行二值化处理。对于梯度图像中灰度值小于或等于90的像素,将其设置为0(黑色);其余像素则设置为255(白色)。
# blur and threshold the image
blurred = cv2.blur(gradient, (9, 9))
(_, thresh) = cv2.threshold(blurred, 90, 255, cv2.THRESH_BINARY)
执行完这一步,得到的图像如下:

第四步:观察上图可见,蜜蜂身体区域存在较多黑色空白。为使后续程序更易识别昆虫区域,需使用白色填充这些空白,这涉及形态学操作。
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (25, 25))
closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
处理之后的图像如下:

第五步:从上图中仍可观察到一些微小的白色斑点,这些斑点会干扰后续昆虫轮廓的检测,因此需要将其去除。具体操作是连续执行4次形态学腐蚀与膨胀操作。
# perform a series of erosions and dilations
closed = cv2.erode(closed, None, iterations=4)
closed = cv2.dilate(closed, None, iterations=4)
执行完这步,得到的图形如下:

轮廓检测与提取
第六步:提取昆虫区域的轮廓。cv2.findContours()函数的第一个参数为待检索的图片,该图片必须为二值图(黑白图像,而非灰度图)。因此,读取的图像需先转换为灰度图,再转换为二值图,我们在第三步中已通过cv2.threshold()函数得到了二值图。第二个参数指定轮廓的检索模式,共有四种选项:
- cv2.RETR_EXTERNAL表示仅检测外轮廓
- cv2.RETR_LIST检测所有轮廓,不建立等级关系
- cv2.RETR_CCOMP建立两个等级的轮廓,上层为外边界,内层为内孔边界。若内孔内还有连通物体,其边界也位于顶层。
- cv2.RETR_TREE建立一个等级树结构的轮廓。
第三个参数指定轮廓的近似方法
- cv2.CHAIN_APPROX_NONE存储所有轮廓点,相邻两点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1
- cv2.CHAIN_APPROX_SIMPLE压缩水平、垂直及对角线方向的元素,仅保留终点坐标。例如,矩形轮廓仅需4个点即可保存轮廓信息
cv2.findContours()函数返回两个值:一个是轮廓本身,另一个是每条轮廓对应的属性。cv2.findContours()函数返回的第一个值是列表(list),列表中每个元素均为图像中的一个轮廓,由numpy中的ndarray表示。每个ndarray内保存了轮廓上各点的坐标。我们将列表排序,点数量最多的那个轮廓即为我们需要查找的昆虫轮廓。 OpenCV中通过cv2.drawContours在图像上绘制轮廓。
- 第一个参数指定在哪幅图像上绘制轮廓
- 第二个参数是轮廓本身,在Python中是一个列表(list)
- 第三个参数指定绘制轮廓列表中的哪条轮廓,若为-1,则绘制其中所有轮廓
- 第四个参数是轮廓线条的颜色
- 第五个参数是轮廓线条的粗细
cv2.minAreaRect()函数: 主要用于求得包含点集的最小面积矩形,该矩形可具有偏转角度,不一定与图像边界平行。
(cnts, _) = cv2.findContours(closed.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
c = sorted(cnts, key=cv2.contourArea, reverse=True)[0]
# compute the rotated bounding box of the largest contour
rect = cv2.minAreaRect(c)
box = np.int0(cv2.cv.BoxPoints(rect))
# draw a bounding box arounded the detected barcode and display the image
cv2.drawContours(image, [box], -1, (0, 255, 0), 3)
cv2.imshow("Image", image)
cv2.imwrite("contoursImage2.jpg", image)
cv2.waitKey(0)
执行完这步得到的图形如下:

目标区域裁剪实现
第七步:裁剪。box变量中保存了绿色矩形区域四个顶点的坐标。我将按照图中红色矩形所示裁剪昆虫图像。通过找出四个顶点的X和Y坐标的最大值与最小值,计算新图像的高=maxY-minY,宽=maxX-minX。

Xs = [i[0] for i in box]
Ys = [i[1] for i in box]
x1 = min(Xs)
x2 = max(Xs)
y1 = min(Ys)
y2 = max(Ys)
hight = y2 - y1
width = x2 - x1
cropImg = image[y1:y1+hight, x1:x1+width]
裁剪出的图片如下:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













