TheStrangeQuark
TheStrangeQuark

Reputation: 2405

Thicken a one pixel line

I'm using OpenCV to do some image processing on Python. I'm trying to overlay an outline on an image where the outline was made from a mask. I'm using cv2.Canny() to get the outline of the mask, then changing that to a color using cv2.cvtColor() then finally converting that edge to cyan using outline[np.where((outline == [255,255,255]).all(axis=2))] = [180,105,255]. My issue now is that this is a one pixel thick line and can barely be seen on large images. This outline is all [0,0,0] except on the points I apply as a mask onto my color image using cv2.bitwise_or(img, outline.

I'm currently thickening this outline by brute forcing and checking every single pixel in the bitmap to check if any of its neighbors are [180,105,255] and if so, that pixel will also change. This is very slow. Is there any way using numpy or openCV to do this automatically? I was hoping for some conditional indexing with numpy, but can't find anything.

Upvotes: 8

Views: 14051

Answers (3)

nathancy
nathancy

Reputation: 46600

Here are two methods depending on the situation:


Example

Using this input image

enter image description here

import cv2
import numpy as np

# Create test image
mask = np.zeros((200,200,3), dtype=np.uint8)
cv2.line(mask, (50, 100), (150, 100), (255,255,255), 1)

Method #1

All pixels in the foreground (white) will have its area increased with cv2.dilate(). We create a structuring element and dilate. More iterations will generate a thicker line

iterations=1 (left), iterations=2 (middle), iterations=3 (right)

enter image description here enter image description here enter image description here

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
dilate = cv2.dilate(mask, kernel, iterations=1)

Method #2

When we want to only enhance a particular part of an image but leave other sections untouched, we can use cv2.drawContours(). We can specify the color and adjust the size using the thickness parameter. The result will be similar to cv2.dilate() with the added benefit of color selection

enter image description here

gray = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
cnts = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]

for c in cnts:
    cv2.drawContours(mask, [c], -1, (255,255,255), thickness=15)

Upvotes: 10

vSomers
vSomers

Reputation: 426

You need to perform a dilation. A dilation is a morphological operation which causes brighter regions within the input image to grow. As explained here, dilation is performed by convoluting the input image with some kernel:

As the kernel B is scanned over the image, we compute the maximal pixel value overlapped by B and replace the image pixel in the anchor point position with that maximal value.

The anchor point is usually the center of the kernel.

In your case, I would suggest using an elliptical kernel with a size of your choice. With one pixel thick lines and with a kernel of size (n,n), the lines in your resulting dilated image will be approximately 2*n thick.

Here's a sample code :

    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
    result = cv2.dilate(outline, kernel)

Here is an example of dilation :

enter image description here

Upvotes: 1

Dave Caulton
Dave Caulton

Reputation: 11

I'd take a look at morphological operations. Dilation sounds closest to what you want. You might need to work on a subregion with your line if you don't want to dilate the rest of the image.

Upvotes: 1

Related Questions