Search code examples
pythonopencvcrop

cut out a specific part of an image with opencv in python


I have an image of an IC die and I want to cut out the marking in the center.The marking is always at this specific position above the circle at the bottom left. The idea is to first find the circle position which I already accomplished with the hough circle transformation. Now I want to cut out the part where the marking is. It should ideally be a not a square or rectangle but something more like in the image:

example

This is a part of my code:

        cimg = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
        circles = cv2.HoughCircles(morph_image, cv2.HOUGH_GRADIENT, 1.3, 20, param1=50, param2=25, minRadius=15,
                                   maxRadius=19)

        if circles is not None:
            circles = np.uint16(np.around(circles))
            for i in circles[0, :]:
                # Zeichne äußeren Kreis
                cv2.circle(cimg, (i[0], i[1]), i[2], (0, 255, 0), 2)
                # Zeichne Kreiszentrum
                cv2.circle(cimg, (i[0], i[1]), 2, (0, 0, 255), 3)
                # Tupel mit x- und y-Koordinaten des Kreiszentrums
                circle_center = (i[0], i[1])
                print('Die Koordinaten des Kreiszentrums lauten: ', circle_center)
                """cv2.imshow('Kreis', cimg)
                cv2.waitKey(0)
                cv2.destroyAllWindows()"""
        else:
            circle_center = None
            print('Kein Kreis gefunden')
            """cv2.imshow('Kein Kreis', cimg)
            cv2.waitKey(0)
            cv2.destroyAllWindows()"""

so my cicle center has the center position of my circle (e.g. (124, 370)). How can I cut out this part of the image automatically? Can I somehow crop it out? Ideally I would want to crop the marking out into another image to inspect it separately but the normal cropping approach with marking_img = img[y:y+h, x:x+w] wouldn't work I guess.

EDIT: Here is the original image:

enter image description here

The output should be like the first image and if it is possible something like this:

enter image description here

So in the end I would want to have 2 images: One image with just the die without the marking and one image with just the marking


Solution

  • Here is one way in Python/OpenCV.

    • Read the image
    • Read the mask (separately created one time from your other image)
    • Convert the mask to gray and threshold it to binary, invert it and make it 3 channels
    • Get the center of the circle from your own code. (I have just measured it manually)
    • Set the expected x,y offsets of the bottom of the region of text from the center of the circle
    • Compute the expected top left corner of the mask from the center of the circle, the offsets and the height of the mask image
    • Put the mask into black image the size of the input at that location
    • Apply the new mask to the image to make the rest of the image black
    • Crop out the region of interest from the top left corner and the size of the original mask
    • OPTIONALLY, crop the original image
    • Save the results

    Input image:

    enter image description here

    Prepared mask image:

    enter image description here

    import cv2
    import numpy as np
    
    # read image
    img = cv2.imread('die.jpg')
    ht, wd, cc = img.shape
    
    # read mask as grayscale
    mask = cv2.imread('die_mask.png', cv2.IMREAD_GRAYSCALE)
    
    # threshold mask and invert
    mask = cv2.threshold(mask,0,255,cv2.THRESH_BINARY)[1]
    mask = 255 - mask
    hh, ww = mask.shape
    
    # make mask 3 channel
    mask = cv2.merge([mask,mask,mask])
    
    # set circle center
    cx = 62
    cy = 336
    
    # offsets from circle center to bottom of region
    dx = -20
    dy = -27
    
    # compute top left corner of mask using size of mask and center and offsets
    left = cx + dx
    top = cy + dy - hh
    
    # put mask into black background image
    mask2 = np.zeros_like(img)
    mask2[top:top+hh, left:left+ww] = mask
    
    # apply mask to image
    img_masked = cv2.bitwise_and(img, mask2)
    
    # crop region
    img_masked_cropped = img_masked[top:top+hh, left:left+ww]
    
    # ALTERNATE just crop input
    img_cropped = img[top:top+hh, left:left+ww]
    
    cv2.imshow('image', img)
    cv2.imshow('mask', mask)
    cv2.imshow('mask2', mask2)
    cv2.imshow('masked image', img_masked)
    cv2.imshow('masked cropped image', img_masked_cropped)
    cv2.imshow('cropped image', img_cropped)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    # save results
    cv2.imwrite('die_mask_inserted.jpg', mask2)
    cv2.imwrite('die_masked_image.jpg', img_masked)
    cv2.imwrite('die_masked_cropped.jpg', img_masked_cropped)
    cv2.imwrite('die_cropped.jpg', img_cropped)
    


    Mask inserted in black image:

    enter image description here

    Masked image:

    enter image description here

    Crop of masked image:

    enter image description here

    (Optional) Crop of input image:

    enter image description here