pippo1980
pippo1980

Reputation: 3061

OpenCV find contours of an RGBA image not working

With input image as Contours_X.png [Contours_X.png: PNG image data, 819 x 1154, 8-bit grayscale, non-interlaced] :

enter image description here

Stealing code from Python Find Contours white region only OpenCV code :

import cv2 as cv
import numpy as np

def generate_X_Y(image_path):
    
    image = cv.imread(image_path)
    
    # image = cv.imread(image_path, cv.IMREAD_UNCHANGED)
    
    cv.imwrite("image_ori.png" , image)
    
    print('image[0] : ', image[0])
    
    gray = cv.cvtColor(image, cv.COLOR_RGBA2GRAY)
    
    print('gray[0] : ', gray[0])
    
    ## CHANGED TO:
    ret, thresh = cv.threshold(gray, 128, 255, cv.THRESH_BINARY_INV)
    
    cv.imwrite("image2.png", thresh)
    
    contours, hierarchies = cv.findContours(thresh, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
    blank = np.zeros(thresh.shape[:2], dtype='uint8')
    cv.drawContours(blank, contours, -1, (255, 0, 0), 1)
    cv.imwrite("Contours.png", blank)
    
    print('len(contours) : ' , len(contours))
    
    for i in contours:
        
        cv.drawContours(image, [i], -1, (0, 255, 0), 2)
        
    cv.imwrite("image.png", image)
    

if __name__ == '__main__':
    
    image_path = 'Contours_X.png'  # Provide the correct path in Colab
    
    # image_path = 'input_alpha.png' 
    
    generate_X_Y(image_path)

I get output image.png [image.png: PNG image data, 819 x 1154, 8-bit/color RGB, non-interlaced] :

enter image description here

While using input_alpha_2.png [input_alpha_2.png: PNG image data, 1000 x 1200, 8-bit/color RGBA, non-interlaced] :

enter image description here

enter image description here

and code:

import cv2 as cv
import numpy as np


def generate_X_Y(image_path):
    
    # image = cv.imread(image_path)
    
    image = cv.imread(image_path, cv.IMREAD_UNCHANGED)
    
    cv.imwrite("image_ori.png" , image)
    
    print('image[0] : ', image[0])
    
    gray = cv.cvtColor(image, cv.COLOR_RGBA2GRAY)
    
    print('gray[0] : ', gray[0])
    
    ## CHANGED TO:
    ret, thresh = cv.threshold(gray, 128, 255, cv.THRESH_BINARY_INV)
    
    cv.imwrite("image2.png", thresh)
    
    contours, hierarchies = cv.findContours(thresh, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
    blank = np.zeros(thresh.shape[:2], dtype='uint8')
    cv.drawContours(blank, contours, -1, (255, 0, 0), 1)
    cv.imwrite("Contours.png", blank)
    
    print('len(contours) : ' , len(contours))
    
    for i in contours:
        
        cv.drawContours(image, [i], -1, (0, 255, 0), 20)
        

    cv.imwrite("image.png", image)
    

if __name__ == '__main__':
    
    # image_path = 'Contours_X.png'  # Provide the correct path in Colab
    
    image_path = 'input_alpha_2.png' 
    
    generate_X_Y(image_path)

I get image.png [image.png: PNG image data, 1000 x 1200, 8-bit/color RGBA, non-interlaced] :

enter image description here

enter image description here

Why I don't get a nice green border around the subject like in first example?

As suggested in comments :

Your base BGR image (under the alpha channel) has your green lines. The alpha channel is covering it). Remove the alpha channel to see it.

and doing that with :

cv.imwrite("image.png", image[:,:,:3])

I get image.png: PNG image data, 1000 x 1200, 8-bit/color RGBA, non-interlaced :

enter image description here

but still I don't get how a transparent alpha channel could hide a contour and why do I get the gray background, that I believe could be the area of the biggest contour in my image the squared external black border?

More on this using :

cntsSorted = sorted(contours, key = lambda x: cv.contourArea(x) , reverse=True)
    
for index , i in enumerate(cntsSorted) :
        
    print(cv.contourArea(i))
        
    if index > 0 :

        cv.drawContours(image, [i], -1, (0, 255, 0), 20)
  
cv.imwrite("image.png", image)    

cv.imwrite("image_rem.png", image[:,:,:3])
    

The second image doesn't have the more external green border but still keeps the dark grey background.

Upvotes: 0

Views: 170

Answers (2)

Tino D
Tino D

Reputation: 2648

Another way to also draw the contour on the image, since it has four channel, is to specify four channels in the contour drawing:

im = cv2.imread("disneyRGBA.png", cv2.IMREAD_UNCHANGED)
imContoured = im.copy()
imGray = cv2.cvtColor(im, cv2.COLOR_RGBA2GRAY)
ret, thresh = cv2.threshold(imGray, 128, 255, cv2.THRESH_BINARY_INV)
contours, hierarchies = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
# I copied this from you, but you could have easily went with cv2.drawContours(imContoured, contours, -1, (0, 255, 0, 255), 20)
for i in contours:
    imContoured = cv2.drawContours(imContoured, [i], -1, (0, 255, 0, 255), 20)

Here is the result after writing the image contoured with 255 on the alpha channel:

255 on alpha channel

Here is the result after writing the image contoured with 0 on the alpha channel:

0 on alpha channel

Same image from Adobephotoshop:

enter image description here

Using :

imContoured = cv2.drawContours(imContoured, [i], -1, (0, 255, 0, 0), 100)

0 on alpha channel and thicker contours (100) :

enter image description here

Here is the result after writing the image contoured with 127 on the alpha channel:

127 on alpha

I somehow expected the other way around, that 255 is completely transparent and 0 is opaque. But seems to be the other way around.

Upvotes: 1

fmw42
fmw42

Reputation: 53071

My python/opencv is broke at the moment. So if I take your transparent image

enter image description here

and remove the alpha channel in Imagemagick, I get

convert image.png -alpha off x.png

enter image description here

Perhaps you are using the wrong input image?

Upvotes: 1

Related Questions