OpenCV 4 with Python Blueprints
上QQ阅读APP看书,第一时间看更新

Detecting and emphasizing prominent edges

Again, when it comes to edge detection, the challenge often does not lie in how the underlying algorithm works, but instead lies in which particular algorithm to choose for the task at hand. You might already be familiar with a variety of edge detectors. For example, Canny edge detection (cv2.Canny) provides a relatively simple and effective method to detect edges in an image, but it is susceptible to noise.

The Sobel operator (cv2.Sobel) can reduce such artifacts, but it is not rotationally symmetric. The Scharr operator (cv2.Scharr) was targeted at correcting this but only looks at the first image derivative. If you are interested, there are even more operators for you, such as the Laplacian ridge operator (which includes the second derivative), but they are far more complex. And in the end, for our specific purposes, they might not look better, perhaps because they are as susceptible to lighting conditions as any other algorithm.

For the purposes of this project, we will choose a function that might not even be associated with conventional edge detection—cv2.adaptiveThreshold. Like cv2.threshold, this function uses a threshold pixel value to convert a grayscale image into a binary image. That is, if a pixel value in the original image is above the threshold, then the pixel value in the final image will be 255. Otherwise, it will be 0.

However, the beauty of adaptive thresholding is that it does not look at the overall properties of the image. Instead, it detects the most salient features in each small neighborhood independently, without regard to the global image characteristics. This makes the algorithm extremely robust to lighting conditions, which is exactly what we want when we seek to draw bold black outlines around objects, and people in a cartoon.

However, it also makes the algorithm susceptible to noise. To counteract this, we will preprocess the image with a median filter. A median filter does what its name suggests: it replaces each pixel value with the median value of all the pixels in a small pixel neighborhood. Therefore, to detect edges, we follow this short procedure:

  1. We first convert the RGB image (rgb_image) into grayscale (img_gray) and then apply a median blur with a seven-pixel local neighborhood:
    # convert to grayscale and apply median blur
img_gray = cv2.cvtColor(rgb_image, cv2.COLOR_RGB2GRAY)
img_blur = cv2.medianBlur(img_gray, 7)
  1. After reducing the noise, it is now safe to detect and enhance the edges using adaptive thresholding. Even if there is some image noise left, the cv2.ADAPTIVE_THRESH_MEAN_C algorithm with blockSize=9 will ensure that the threshold is applied to the mean of a 9 x 9 neighborhood minus C=2:
    gray_edges = cv2.adaptiveThreshold(img_blur, 255,
cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY, 9, 2)

The result of the adaptive thresholding looks like this:

Next, let's look at how to combine colors and outlines to produce a cartoon in the following section.