Reputation: 2405
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
Reputation: 46600
Here are two methods depending on the situation:
cv2.dilate()
- enhance all white pixelscv2.drawContours()
- enhance specific pixelsExample
Using this input image
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)
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
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
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 :
Upvotes: 1
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