如何在OpenCV Python中锐化边缘

4
我正在学习图像处理,尝试使用Python中的OpenCV来锐化图像的边缘。我已经尽可能减少了噪声,但现在我想让图片的边缘更清晰。我尝试了cv2.Canny(),但效果不是很理想。
这是图片: enter image description here 在应用cv2.Canny()之后: enter image description here 但我想要更加清晰的文字边界或边缘。
这是我的代码:
import cv2
import matplotlib.pyplot as plt
img_1 = cv2.imread('noise/1.png',cv2.IMREAD_GRAYSCALE)
edges = cv2.Canny(img_1,200,200)
plt.imshow(edges)

什么是边缘锐化?由于图像尺寸,您可能无法获得更清晰的图像,因为您可以看到像素,所以它看起来很奇怪。 - DarK_FirefoX
3个回答

6

以下是使用Python/OpenCV处理此问题的一种方法:

  • 将输入读取为灰度图像
  • 对其进行二值化处理以确保它是二进制的
  • 应用形态学闭运算
  • 查找轮廓并通过在其上绘制黑色来删除输入中的所有小区域
  • 应用Canny边缘检测算法
  • 保存结果

输入图像:

enter image description here

import cv2
import numpy as np

# read image as grayscale
img = cv2.imread('K.png', cv2.IMREAD_GRAYSCALE)

# threshold to binary
thresh = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY)[1]

# apply morphology
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
morph = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)

# find contours - write black over all small contours
letter = morph.copy()
cntrs = cv2.findContours(morph, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cntrs = cntrs[0] if len(cntrs) == 2 else cntrs[1]
for c in cntrs:
    area = cv2.contourArea(c)
    if area < 100:
        cv2.drawContours(letter,[c],0,(0,0,0),-1)

# do canny edge detection
edges = cv2.Canny(letter, 200, 200)

# write results
cv2.imwrite("K_thresh.png", thresh)
cv2.imwrite("K_morph.png", morph)
cv2.imwrite("K_letter.png", letter)
cv2.imwrite("K_edges.png", edges)

# show results
cv2.imshow("K_thresh", thresh)
cv2.imshow("K_morph", morph)
cv2.imshow("K_letter", letter)
cv2.imshow("K_edges", edges)
cv2.waitKey(0)


阈值化后的图像:

enter image description here

应用形态学开运算:

enter image description here

去除了小区域:

enter image description here

Canny边缘检测:

enter image description here


1
我查看了已被接受的答案,感觉这是一个很好的过程,但很多步骤都不必要。例如,在二进制图像中使用Canny没有任何价值。它是一个复杂的过程,旨在检测具有软渐变的图像中的锐边。对于二进制图像,Sobel/Laplacian会给出与Canny相同的结果。
此外,这并不完全是边缘增强,更像是细化。
我的步骤如下:
  1. Threshold to binarize image

  2. Apply erode and dilate individually because then you have more control over things like in-place or not, memory management if you write in C/C++. This matters when you have more than one iteration, to handle more noise.

  3. Find contours. Based on use case, if you don't have loops (such as the loop in letter P), you can use find the only the external contours to optimise the code.

  4. Iterate through the contours and remove any contour below threshold from the list of contours.

  5. You already have the entire edge in the form of list of points here. If you can, use the edges as is or just create a blank image and draw all the valid contours with thickness 1 (only edges)

     import cv2
     import numpy as np
    
     # read image as grayscale
     img = cv2.imread('K.png', cv2.IMREAD_GRAYSCALE)
    
     # threshold to binary
     thresh = cv2.threshold(img, 1, 255, cv2.THRESH_BINARY)[1]
    
     # apply morphology.
     kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
     morph = cv2.erode(thresh, kernel)
     morph = cv2.dilate(morph, kernel)
    
     # find contours & draw only valid contour edges on a black image
     letter = np.zeros_like(img)
     cntrs, hier = cv2.findContours(morph, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
     for idx, c in enumerate(cntrs):
         area = cv2.contourArea(c)
         if area > 100:
             cv2.drawContours(letter, cntrs, idx, 255, 1, 8, hier)
    
     # write results
     cv2.imwrite("K_thresh.png", thresh)
     cv2.imwrite("K_morph.png", morph)
     cv2.imwrite("K_letter.png", letter)
    
     # show results
     cv2.imshow("K_thresh", thresh)
     cv2.imshow("K_morph", morph)
     cv2.imshow("K_letter", letter)
     cv2.waitKey(0)
    

1
谢谢提供示例代码。我已经测试并得到以下错误:----> morph = cv2.erode(thresh, kernel)TypeError: Expected Ptr<cv::UMat> for argument 'src'调试发现,cv2.threshold方法返回的不仅仅是图像,所以我可以通过更改“阈值到二进制”的那一行来解决它: r,thresh = cv2.threshold(img,1,255,cv2.THRESH_BINARY) - Mourits de Beer
谢谢。我没有在IDE中编写。我只是修改了现有的答案,所以可能错过了它。感谢您纠正我的错误。 - Shravya Boggarapu
在解决问题后,你得到想要的结果了吗?......如果不填充drawcontour中的轮廓,我认为层次结构甚至都不需要,但我只是为了保险起见添加了它(因为我不知道应用程序)。 - Shravya Boggarapu

1

首先使用Gaussian模糊去除图像噪声,然后使用自动阈值Canny算法,如下代码:

def auto_canny(image, sigma=0.33):
    v = np.median(image)
    lower = int(max(0, (1.0 - sigma) * v))
    upper = int(min(255, (1.0 + sigma) * v))
    edged = cv2.Canny(image, lower, upper)
    return edged

def main():
    img_1 = cv2.imread('noise/1.png',cv2.IMREAD_GRAYSCALE)
    blurred = cv2.GaussianBlur(img_1, (3, 3), 0)
    auto_edge = auto_canny(blurred)
    plt.imshow(auto_edge)

我希望这对你有所帮助


你是不是想用 img_1 而不是 gray?另外,您为什么要添加噪声来再次去除它? - DarK_FirefoX
1
是的,我已经修复了。图像有噪点,高斯模糊可以去除噪点并帮助边缘检测过程。 - ahmadgh74

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接