Jim
Jim

Reputation: 353

OpenCV Python - Replace Channels in an Image

I'm currently using a Sobel filter on the x and y axis and computing an angle for that. The output image is a weighted average of the two directions that is in HSV colorspace.

I'm trying to replace the H channel with the computed angles (a 1d list in the range [0,pi]) and then set the S and V channel to 255 if H is non-zero

The code I'm using to compute the angles is:

 Sx = cv2.Sobel(gray, -1, 1, 0, ksize=3)
 Sy = cv2.Sobel(gray, -1, 0, 1, ksize=3)
 theta = np.arctan2(Sy, Sx)

And to swap the channels:

color[:,:,0] = np.rad2deg(theta)
color[color[:, :, 0] > 0, 1] = 255
color[color[:, :, 0] > 0, 2] = 255

Before swapping the channels I have: before But after the swap I get: after

I'm expected results similar to:

enter image description here

If the input image was a white background with a black circle

Upvotes: 1

Views: 1803

Answers (1)

vasiliykarasev
vasiliykarasev

Reputation: 871

There are a couple of issues related to colorspace (HSV and BGR, array dtype's, and ranges). For most part, OpenCV expects the user to pay attention to these things.

  1. You are treating the numpy array as being in HSV colorspace, but cv2.imshow interprets the image as BGR. The color array needs to be explicitly converted to BGR, like this: cv2.imshow("image", cv2.cvtColor(color, cv2.COLOR_HSV2BGR))

  2. If gray is a uint8 image, then Sx/Sy will contain only non-negative values. That basically sets all negative derivatives to zero, which is wrong. Would suggest changing Sobel ddepth argument to be CV_32F (so that it ensures that output is floating point): Sx = cv2.Sobel(gray, cv2.CV_32F, 1, 0, ksize=3) (and similarly for Sy). Or you can explicitly ensure that gray.dtype is np.float32.

  3. Output of np.rad2deg is (in theory) in [0, 360] range, but the numpy array representing an image should have values in [0, 255]. Here is one possible way to deal with this situation:

theta = np.arctan2(Sy, Sx)
# Instead of converting [-pi, pi] range to degrees, linearly convert
# the array to [0, 255] range using cv2.normalize.
hue_value = cv2.normalize(theta, dst=None, alpha=255.0, norm_type=cv2.NORM_MINMAX)
# Select nonzero values as a mask.
mask = np.logical_or(theta > 0.01, theta < -0.01)
color[:,:,0] = hue_value
color[mask, 1] = 255
color[mask, 2] = 255

Upvotes: 3

Related Questions