Search code examples
pythonopencvobject-detection

Editing out part of an image in python


I have a collection of images featuring ships on or near the horizon in open ocean. I am trying to create an image that removes the ship(s) from the image while leaving most of the background intact. My initial attempt was to scan each row and column of the blurred image in black and white and replace the value in it with the median of the row if the value exceeds a certain threshold:

colorImage= cv2.imread(path,1)  # where path is the directory to my image
gray = cv2.cvtColor(colorImage, cv2.COLOR_BGR2GRAY)
gray= cv2.GaussianBlur(gray, (21,21), 0)
bkgd= gray
imageHeight, imageWidth= bkgd.shape

for row in range(0, imageHeight):
    for column in range(0, imageWidth):
        if bkgd[row, column] > 100:
            bkgd[row, column]= int(np.median(bkgd[row]))

100 is an arbitrary value that I chose that closely resembles the minimum value the sky would be in a blurred b/w image, thus leaving all the noise from the ocean intact (desired for comparing the original with the ship to the one without). This process slow and produces erratic results when there is noise from clouds in the sky. Is there a better method available in OpenCV or some other library to accomplish what I'm trying to produce?


Solution

  • Well I figured out a solution before anyone replied, so here's what I came up with. I was on the right track by replacing each pixel with the median, but I instead utilized some numpy array magic and iterated over just the rows instead of the rows and columns. I also applied the method to the color image in BGR instead of black and white:

    def remove_ship(colorImage):
    
        imageHeight, imageWidth, channel= colorImage.shape
        bkgd= colorImage
        highFactor= 1.05
        lowFactor= .95
        for row in range(0, imageHeight):
            bRow= np.array(bkgd[row, :, 0])
            gRow= np.array(bkgd[row, :, 1])
            rRow= np.array(bkgd[row, :, 2])
            noBlack= np.where(bRow != 0) # exclude the 0's in the array to find the true median.
            black= np.where(bRow == 0)   # collect the indices of the blacks to recolor them black later
            bMedian= np.median(bRow[noBlack])
            bReplace= np.where(bRow > int(highFactor * bMedian))
            bReplace= np.append(bReplace, [np.where(bRow < int(lowFactor * bMedian))])
            bRow[bReplace]= bMedian
            bRow[black]= 0 
            bkgd[row, :, 0]= bRow
    
            gMedian= np.median(gRow[noBlack])
            gReplace= np.where(gRow > int(highFactor * gMedian))
            gReplace= np.append(gReplace, [np.where(gRow < int(lowFactor * gMedian))])
            gRow[gReplace]= gMedian
            gRow[black]= 0
            bkgd[row, :, 1]= gRow
    
            rMedian= np.median(rRow[noBlack])
            rReplace= np.where(rRow > int(highFactor * rMedian))
            rReplace= np.append(rReplace, [np.where(rRow < int(lowFactor * rMedian))])
            rRow[rReplace]= rMedian
            rRow[black]= 0
            bkgd[row, :, 2]= rRow
    

    If anyone has a better, more efficient, or elegant solution, I'd still love to be educated.