def adjust_bitmap_n_word_data_margins(bitmap, word_data, left_x, upper_y, right_x, lower_y, fill=(0, 0, 0, 0)): # Image initial_size = bitmap.size margins = [left_x, upper_y, right_x, lower_y] expansion_border_width = max(margins) # expand image equally in all directions by the largest margin, preparing it for cropping bitmap = ImageOps.expand( bitmap, border= expansion_border_width, # expand it with a border with the width of the largest margin fill=fill ) # the fill of the border is transparent, i've used transparent black throughout the script expanded_size = bitmap.size # I won't take the time to explain the arithmetic below crop_coordinate_box = ( expansion_border_width - left_x, # upperLeftCorner_x expansion_border_width - upper_y, # upperLeftCorner_y expanded_size[0] - (expansion_border_width - right_x), # lowerRightCorner_x expanded_size[1] - (expansion_border_width - lower_y) ) # lowerRightCorner_y # crop the image with it's own class method bitmap = bitmap.crop(box=crop_coordinate_box) bounding_boxes = word_data_2_bounding_boxes(word_data) # Bounding Boxes bounding_boxes = map( lambda bbox: (vector_add(bbox[0], (left_x, 0)), vector_add(bbox[1], (left_x, 0))), bounding_boxes) bounding_boxes = list( map( lambda bbox: (vector_add(bbox[0], (0, upper_y)), vector_add(bbox[1], (0, upper_y))), bounding_boxes)) integrate_bounding_boxes_into_word_data(bounding_boxes, word_data) return bitmap, word_data
def add_arc_noise(image, n, fill=None, max_width=5, min_length=4, max_length=60): w, h = image.size noise = PIL.Image.new('RGBA', (w, h), color=(0, 0, 0, 0)) draw = ImageDraw.Draw(noise) for i in range(n): origin_point = random_point_on_image((w, h)) endpoint = vector_add(random_vector__pixels(min_length, max_length), origin_point) bbox = (origin_point, endpoint) if fill == None: draw.arc(bbox, start=randang(), end=randang(), fill=(randcol(), randcol(), randcol(), randcol())) else: draw.arc(bbox, start=randang(), end=randang(), fill=fill) image = PIL.Image.alpha_composite(image, noise) return image
def string_2_word_sample(string, max_rotation_angle=5, margin__left_x=np.random.randint(-4, 30), margin__upper_y=np.random.randint(-4, 30), margin__right_x=np.random.randint(-4, 30), margin__lower_y=np.random.randint(-4, 30), open_close__factor=random.randint(-3, 1), dilate_erode__factor=random.randint(-1, 1), white_bg=False): fnt = random_font() max_char_w, max_char_h = fnt['max_char_size'] font = fnt['font'] img_w, img_h = 0, max_char_h # we start with full height and zero width, the width is incremented letter by letter # Aquiring position data (letter center points and initial bounding boxes) for the string at hand letter_data = [] for letter in string: data_dict = {} random_offset = np.random.randint(-6, 10) img_w += random_offset letter_w, letter_h = font.getsize(letter) letter_bbox = ( (img_w, 0), (img_w + letter_w, letter_h) ) # this should include some randomization in the future letter_center = (img_w + letter_w / 2, img_h / 2) data_dict['char'] = letter data_dict['bounding_box'] = letter_bbox data_dict['center_point'] = letter_center letter_data.append(data_dict) img_w += letter_w # Creating a canvas to write the letters to img = Image.new( mode='RGBA', size=(img_w, img_h), color=(0, 0, 0, 0) # transparent bg ) img_draw = ImageDraw.Draw(img) # writing the characters to canvas for char_data in letter_data: img_draw.text( char_data['bounding_box'][0], char_data['char'], font=font, fill=(0, 0, 0, 255) # black ) rotation_angle = np.random.random() * max_rotation_angle * [ -1, 1 ][np.random.randint(2)] og_image_centerpoint = (img_w / 2, img_h / 2) # rotate the image img = img.rotate( angle=rotation_angle, center=og_image_centerpoint, expand=True, resample=PIL.Image. BICUBIC # I looked at nearest, bilinear and bicubic and bicubic looks accurate and sharp, i like it best ) img_w__new, img_h__new = img.size new_centerpoint = (img_w__new / 2, img_h__new / 2) # after rotation centerpoints_delta_vector = vector_subtract(new_centerpoint, og_image_centerpoint) # rotating the character centerpoints (positions) rotated_centerpoints = map( lambda char_data: rotate_point(char_data['center_point'], og_image_centerpoint, rotation_angle), letter_data) # aligning the character centerpoints (positions) to the rotated and expanded image rotated_centerpoints = map( lambda point: vector_add(point, centerpoints_delta_vector), rotated_centerpoints) # offsettng the character centerpoints (positions) randomly to break uniformity rotated_centerpoints = map( lambda point: vector_add(point, random_vector__pixels(0, 2)), rotated_centerpoints) # create new bounding boxes from the new letter centers bounding_boxes = list( map( lambda p: point_2_squareBoundingBox( p, (max_char_h / 2) + np.random.randint(-2, 2) ), # square radius is half the max char height plus some random padding (postive or negative) rotated_centerpoints)) # Morphology adjustment (open, close, dilate and erode) img = morphology_ops(img, open_close__factor=open_close__factor, dilate_erode__factor=dilate_erode__factor) # Margins Adjustment img, bounding_boxes = adjust_bitmap_n_bounding_box_margins(img, bounding_boxes, margin__left_x, margin__upper_y, margin__right_x, margin__lower_y, fill=(0, 0, 0, 0)) img = add_line_noise(img, np.random.randint(20, 60), max_width=3, min_length=2, max_length=60) img = add_line_noise(img, np.random.randint(2, 20), max_width=6, min_length=2, max_length=500, fill=(0, 0, 0, randcol())) # img = add_arc_noise(img, np.random.randint(60, 120), max_width=3, min_length=2, max_length=60) # img = add_arc_noise(img, np.random.randint(2, 60), max_width=6, min_length=2, max_length=500, fill=(0, 0, 0, randcol())) if white_bg: img = add_background(img, fill=(255, 255, 255, 255)) else: bg_noise = noisey_background(bitmap_size=img.size, steps=12, noise_saturation=random.uniform( 0.05, 0.93), noise_brightness=random.uniform(0.7, 1.8), noise_contrast=random.uniform(0.01, 0.6)) grain = noiseyRGBBitmap(bitmap_size=img.size, noise_saturation=random.uniform(0.05, 0.93), noise_brightness=random.uniform(0.7, 1.8), noise_contrast=random.uniform(0.01, 0.6), alpha_value=255, noise_size=1) img = PIL.Image.alpha_composite(bg_noise, img) img = PIL.Image.blend(img, grain, np.random.uniform(0.1, 0.8)) image = img.convert('RGB') # remove the alpha channel # Blur image = image.filter(ImageFilter.GaussianBlur(radius=np.random.randint(3))) # Contrast contrast = ImageEnhance.Contrast(image) image = contrast.enhance(random.uniform(0.2, 1)) # Brightness brightness = ImageEnhance.Brightness(image) image = brightness.enhance(random.uniform(0.2, 1)) # Saturation saturation = ImageEnhance.Color(image) image = saturation.enhance(random.uniform(0, 1)) angle = rotation_angle return image, bounding_boxes, angle