对于一些特殊多目标图像,例如细粒度目标、缺乏标注文件等。或是图像前背景区分较明显的图像,不适合用深度学习模型进行目标定位与分割。相比之下,利用较为传统的图像处理方式,可以更高效地实现对上述图像的目标定位与分割(抠图)。
本文将分为三个部分详细讲解利用opencv对多目标图像进行目标定位与分割的实现过程。
先对目标文件夹下所有图片进行resize,将所有图片resize到同一大小,方便后续进行批量操作。
将图片resize成800×800,原图为502×502。
先利用os.listdir方法将目标文件夹下的所有图像读取出来,再分别根据图像的宽高等比例地将图像resize成目标大小。最后利用opencv的imwrite()方法批量保存resize后的图片。
for i in os.listdir(imgs_p):
img_path=os.path.join(imgs_p,i)
img = cv2.imread(img_path)
height, width = img.shape[:2] # 得到行和列的长度
scale=size/height # 得到size和图像高的缩放比例
height=size
width=int(width*scale)
img = cv2.resize(img, (width, height))
new_img_path=os.path.join(imgs_save_p,i)
cv2.imwrite(new_img_path,img)
试验过多种方式(包括阈值分割、边缘分割等)最后选用网上流传度最高的xy方向梯度相减方式进行分割。
读取图片,先将图片转换成灰度图,分别计算x方向和y方向的梯度,然后对其相减,留下具有高水平梯度和低垂直梯度的图像区域。
之后去除噪声,再进行二值化,最后确定目标轮廓。
image = cv2.imread(img_path)
shape = image.shape
image_row = shape[0]
image_col = shape[1]
print(image_row,image_col)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 得到灰度图
gradX = cv2.Sobel(gray, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1) # x方向一阶导数
gradY = cv2.Sobel(gray, ddepth=cv2.CV_32F, dx=0, dy=1, ksize=-1) # y方向一阶导数
gradient = cv2.subtract(gradX, gradY)
blurred = cv2.blur(gradient, (9, 9)) # 滤波,去除噪声
(_, thresh) = cv2.threshold(blurred, 20, 255, cv2.THRESH_BINARY) # 图像二值化,thresh为得到的图像
thresh = cv2.erode(thresh, None, iterations=5)
thresh = cv2.dilate(thresh, None, iterations=4)
(cnts, _) = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # cnts为得到的轮廓,存放在一个list中
all_box_img = copy.deepcopy(image) # 深复制,跟原图没有关系,一个改变不影响另一个
cv2.drawContours(all_box_img, cnts, -1, (0, 255, 0), 1) # 在all_box_img上画轮廓
利用cv2.minAreaRect函数确定目标的最小外接矩形。再将区域resize成与x、y轴平行的样式,最后选定区域进行裁剪并保存。
for i, contour in enumerate(cnts):
area = cv2.contourArea(contour) # 计算包围形状的面积
if area < 550: # 过滤面积小于550的部分
continue
rect = cv2.minAreaRect(contour) # 检测轮廓最小外接矩形,得到最小外接矩形的(中心(x,y), (宽,高), 旋转角度)
box = np.int0(cv2.boxPoints(rect))
boxs.append(box) # 最后剩下的有用的框
for i,box in enumerate(boxs): # 裁剪
xs = [j[0] for j in boxs[i]]
ys = [j[1] for j in boxs[i]]
x1 = min(xs)
x2 = max(xs)
y1 = min(ys)
y2 = max(ys)
if x1 < 0:
x1 = 0
if x2 > image_col:
x2 = image_col
if y1 < 0:
y1 = 0
if y2 > image_row:
y2 = image_row
print(x1,x2,y1,y2)
hight = y2 - y1
width = x2 - x1