Code:
#!/usr/bin/env python
from __future__ import division
import sys
import os
import numpy
from PIL import Image
import datetime
def detect_white(image):
"""This function takes a PIL image and returns a numpy array of booleans"""
# Save a b&w image for testing
image.convert('1', dither=0).save('black_white.png')
# Make a color matrix of the image
image_array = image.convert('1', dither=0).load()
pixel_array = []
# Array of pixels
for index in range(image.size[1]):
pixel_array.append([])
for i in range(image.size[0]):
# If color is greater than 250, then it's white (True)
pixel_array[index].append(image_array[i, index] >= 250)
# Convert list of lists into a numpy array
pixel_array = numpy.array(pixel_array)
# Return size and array as a tuple
return pixel_array
def detect_stripes(area_array, threshold):
"""This function detects horizontal (=) stripes in an area of an image"""
# Get width and height (shape of matrix)
height, width = area_array.shape
# Calculate number of pixels allowed to be False for it to still be a stripe
false_pixels = int(height * threshold)
# Fill a list with all pixels
stripes = range(height)
# Loop through each row in the image, checking whether the pixels are True
for line_index, line in enumerate(area_array):
# Start with zero false pixels
num_false_pixels = 0
for pixel in line:
# If the number of allowed false pixels is reached, go to the next line
if pixel == False:
if num_false_pixels >= false_pixels:
stripes.remove(line_index)
break
else:
num_false_pixels += 1
stripes.append(0) # Assume that there is a stripe at the start and end of the image
stripes.append(height)
return stripes
def clean_stripes(stripes):
"""This function consolidates stripes that are close together"""
clean_stripes = []
i = 0
# Go through the stripes
while i < len(stripes):
try:
total = stripes[i]
number_of_lines = 1
while stripes[i] + 1 == stripes[i + 1]:
i += 1
number_of_lines += 1
total += stripes[i]
except IndexError: # If the value is the last in the list
pass
try:
clean_stripes.append(int(total/number_of_lines))
except ZeroDivisionError:
clean_stripes.append(stripes[i])
i += 1
# Remove duplicates
clean_stripes = list(set(clean_stripes))
clean_stripes.sort()
return clean_stripes
def crop_panels(image):
"""This function returns a tree of panels"""
# Orientation - 0 is horizontal (=), 1 is vertical (||), swap every loop
pixel_array = detect_white(image)
# Get width and height (shape of matrix)
height, width = pixel_array.shape
crop_array = [] # [(top_x, top_y, bottom_x, bottom_y), [(top_x, top_y, bottom_x, bottom_y)]]
# Scan horizontally
stripes = clean_stripes(detect_stripes(pixel_array, HORIZONTAL_THRESHOLD))
# Convert to crop boxes and append
for stripe_index, stripe in enumerate(stripes):
try:
crop_array.append((0, stripe, width, stripes[stripe_index + 1]))
except IndexError: # Last stripe
pass
# Scan vertically
for box_index, box in enumerate(crop_array):
box_array = []
stripes = clean_stripes(detect_stripes(pixel_array[box[1]:box[3],box[0]:box[2]].T, VERTICAL_THRESHOLD))
for stripe_index, stripe in enumerate(stripes):
try:
box_array.append((stripe, crop_array[box_index][1], stripes[stripe_index + 1], crop_array[box_index][3]))
except IndexError: # Last stripe
pass
# Replace coordinates with new panels if found
if len(box_array) > 0:
crop_array[box_index] = box_array
else: # Otherwise use old panels
try:
crop_array[box_index] = [(0, crop_array[box_index], width, crop_array[box_index + 1])]
except IndexError: # If it's the last, use the height
crop_array[box_index] = [(0, crop_array[box_index], width, height)]
return crop_array
def output_panels(image, panel_array):
"""This function takes a panel array and outputs individual images"""
for indexi, i in enumerate(panel_array):
for indexj, j in enumerate(i):
image.crop(j).save('%s_%s-%s.png'%(INPUT_FILE, indexi, indexj))
def init():
"""Run application"""
global INPUT_FILE, HORIZONTAL_THRESHOLD, VERTICAL_THRESHOLD
# Get input file
INPUT_FILE = sys.argv[1]
# Percentage of line to be black but still a stripe
HORIZONTAL_THRESHOLD = 0.01
VERTICAL_THRESHOLD = 0.0
# Get image
image = Image.open(INPUT_FILE)
# Crop panels out and output images
output_panels(image, crop_panels(image))
if __name__ == "__main__":
init()
EDIT: I think I'm supposed to use a