一、用到的函数
1.cv2.findContours()
image,contours,hierarchy =
cv2.findContours(image, mode, method[, contours[, hierarchy[, offset ]]])
输入:
image:输入图像;
mode:轮廓的检索模式
1.cv2.RETR_EXTERNAL表示只检测外轮廓
2.cv2.RETR_LIST检测的轮廓不建立等级关系
3.cv2.RETR_CCOMP建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。
4.cv2.RETR_TREE建立一个等级树结构的轮廓。
method:为轮廓的近似办法
1.cv2.CHAIN_APPROX_NONE存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1
2.cv2.CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标
输出
image:与输入image类似的一张二值图;
contours:list结构,列表中每个元素代表一个边沿信息。每个元素是(x,1,2)的三维向量,x表示该条边沿里共有多少个像素点,第三维的那个“2”表示每个点的横、纵坐标;
注意:如果输入选择cv2.CHAIN_APPROX_SIMPLE,则contours中一个list元素所包含的x点之间应该用直线连接起来,这个可以用cv2.drawContours()函数观察一下效果。
hierarchy:返回类型是(x,4)的二维ndarray。x和contours里的x是一样的意思。如果输入选择cv2.RETR_TREE,则以树形结构组织输出,hierarchy的四列分别对应下一个轮廓编号、上一个轮廓编号、父轮廓编号、子轮廓编号,该值为负数表示没有对应项。
2.ellipse = cv2.fitEllipse(cnt)
输入
cnt即1中所获取的轮廓点集
输出
ellipse = [ (x, y) , (a, b), angle ]
其中(x,y)为中心点位置,a为长轴长度,b为短轴长度,angle为中心旋转角度
3. cv2.ellipse(image, centerCoordinates, axesLength, angle, startAngle, endAngle, color [, thickness[, lineType[, shift]]])
输入
image:它是要在其上绘制椭圆的图像。
centerCoordinates:它是椭圆的中心坐标。坐标表示为两个值的元组,即(X坐标值,Y坐标值)。
axesLength:它包含两个变量的元组,分别包含椭圆的长轴和短轴(长轴长度,短轴长度)。
angle:椭圆旋转角度,以度为单位。
startAngle:椭圆弧的起始角度,以度为单位。
endAngle:椭圆弧的终止角度,以度为单位。
color:它是要绘制的形状边界线的颜色。对于BGR,我们通过一个元组。例如:(255,0,0)为蓝色。
thickness:是形状边界线的粗细像素。厚度-1像素将用指定的颜色填充形状。
lineType:这是一个可选参数,它给出了椭圆边界的类型。
shift:这是一个可选参数。它表示中心坐标中的小数位数和轴的值。
二、代码实现
import cv2
import numpy as np
import PIL.Image as Image
import math
from skimage.draw import linedef FitEll(image_path):image_uf = cv2.imread(image_path)print("image的形状{}".format(image_uf.shape))binary = cv2.Canny(image_uf, 50, 150)cnt, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)image_f = np.zeros(image_uf.shape, dtype=np.uint8)step = 0for i in range(len(cnt)):ellipse = cv2.fitEllipse(cnt[i])cv2.ellipse(image_f,ellipse,(255-step*40,255-step*40,255-step*40),-1)(x, y), (a, b), ang = ellipsestep = step+1cv2.imshow('out',image_f)cv2.waitKey(0)image_f = Image.fromarray(cv2.cvtColor(image_f,cv2.COLOR_BGR2RGB))res_image_f = image_f.convert('P')return res_image_fif __name__ == '__main__':image_path = "test/test.png"res_image_f = FitEll(image_path)res_image_f.save("test/test_result.png")
三、加料内容
如上图所示的两个椭圆,求经过小椭圆右端点且与小椭圆长轴垂直的直线与大椭圆的第一个交点位置。
直接上代码
import cv2
import numpy as np
import PIL.Image as Image
import math
from skimage.draw import linedef FitEll(image_path):image_uf = cv2.imread(image_path)print("image的形状{}".format(image_uf.shape))binary = cv2.Canny(image_uf, 50, 150)cnt, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)image_f = np.zeros(image_uf.shape, dtype=np.uint8)step = 0cnt = sorted(cnt, key=lambda x: len(x))max_index = len(cnt)-1for i in range(len(cnt)):ellipse = cv2.fitEllipse(cnt[i])cv2.ellipse(image_f,ellipse,(255-step*40,255-step*40,255-step*40),-1)(x, y), (a, b), ang = ellipseif i!=max_index:p_l_x = x - (b / 2 * math.sin(ang * (math.pi) / 180))p_l_y = y + (b / 2 * math.cos(ang * (math.pi) / 180))p_r_x = x + (b / 2 * math.sin(ang * (math.pi) / 180))p_r_y = y - (b / 2 * math.cos(ang * (math.pi) / 180))cv2.circle(image_f, (int(p_l_x),int(p_l_y)), 8, (255,0,0), -1)cv2.circle(image_f, (int(p_r_x),int(p_r_y)), 8, (255, 0, 0), -1)else:x_b, y_b, w_b, h_b = cv2.boundingRect(cnt[i])if p_r_y!=p_l_y:k_l = -(p_r_x-p_l_x)/(p_r_y-p_l_y)b_l = -k_l*p_r_x+p_r_ypa, pb = (x_b, int(k_l * x_b + b_l)), ((x_b + w_b), int(k_l * (x_b + w_b) + b_l))else:pa, pb = (int(p_r_x), y_b), (int(p_r_x), y_b + h_b)for pt in zip(*line(*pa, *pb)):if cv2.pointPolygonTest(cnt[i], pt, False) == 0: cv2.circle(image_f, pt, 8, (0, 0, 255), -1)cv2.line(image_f,(int(p_r_x),int(p_r_y)),pt,(0,255,0),4)breakstep = step+1cv2.imshow('out',image_f)cv2.waitKey(0)image_f = Image.fromarray(cv2.cvtColor(image_f,cv2.COLOR_BGR2RGB))res_image_f = image_f.convert('P')return res_image_fif __name__ == '__main__':image_path = "test/test.png"res_image_f = FitEll(image_path)res_image_f.save("test/test_result.png")
这里主要用了一个cv2.pointPolygonTest(cnt[i], pt, False)函数,cnt即为最前面提取到的轮廓点集,pt为遍历线段上某一点坐标,返回值0(在轮廓上),1(在轮廓内),-1(在轮廓外),当返回值为0时,pt即为交点坐标。
求交点部分参考这位朋友的博客
(https://javis486.blog.csdn.net/article/details/109542852)