3

I use this code to delete the border of an image 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)

Which I found here: Trim whitespace using PIL

And I use this to process all images contained in a 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 this operation take a lot of time to be done on ~1000 images, so what I want to do is to check BEFORE starting the process if one of the images in the folder has a border to be removed, this because if image 1.jpg has the border, the image [n].jpg will have it too for sure.

Community
  • 1
  • 1
Hyperion
  • 2,515
  • 11
  • 37
  • 59
  • So basically for a given image you want to detect if it has a border or not ? – ZdaR May 18 '15 at 20:15
  • Yes the image can be selected at the middle of the sequence like for i=i/2 just to be sure you won't take an intro or a black frame at the beginning of the video (images are frames from a video). I was thinking to detect if the first pixel at the top left is black but this would only work for black borders and I think it will give a lot of false positives... – Hyperion May 18 '15 at 20:26
  • Can you attach a sample image to make the situation more clear ? – ZdaR May 18 '15 at 20:27
  • Image with border (need to be trimmed): [image1](http://i.imgur.com/WvyTQno.jpg) and image processed with the algorithm (should be detectes ad "no need to be trimmed): [image2](http://i.imgur.com/icjaskI.jpg) – Hyperion May 18 '15 at 20:49
  • I would take a look at http://stackoverflow.com/questions/10985550/detect-if-an-image-has-a-border-programmatically-return-boolean and see if that can help you out at all. – signus May 18 '15 at 21:13
  • 1
    Might `opencv` also be a good choice for this task? – Paul Rooney May 18 '15 at 21:21
  • Uhm seems perfect, it uses the same algorithm. But seems to have a bug, if I put an image which really don't have a frame, it returns "False" as expected, but if I put an image that the same algorithm has already trimmed (so should be without border) it returns "True", really dunno why. Should work fine for now. – Hyperion May 18 '15 at 21:30
  • 1
    @Hyperion, the point of using that post is that it effectively does what you need, however you might need to make slight adjustments to it. – signus May 18 '15 at 21:52

1 Answers1

2

I haven't worked on PIL much, So I will try to implement the solution in using OPenCV, and if you are satisfied then you can put some effort to rewrite the code using PIL.

Assumptions made:

  • The borders are only present on top and bottom of a given image frame.
  • The borders are dark black in color.

So let us take a sample image:

enter image description here

First of all we load the given image find the length and breadth 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 the pixels parallel to the height and at a distance of (width * 0.5) from the both the sides or you can say centre of the image.

As we know that the border is of dark black color so as per our assumption, therefore for black color (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

Now we have the range in which the given image as dark black color, But we still cannot say if it consists of a border or not. To solve this issue lets randomly iterate in direction parallel to width of the image but at a distance less than top_border_height and bottom_border_height and check if we can successfully iterate over a line with (R, G, B) pixel values less than threshold(<4). For every successful iteration of line we increment a variable which shows the corrected width of the border.

Let us define a function which returns true only when a full row 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 iterating over the assumed border dimensions to accurately find the dimensions of the borders.

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 the given image the respective values are:

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

The full code may look like:

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] < r_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
A.Bau
  • 82
  • 1
  • 10
ZdaR
  • 22,343
  • 7
  • 66
  • 87
  • I am in a bit hurry for my tomorrow exam and it's 4:04 am morning, So there are a bunch of things that I missed/skipped intentionally, I would edit my answer by tomorrow, In the meantime feel free to understand the code and point out any bugs or explanation you require for my any assumption. – ZdaR May 18 '15 at 22:35
  • Wow this is awesome. I've tested it, I expected to get a much longer computational time compared to the other algorithm, but it's still fast! Plus your algorithm detected [this](http://i.imgur.com/icjaskI.jpg) with no borders, which is the case. The problems are only two: it works only for top and bottom borders but videos can have also a frame which cover all the sides, and it works only with black, or at least he needs an input color to be detected as border. The only bug is that if he doesn't detect any border he print error because "corrected_top_border_height" is not defined. – Hyperion May 19 '15 at 07:12
  • That was the only reason why I declared a lot of variables So that you may get an understand and edit the code according to your choice, However, in the given test image the borders were only on bottom and top, So I made assumptions based on those, The algorithm is fast because in any case it is never iterating over whole image instead it iterates only 5~10% of the image. – ZdaR May 19 '15 at 08:43
  • Can you add also the feature of detecting left and right border? – Hyperion May 19 '15 at 12:22