Search code examples
pythonc#opencvemgucv

Coloring edges in python cv2 and C# emgu cv


So I'm trying to reproduce a cool filter I did a while back in C# (emgucv) in Python cv2. Despite my hopes it's not going very smoothly. The programs suppose to highlight edges and color them with a cool looking gradient.

The code in C#:

        {
        Image<Gray, byte> gray= imgColored.Convert<Gray, byte>();
        Image<Gray, float> photo_dx = gray.Sobel(1, 0, 3);
        Image<Gray, float> photo_dy = gray.Sobel(0, 1, 3);
        Image<Gray, float> photo_grad = new Image<Gray, float>(gray.Size);
        Image<Gray, float> photo_angle = new Image<Gray, float>(gray.Size);
        CvInvoke.CartToPolar(photo_dx, photo_dy, photo_grad, photo_angle, true);
        Image<Hsv, float> coloredEdges = gray.Convert<Hsv, float>(); 

        for (int j = 0; j < coloredEdges.Cols; j++)
            for (int i = 0; i < coloredEdges.Rows; i++)
            {
                Hsv pix = coloredEdges[i, j];

                pix.Hue = photo_angle[i, j].Intensity;
                pix.Satuation = 1;
                pix.Value = photo_grad[i, j].Intensity;

                coloredEdges[i, j] = pix;
            }

        coloredEdges.Save("test.jpg");
       }

The code in Python:

def LSD_ify(image, mag, angle):
     image = image = image.astype(np.float64)
     height, width, depth = image.shape

     for x in range(0, height):
         for y in range(0, width):
            image[x, y, 0] = angle[x, y]
            image[x, y, 1] = 1
            image[x, y, 2] = mag[x, y]

    return image

def main():
       image = plt.imread(str(sys.argv[1]))
       gray_image = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
       g2bgr = cv.cvtColor(gray_image, cv.COLOR_GRAY2BGR) #cv2 cant convert gray to HSV directly, so i had to convert back to colored and finally to HSV

       gx = cv.Sobel(gray_image, cv.CV_64F, 1, 0, ksize = 3)
       gy = cv.Sobel(gray_image, cv.CV_64F, 0, 1, ksize = 3)

       mag, angle = cv.cartToPolar(gx, gy, angleInDegrees = True)
       hsv_image = cv.cvtColor(g2bgr, cv.COLOR_BGR2HSV)

       lsd = LSD_ify(hsv_image, mag, angle)

       cv.imwrite("test.jpg", lsd)

if __name__ == "__main__":
      main()

The code is mostly identical(i think?), the results they produce however are different. The input image: enter image description here

C# program: enter image description here

Python script: enter image description here

Does anyone have any insight on what I'd have to do to get identical results in Python? I'm not sure how things work in the background in Python.


Solution

  • I think this is what you are trying to do in Python/OpenCV. Python HSV hue is limited to range 0 to 180 so your angle needs to be scaled to that range. Similarly the magnitude is greater than 255 and also needs to be scaled to the range 0 to 255. The saturation you want would be a constant 255. I use Skimage to do the scaling. I have printed out the shape and min and max values at various places to show you these issues.

    I believe the process is as follows:

    • Read the input
    • Convert it to gray
    • Get the Sobel x and y derivatives
    • Compute the magnitude and angle from the derivatives and scale mag to range 0 to 255 and angle to range 0 to 180
    • Merge the angle, the magnitude and the magnitude into a 3 channel image as if HSV with angle first, then the magnitudes.
    • Replace the second channel (channel 1) with 255 for the saturation
    • Convert this HSV image to BGR as the result
    • Save the result

    Input:

    enter image description here

    import cv2
    import numpy as np
    import skimage.exposure as exposure
    
    # read the image
    img = cv2.imread('rabbit.jpg')
    
    # convert to gray
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    # apply sobel derivatives
    sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3)
    sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3)
    
    print(sobelx.shape, np.amin(sobelx), np.amax(sobelx))
    print(sobely.shape, np.amin(sobely), np.amax(sobely))
    print("")
    
    # get magnitude and angle
    mag, angle = cv2.cartToPolar(sobelx, sobely, angleInDegrees = True)
    
    print(mag.shape, np.amin(mag), np.amax(mag))
    print(angle.shape, np.amin(angle), np.amax(angle))
    print("")
    
    # normalize mag to range 0 to 255 and angle to range 0 to 180
    mag = exposure.rescale_intensity(mag, in_range='image', out_range=(0,255)).clip(0,255).astype(np.uint8)
    angle = exposure.rescale_intensity(angle, in_range='image', out_range=(0,180)).clip(0,180).astype(np.uint8)
    
    print(mag.shape, np.amin(mag), np.amax(mag))
    print(angle.shape, np.amin(angle), np.amax(angle))
    
    # combine channels as if hsv where angle becomes the hue and mag becomes the value. (saturation is not important since it will be replace by 255)
    hsv = cv2.merge([angle, mag, mag])
    hsv[:,:,1] = 255
    
    # convert hsv to  bgr
    result = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
    
    # save results
    cv2.imwrite('rabbit_color_edges.jpg', result)
    
    # show result
    cv2.imshow('result', result)  
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    enter image description here

    ADDITION

    If I scale the magnitude (or gray) by doubling "out_range":

    mag = exposure.rescale_intensity(mag, in_range='image', out_range=(0,255)).clip(0,255).astype(np.uint8) 
    

    to

    mag = exposure.rescale_intensity(mag, in_range='image', out_range=(0,510)).clip(0,255).astype(np.uint8)
    

    Then I get very close to your result:

    enter image description here