I have some sprite sheets. In some cases, the bounding boxes of the sprites are overlapping, even though the sprites themselves are not overlapping:
In other cases, the bounding boxes are not overlapping:
To extract the individual sprites, I am doing the following:
im = cv2.imread("trees.png") # read image
imGray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY) # convert to gray
contours, _ = cv2.findContours(imGray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # contouring
sortedContours = sorted(contours, key=cv2.contourArea, reverse=True) # sorting, not necessary...
for contourIdx in range(0,len(sortedContours)-1): # loop with index for easier saving
contouredImage = im.copy() # copy image
contouredImage = cv2.drawContours(contouredImage, sortedContours, contourIdx, (255,255,255), -1) # fill contour with white
extractedImage = cv2.inRange(contouredImage, (254,254,254), (255,255,255)) # extract white from image
resultImage = cv2.bitwise_and(im, im, mask=extractedImage) # AND operator to get only one filled contour at a time
x, y, w, h = cv2.boundingRect(sortedContours[contourIdx]) # get bounding box
croppedImage = resultImage[y:y + h, x:x + w] # crop
cv2.imwrite("contour_"+str(contourIdx)+".png", croppedImage) # save
This works great for the former image where the bounding boxes are overlapping, but fails in the latter case where the bounding boxes are not overlapping. Why is that and how can I fix it ?
EDIT: In the former case, as expected, it detects the individual contours and outputs each separately. But in the latter case, it doesn't seem to detect any individual contours, or rather the whole image is output.


The problem is that your images have not been created in the same way. If you run ExifTool on the overlap image, it has this:
And the no-overlap image:
Which of those aspects is causing the exact issue doesn't really matter, but if you examine the image after loading it with OpenCV, you'll find that the transparent pixels in the no-overlap image are green, and the grey-scale conversion turns all the pixels into the same colour.
However, this code works for both images:
Instead of picking black, you might pick white, depending on what you expect to see in the image. The change above works, but it doesn't means it's the best way to approach this. I would imagine the alpha channel (
im[:, :, 3]) actually has everything you need and perhaps you should work with that instead of the RGB channel, to find the contours.Here's a rewrite of your code that's a bit easier to understand and uses the alpha channel for the contouring: