Detecting if an image has a border using PIL

I am using this code to remove an image border using PIL:

def RemoveBlackBorders(img):
    bg = Image.new(img.mode, img.size, img.getpixel((0,0)))
    diff = ImageChops.difference(img, bg)
    diff = ImageChops.add(diff, diff, 2.0, -100)
    bbox = diff.getbbox()
    if bbox:
        return img.crop(bbox)

      

What I found here: Trim Whitespace Using PIL

And I use this to process all the images contained in the folder:

def CropImages():
    global FOLDER
    for i in range(1, len(os.listdir(FOLDER))+1):
        image = FOLDER + "\\" + str(i) + ".jpg"
        img = Image.open(image)
        img = RemoveBlackBorders(img)
        img.save(image, "JPEG")

      

Now the problem is that it takes a long ~ 1000 images to complete this operation, so I want to check, BEFORE starting the process, if one of the images in the folder has a border to be deleted, because if the 1.jpg image has a border , the image [n] .jpg will have it for sure too.

+3


source to share


1 answer


I haven't worked on PIL very much, so I'll try to implement a solution using OPenCV, and if you're satisfied you can put some effort into rewriting your code using PIL.

Assumptions:

  • Borders are present only at the top and bottom of this image Frame.
  • The borders are dark black.

So let's take a sample image:

enter image description here

First of all, we load the given image to find the length and width of the given image.

import cv2

img = cv2.imread("sample_frame.jpg") #Loading an image in RGB mode.

height, width, channels = img.shape

      

Now we iterate over pixels parallel to the height and distance (width * 0.5) from both sides, or you can tell the center of the image.

As we know, the border of dark black matches our assumption, so for black (R, G, B) = (0, 0, 0). Or we can say that all values ​​are strictly less than 4 (including some noise in the image.).

border_threshold_R = 4
border_threshold_G = 4
border_threshold_B = 4

mid_pixels = []
top_border_height = 0
bottom_border_height = 0

      

Iterating in the top half:

for i in xrange(height/2):
    mid_pixel_top_half = img[i][width/2]
    R, G, B = mid_pixel_top_half[2], mid_pixel_top_half[1], mid_pixel_top_half[0]
    if (R<border_threshold_R) and (G<border_threshold_G) and (B<border_threshold_B):
        top_border_height+=1
    else:
        break

      



Iterating the bottom half:

for i in xrange(height-1, (height/2)-1, -1):
    mid_pixel_bottom_half = img[i][width/2]
    R, G, B = mid_pixel_bottom_half[2], mid_pixel_bottom_half[1], mid_pixel_bottom_half[0]
    if (R<border_threshold_R) and (G<border_threshold_G) and (B<border_threshold_B):
        bottom_border_height+=1
    else:
        break

      

We now have a range in which a given image is dark black, but we still cannot tell if it is in a border or not. To solve this problem, random iteration is performed in a direction parallel to the image width, but at a distance less than top_border_height

and bottom_border_height

, and check if we can successfully iterate over a row with (R, G, B) pixel values ​​less than the threshold (<4) ... For each successful iteration of the line, we increment the variable that shows the corrected border width.

Let's define a function that returns true only when the full string has RGB values ​​less than the threshold.

def iterate_line(img, r_thresh, g_thresh, b_thresh, y):
    """
        This function returns true only when a given row at a height "y"
        from the origin(top - left) if fully black and false othrwise
    """
    for i in img[y]:
        if not((i[0]<b_thresh) and (i[1]<g_thresh) and i[2]<b_thresh):
            return False
    return True

      

And now iterate over the estimated border sizes to find the exact border sizes.

corrected_top_border_height = 0
corrected_bottom_border_height =0

for i in xrange(top_border_height):
    if iterate_line(img, border_threshold_R, border_threshold_G, border_threshold_B, i):
        corrected_top_border_height+=1
    else:
        break

for i in xrange(height-1, height-1-bottom_border_height, -1):
    if iterate_line(img, border_threshold_R, border_threshold_G, border_threshold_B, i):
        corrected_bottom_border_height+=1
    else:
        break

      

For this image, the corresponding values ​​are:

top_border_height                : 15
bottom_border_height             : 15
corrected_top_border_height      : 8
corrected_bottom_border_height   : 8

      

The complete code might look like this:

import cv2

img = cv2.imread("sample_frame.jpg") #Loading an image in RGB mode.

def iterate_line(img, r_thresh, g_thresh, b_thresh, y):
    """
        This function returns true only when a given row at a height "y"
        from the origin(top - left) if fully black and false othrwise
    """
    for i in img[y]:
        if not((i[0]<b_thresh) and (i[1]<g_thresh) and i[2]<b_thresh):
            return False
    return True


height, width, channels = img.shape

print width, height

border_threshold_R = 4
border_threshold_G = 4
border_threshold_B = 4

top_border_height = 0
bottom_border_height = 0

for i in xrange(height/2):
    mid_pixel_top_half = img[i][width/2]
    R, G, B = mid_pixel_top_half[2], mid_pixel_top_half[1], mid_pixel_top_half[0]
    if (R<border_threshold_R) and (G<border_threshold_G) and (B<border_threshold_B):
        top_border_height+=1
    else:
        break

for i in xrange(height-1, (height/2)-1, -1):
    mid_pixel_bottom_half = img[i][width/2]
    R, G, B = mid_pixel_bottom_half[2], mid_pixel_bottom_half[1], mid_pixel_bottom_half[0]
    if (R<border_threshold_R) and (G<border_threshold_G) and (B<border_threshold_B):
        bottom_border_height+=1
    else:
        break

if (top_border_height>1) and (bottom_border_height>1):

    corrected_top_border_height = 0
    corrected_bottom_border_height =0

    for i in xrange(top_border_height):
        if iterate_line(img, border_threshold_R, border_threshold_G, border_threshold_B, i):
            corrected_top_border_height+=1
        else:
            break

    for i in xrange(height-1, height-1-bottom_border_height, -1):
        if iterate_line(img, border_threshold_R, border_threshold_G, border_threshold_B, i):
            corrected_bottom_border_height+=1
        else:
            break

    if corrected_bottom_border_height>1 and corrected_top_border_height>1:
        print "The frame has borders."
    else:
        print "The frame has no borders."

else:
    print "The frame has no borders."


print top_border_height, bottom_border_height
print corrected_top_border_height, corrected_bottom_border_height

      

+1


source







All Articles