Editing out part of an image in python

Question:

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?

Asked By: Drew Wilkins

||

Answers:

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.

Answered By: Drew Wilkins