def expand_with_white(image: Image, where, size: int) -> Image: verification.verify_is_image_or_exception(image) fill = white_color_by_mode(image.mode) if where == 'to_left': result = Image.new(image.mode, (image.width + size, image.height)) draw = ImageDraw.Draw(im=result, mode=result.mode) draw.rectangle(xy=[(0, 0), (size + 1, result.height + 1)], fill=fill) result.paste(im=image, box=(size + 1, 0)) elif where == 'to_right': result = Image.new(image.mode, (image.width + size, image.height)) draw = ImageDraw.Draw(im=result, mode=result.mode) draw.rectangle(xy=[(result.width - 1 - size, 0), (result.width + 1, result.height + 1)], fill=fill) result.paste(im=image, box=(0, 0)) elif where == 'to_top': result = Image.new(image.mode, (image.width, image.height + size)) draw = ImageDraw.Draw(im=result, mode=result.mode) draw.rectangle(xy=[(0, 0), (result.width + 1, size + 1)], fill=fill) result.paste(im=image, box=(0, size + 1)) elif where == 'to_bottom': result = Image.new(image.mode, (image.width, image.height + size)) draw = ImageDraw.Draw(im=result, mode=result.mode) draw.rectangle(xy=[(0, result.height - 1 - size), (result.width + 1, result.height + 1)], fill=fill) result.paste(im=image, box=(0, 0)) else: raise ValueError(f'Where argument {where} is unsupported') return result
def symbol_segments(image: Image, diff_threshold: float): verify_is_image_or_exception(image) v_levels, v_projections = inverted_vertical_projection(image) max_v_projection = max(v_projections) min_v_projection = min(v_projections) diff = (max_v_projection - min_v_projection) * diff_threshold start = 0 prev_v_projection = 0 result = [] for i in range(len(v_projections) - 1): if prev_v_projection - diff <= 0 < v_projections[i] - diff: start = i elif prev_v_projection - diff > 0 >= v_projections[i] - diff: result.append((start, i)) start = -1 prev_v_projection = v_projections[i] if start != -1: result.append((start, len(v_projections) - 1)) return result
def roberts_cross(image): verify_is_image_or_exception(image) result = Image.new(image.mode, (image.width, image.height)) gradient_max = 0 gradient_matrix = list() for x in range(result.width - 1): gradient_matrix.append(list()) for y in range(result.height - 1): gradient_x = roberts_cross_gradient_x(image, x, y) gradient_y = roberts_cross_gradient_y(image, x, y) gradient = roberts_cross_gradient(gradient_x, gradient_y) if gradient > gradient_max: gradient_max = gradient gradient_matrix[x].append(gradient) for x in range(result.width - 1): for y in range(result.height - 1): result.putpixel( (x, y), int(round(gradient_matrix[x][y] * 255 / gradient_max))) return result
def balansed_histogram_method(image): verify_is_image_or_exception(image) histogram = image.histogram() result = Image.new(THRESHOLDING_MODE, image.size) extreme_left = 0 extreme_right = len(histogram) - 1 histogram_center = histogram_weight_center(histogram, extreme_left, extreme_right) while extreme_left < extreme_right: left_part_weight = sum(histogram[extreme_left:histogram_center]) right_part_weight = sum(histogram[histogram_center:extreme_right]) if left_part_weight > right_part_weight: extreme_left += 1 else: extreme_right -= 1 histogram_center = histogram_weight_center(histogram, extreme_left, extreme_right) threshold = histogram_center for x in range(result.width): for y in range(result.height): result.putpixel((x, y), image.getpixel((x, y)) >= threshold) return result
def draw_projections(image: Image) -> Image: verification.verify_is_image_or_exception(image) x_expand_size = 30 y_expand_size = 30 result = expand_with_white(image=expand_with_white(image=image, where='to_left', size=x_expand_size), where='to_bottom', size=y_expand_size).convert('RGB') result_draw = ImageDraw.Draw(im=result) h_color = (44, 176, 62) h_levels, h_projections = horizontal_projection(image) max_h_projection = max(h_projections) for y in range(image.height): x_projection_coord = int( round(lerp(0, 0, max_h_projection, 30, h_projections[y]))) + 1 result_draw.rectangle(xy=[(0, y), (x_projection_coord, y + 1)], fill=h_color) v_color = (166, 29, 5) v_levels, v_projections = inverted_vertical_projection(image) max_v_projection = max(v_projections) for x in range(image.width): y_projection_coord = result.height - int( round(lerp(0, 0, max_v_projection, 30, v_projections[x]))) result_draw.rectangle(xy=[(x + x_expand_size, y_projection_coord), (x + x_expand_size + 1, result.height + 1)], fill=v_color) return result
def black_weight(image): verify_is_image_or_exception(image) result = 0 for x in range(image.width): for y in range(image.height): result += image.getpixel((x, y)) return result
def central_vertical_axial_moment(image): verify_is_image_or_exception(image) center = gravity_center(image) result = 0 for x in range(image.width): for y in range(image.height): result += (x - center[0])**2 * image.getpixel((x, y)) return result
def mean_grayscale(image): verify_is_image_or_exception(image) result = Image.new(GRAYSCALE_MODE, (image.width, image.height)) for x in range(result.width): for y in range(result.height): result.putpixel((x, y), pixel_mean_value_grayscale(image.getpixel((x, y)))) return result
def gravity_center(image): verify_is_image_or_exception(image) x_coord = 0 y_coord = 0 for x in range(image.width): for y in range(image.height): pixel = image.getpixel((x, y)) x_coord += x * pixel y_coord += y * pixel weight = black_weight(image) return int(round(x_coord / weight)), int(round(y_coord / weight))
def simple_threshold(image, threshold): verify_is_image_or_exception(image) verify_is_natural_or_exception(threshold) if threshold > 255: raise ValueError(f'Parameter threshold {threshold} > 255') result = Image.new(THRESHOLDING_MODE, image.size) for x in range(image.width): for y in range(image.height): result.putpixel((x, y), image.getpixel((x, y)) > threshold) return result
def spatial_smoothing_difference(image): verify_is_image_or_exception(image) spatial_smoothed = spatial_smoothing(image) result = Image.new(image.mode, (image.width, image.height)) for x in range(result.width): for y in range(result.height): result.putpixel((x, y), abs( image.getpixel((x, y)) - spatial_smoothed.getpixel((x, y)))) return result
def horizontal_projection(image): verify_is_image_or_exception(image) levels = [] projections = [] for y in range(image.height): y_projection = 0 for x in range(image.width): y_projection += image.getpixel((x, y)) levels.append(y) projections.append(y_projection) return levels, projections
def spatial_smoothing(image): verify_is_image_or_exception(image) result = Image.new(image.mode, (image.width, image.height)) for x in range(1, result.width - 1): for y in range(1, result.height - 1): window = list() for i in range(3): for j in range(3): window.append(image.getpixel((x + i - 1, y + j - 1))) result.putpixel((x, y), int(round(mean(window)))) return result
def inverted_vertical_projection(image): verify_is_image_or_exception(image) levels = [] projections = [] for x in range(image.width): x_projection = 0 for y in range(image.height): x_projection += white_color_by_mode(image.mode) - image.getpixel( (x, y)) levels.append(x) projections.append(x_projection) return levels, projections
def one_pass_resampling(image, upsample_factor, downsample_factor): verification.verify_is_image_or_exception(image) verification.verify_is_natural_or_exception(upsample_factor) verification.verify_is_natural_or_exception(downsample_factor) if upsample_factor == downsample_factor: return image.copy() result_width = image.width * upsample_factor // downsample_factor result_height = image.height * upsample_factor // downsample_factor result = Image.new(image.mode, (result_width, result_height)) for x in range(result_width): for y in range(result_height): result.putpixel((x, y), image.getpixel((x * downsample_factor // upsample_factor, y * downsample_factor / upsample_factor))) return result
def closest_neighbor_upsampling(image, factor): verification.verify_is_image_or_exception(image) if factor == 1: return image.copy() verification.verify_is_natural_or_exception(factor) result_width = image.width * factor result_height = image.height * factor result = Image.new(image.mode, (result_width, result_height)) for x in range(result_width): x_nearest = int(x * image.width / result_width) for y in range(result_height): y_nearest = int(y * image.height / result_height) result.putpixel((x, y), image.getpixel((x_nearest, y_nearest))) return result
def draw_symbol_segments(image: Image, diff_threshold: float) -> Image: verification.verify_is_image_or_exception(image) start_color = (66, 218, 245) stop_color = (204, 87, 247) segments = symbol_segments(image, diff_threshold) result = image.copy().convert('RGB') result_draw = ImageDraw.Draw(im=result) for segment in segments: result_draw.rectangle(xy=[(segment[0], 0), (segment[0], result.height)], fill=start_color) result_draw.rectangle(xy=[(segment[1], 0), (segment[1], result.height)], fill=stop_color) return result
def cut_empty_rows_and_cols(image): verification.verify_is_image_or_exception(image) empty_row_numbers = [] empty_column_numbers = [] for x in range(image.width): is_col_empty = True for y in range(image.height): if image.getpixel((x, y)) < 255: is_col_empty = False break if is_col_empty: empty_column_numbers.append(x) for y in range(image.height): is_row_empty = True for x in range(image.width): if image.getpixel((x, y)) < 255: is_row_empty = False break if is_row_empty: empty_row_numbers.append(y) def last_element_in_a_row(elements, start_element, step): prev_element = start_element for element in elements[::step]: if abs(element - prev_element) > 1: return prev_element + step prev_element = element return prev_element + step left_whitespace_end = last_element_in_a_row(empty_column_numbers, -1, 1) upper_whitespace_end = last_element_in_a_row(empty_row_numbers, -1, 1) right_whitespace_end = last_element_in_a_row(empty_column_numbers, image.width, -1) lower_whitespace_end = last_element_in_a_row(empty_row_numbers, image.height, -1) return image.crop(box=(left_whitespace_end, upper_whitespace_end, right_whitespace_end + 1, lower_whitespace_end + 1))
def power_transformation(image: Image, c: float = 1, f_zero: float = 0, gamma: float = 0.5) -> Image: def calculate_brightness(pixel_brightness: int) -> int: normalized_pixel_brightness = math.lerp(x1=0, f1=0, x2=255, f2=1, x=pixel_brightness) result_brightness = c * pow(normalized_pixel_brightness + f_zero, gamma) return int(round(math.lerp(x1=0, f1=0, x2=1, f2=255, x=result_brightness))) verification.verify_is_image_or_exception(image) assert c >= 0 assert f_zero >= 0 assert gamma >= 0 result = image.copy() for x in range(result.width): for y in range(result.height): brightness = calculate_brightness(result.getpixel((x, y))) result.putpixel((x, y), brightness) return result
def linear_contrasting(image: Image, min_brightness: int = 0, max_brightness: int = 255) -> Image: def calculate_brightness(f, f_min, f_max, g_min, g_max): return round(int((f - f_min) / (f_max - f_min) * (g_max - g_min) + g_min)) verification.verify_is_image_or_exception(image) image_pixels = list(image.getdata()) image_min_brightness = min(image_pixels) image_max_brightness = max(image_pixels) result = image.copy() for x in range(result.width): for y in range(result.height): brightness = calculate_brightness(result.getpixel((x, y)), image_min_brightness, image_max_brightness, min_brightness, max_brightness) result.putpixel((x, y), brightness) return result
def proximity_measure(image_a: Image, image_b: Image) -> float: verify_is_image_or_exception(image_a) verify_is_image_or_exception(image_b) signs = [(normalized_black_weight(image_a), normalized_black_weight(image_b))] normalized_center_a = normalized_gravity_center(image_a) normalized_center_b = normalized_gravity_center(image_b) signs.append((normalized_center_a[0], normalized_center_b[0])) signs.append((normalized_center_a[1], normalized_center_b[1])) signs.append((normalized_central_horizontal_axial_moment(image_a), normalized_central_horizontal_axial_moment(image_b))) signs.append((normalized_central_vertical_axial_moment(image_a), normalized_central_vertical_axial_moment(image_b))) return euclidean_metric(signs)
def bilinear_interpolation_upsampling(image, factor): verification.verify_is_image_or_exception(image) if factor == 1: return image.copy() verification.verify_is_natural_or_exception(factor) result_width = image.width * factor result_height = image.height * factor result = Image.new(image.mode, (result_width, result_height)) for x in range(result_width): for y in range(result_height): if (x % factor == 0) and (y % factor == 0): result.putpixel((x, y), image.getpixel((x // factor, y // factor))) continue prev_row_number = y // factor next_row_number = prev_row_number + 1 prev_col_number = x // factor next_col_number = prev_col_number + 1 if (next_row_number >= image.height) or (next_col_number >= image.width): result.putpixel((x, y), (0, 0, 0)) continue prev_row_prev_col_pixel = image.getpixel((prev_col_number, prev_row_number)) prev_row_next_col_pixel = image.getpixel((next_col_number, prev_row_number)) next_row_prev_col_pixel = image.getpixel((prev_col_number, next_row_number)) next_row_next_col_pixel = image.getpixel((next_col_number, next_row_number)) upper_pseudo_pixel = math.rgb_color_lerp(prev_col_number, prev_row_prev_col_pixel, next_col_number, prev_row_next_col_pixel, x / factor) lower_pseudo_pixel = math.rgb_color_lerp(prev_col_number, next_row_prev_col_pixel, next_col_number, next_row_next_col_pixel, x / factor) result.putpixel((x, y), math.rgb_color_lerp(prev_row_number, upper_pseudo_pixel, next_row_number, lower_pseudo_pixel, y / factor)) return result
def decimation_downsampling(image, factor): verification.verify_is_image_or_exception(image) if factor == 1: return image.copy() verification.verify_is_natural_or_exception(factor) result_width = image.width // factor result_height = image.height // factor result = Image.new(image.mode, (result_width, result_height)) for x in range(image.width - factor + 1): if x % factor != 0: continue for y in range(image.height - factor + 1): if y % factor != 0: continue result.putpixel((x // factor, y // factor), image.getpixel((x, y))) return result
def draw_series_length_matrix(image: Image) -> Image: verification.verify_is_image_or_exception(image) series_length_matrix = texturing.texturing.series_length_matrix(image) result = Image.new(mode=constants.constants.GRAYSCALE_MODE, size=(len(series_length_matrix[0]), len(series_length_matrix))) max_series_length = max([max(row) for row in series_length_matrix]) for x in range(result.width): for y in range(result.height): result.putpixel( (x, y), int( round( math.math.lerp(x1=0, f1=0, x2=max_series_length, f2=255, x=series_length_matrix[y][x])))) return result
def normalized_black_weight(image): verify_is_image_or_exception(image) return black_weight(image) / (image.width * image.height)
def normalized_gravity_center(image): verify_is_image_or_exception(image) x, y = gravity_center(image) return (x - 1) / (image.width - 1), (y - 1) / (image.height - 1)
def normalized_central_vertical_axial_moment(image): verify_is_image_or_exception(image) return central_vertical_axial_moment(image) / (image.width**2 + image.height**2)