Search code examples
pythonimage-processingedge-detection

Remove image background so that only the logo (usually some text) remains as png



I would like to extract logos from golf balls for further image processing. I have already tried different methods.

  1. I wanted to use the grayscale value of the images to locate their location and then cut it out. Due to many different logos and a black border around the images, this method unfortunately failed.
  2. as my next approach I thought that I first remove the black background and then repeat the procedure from 1. but also without success because there is a dark shadow in the lower left corner and this is also recognized as the "logo" with the grayscale method. Covering the border further on the outside is not a solution, because otherwise logos that are on the border will also be cut away or only half of them will be detected.
  3. I used the edge detection algorithm Canny of the Open CV library. The detection looked very promising, but I was not able to extract only the logo from the detection, because the edge of the Golfball was also recognized.

Any solution is welcome. Please forgive my English. Also, I am quite a beginner in programming. Probably there is a very simple solution to my problem but I thank you in advance for your help.

Here are 2 example images first the type of images from which the logos should be extracted and then how the image should look like after extraction.

Thank you very much. Best regards T

enter image description here enter image description here


Solution

  • This is essentially "adaptive" thresholding, except this approach doesn't need to threshold. It adapts to the illumination, leaving you with a perfectly fine grayscale image (or color, if extended to do that).

    • median blur (large kernel size) to estimate ball/illumination
    • division to normalize

    illumination:

    illumination

    normalized (and scaled a bit):

    normalized

    thresholded with Otsu:

    otsu

    def process(im, r=80):
        med = cv.medianBlur(im, 2*r+1)
        with np.errstate(divide='ignore', invalid='ignore'):
            normalized = np.where(med <= 1, 1, im.astype(np.float32) / med.astype(np.float32))
        return (normalized, med)
    
    normalized, med = process(ball1, 80)
    # imshow(med)
    # imshow(normalized * 0.8)
    ret, thresh = cv.threshold((normalized.clip(0,1) * 255).astype('u1'), 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)
    # imshow(thresh)
    

    ball2 illumination

    ball2 normalized

    ball2 otsu