边缘检测对于缺口明细的图形非常好用例如这种:
在或者是
两种都是缺口边缘明显,只要稍加处理就可以得到轮廓边缘
直接开搞
读取文件
# 读取文件
image = Image.open(path)
img = image.copy() # 复制
img = np.array(img) # 转化为numpy
img = cv2.resize(img, (268, 100)) # 用cv2resize
img = img[:, :, (2, 1, 0)] #RGB BGR图像
这里用的 PIL的读取图片而不是opencv 因为我的图片路径存在中文名称用opencv的imread是读取不了的。 所以先用pil读取 再转化为numpy格式最后放到cv2.resize重组,注意一点是PIL读入图片默认通道顺序是RGB,接下来的操作要用cv所以转化为cv.show读取的BGR通道顺序
看一下结果:
图像增强
这里图像增强的方式是通过观察缺口都处于阴影所以该位置通道值要小于150的区域变为黑色,否则转为白色,方便为后续的边缘检测做铺垫。
# 将通道值小于150的转变为黑色
for h in range(img.shape[0]):
for w in range(img.shape[1]):
if img[h, w, 0] < 150 and img[h, w, 1] < 150 and img[h, w, 2] < 150:
for c in range(3):
img[h, w, c] = 0
else:
for c in range(3):
img[h, w, c] = 255
此时可以看出缺口位置非常明显。
边缘检测+轮廓提取
直接使用canny算法进行轮廓提取
canny_img = cv2.Canny(img, 0, 100) # 边缘检测
结果:
此时可以看出轮廓非常清晰了。只需要进行轮廓提取就可以了。
counts, _ = cv2.findContours(canny_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # 轮廓检测
for c in counts:
x, y, w, h = cv2.boundingRect(c)
# 去除较小先验框
if w < 20:
continue
if h < 20:
continue
cv2.rectangle(img1, (x, y), (x + w, y + h), (0, 0, 255), 2)
print(f"左上点的坐标为:{x, y},右下点的坐标为{x + w, y + h}")
边缘检测出来的轮廓肯定有很多种我们不需要的将其通过宽和高过滤掉即可。
如果不过滤呈现的是:
将其过滤后:
得到坐标
左上点的坐标为:(190, 44),右下点的坐标为(222, 76)
完整代码
def hk(path):
# 读取文件
image = Image.open(path)
img = image.copy() # 复制
img = np.array(img) # 转化为numpy
img = cv2.resize(img, (268, 100)) # 用cv2resize
img = img[:, :, (2, 1, 0)] # BGR图像转RGB
img1 = img.copy()
# 将通道值小于150的转变为黑色
for h in range(img.shape[0]):
for w in range(img.shape[1]):
if img[h, w, 0] < 150 and img[h, w, 1] < 150 and img[h, w, 2] < 150:
for c in range(3):
img[h, w, c] = 0
else:
for c in range(3):
img[h, w, c] = 255
canny_img = cv2.Canny(img, 0, 100) # 边缘检测
counts, _ = cv2.findContours(canny_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # 轮廓检测
for c in counts:
x, y, w, h = cv2.boundingRect(c)
# 去除较小先验框
if w < 20:
continue
if h < 20:
continue
cv2.rectangle(img1, (x, y), (x + w, y + h), (0, 0, 255), 2)
print(f"左上点的坐标为:{x, y},右下点的坐标为{x + w, y + h}")
return img1
透明通道问题
类似于这种图片我们肉眼看到的缺口计算机是看不到的,原因是该图片存在透明通道,也就是rgba。
解决此类型的图片思路就是 将透明通道区域的颜色变为对比度强的颜色如黑色,在进行边缘检测和轮廓提取。
我们用cv读取之后的样子是
那么缺口在哪里呢?
image = cv2.imread(path, -1)
cv2.imshow("image", image)
b, g, r, a = cv2.split(image)
b[a != 255] = 0
g[a != 255] = 0
r[a != 255] = 0
img = cv2.merge([b, g, r])
cv2.imshow("image", image)
``
- [ ] List item
我们先将图片读取进来用imread -1的参数将全部通道读取进来
然后将其通道分割,将找到透明通道不等于255的位置,再将其bgr通道转为黑色
得到的结果为:
之后就和上面一样进行边缘检测,轮廓提取之后再找到缺口位置。
结果展示
此方法仅适用图像类型简单,缺口较为明显。