<commit_before>from operator import itemgetter

from PIL import Image


NUMS = 5


def closest_color(color, colors):
    red, green, blue = color
    distances = [(r - red, g - green, b - blue) for r, g, b in colors]
    distances = [sum((r**2, g**2, b**2)) for r, g, b in distances]
    color_index, _ = min((val, index) for val, index in enumerate(distances))
    return colors[color_index]


def get_top_colors(image, amount):
    width, height = image.size
    histogram = image.getcolors(width * height)
    top = sorted(histogram, key=itemgetter(0), reverse=True)[:amount]
    return [color for amount, color in top]


def reduce_to_colorset(image, colorset):
    width, height = image.size
    pixels = image.load()
    for x in range(width):
        for y in range(height):
            if not pixels[x, y] in colorset:
                pixels[x, y] = closest_color(pixels[x, y], colorset)


def crop_to_colors(image, colorset):
    yield image


def solve_captcha(filename='captcha.png'):
    captcha = Image.open(filename)
    result = []

    top_colors = get_top_colors(captcha, amount=NUMS+1)
    reduce_to_colorset(captcha, top_colors)

    for letter in crop_to_colors(captcha, top_colors):
        pass

    captcha.save('output.png')
    return result


if __name__ == '__main__':
    solve_captcha()
<commit_msg>[captcha] crop image to all available colors. save for debugging.<commit_after>from collections import namedtuple
from operator import itemgetter

from PIL import Image


NUMS = 5


def closest_color(color, colors):
    red, green, blue = color
    distances = [(r - red, g - green, b - blue) for r, g, b in colors]
    distances = [sum((r**2, g**2, b**2)) for r, g, b in distances]
    color_index, _ = min((val, index) for val, index in enumerate(distances))
    return colors[color_index]


def get_top_colors(image, amount):
    width, height = image.size
    histogram = image.getcolors(width * height)
    top = sorted(histogram, key=itemgetter(0), reverse=True)[:amount]
    return [color for amount, color in top]


def reduce_to_colorset(image, colorset):
    width, height = image.size
    pixels = image.load()
    for x in range(width):
        for y in range(height):
            if not pixels[x, y] in colorset:
                pixels[x, y] = closest_color(pixels[x, y], colorset)


def crop_to_colors(image, colorset, ignore_first=True):
    color_list = colorset[1:] if ignore_first else colorset
    Border = namedtuple('Border', ['xleft', 'xright', 'yupper', 'ylower'])
    borders = {color: {c: 0 for c in ['xleft', 'xright', 'yupper', 'ylower']} for color in colorset}

    width, height = image.size
    pixels = image.load()
    for x in range(width):
        for y in range(height):
            color = pixels[x, y]
            borders[color]['xleft'] = borders[color]['xleft'] or x
            borders[color]['xright'] = x
            borders[color]['yupper'] = min(y, borders[color]['yupper'])
            borders[color]['ylower'] = max(y, borders[color]['ylower'])

    for color in color_list:
        border = borders[color]
        color_image = image.crop((border['xleft'], border['yupper'], border['xright'], border['ylower']))
        color_image.load()
        color_image.save('{}.png'.format(color))
        yield color_image


def solve_captcha(filename='captcha.png'):
    captcha = Image.open(filename)
    result = []

    top_colors = get_top_colors(captcha, amount=NUMS+1)
    reduce_to_colorset(captcha, top_colors)

    for letter in crop_to_colors(captcha, top_colors):
        pass

    captcha.save('output.png')
    return result


if __name__ == '__main__':
    solve_captcha()

{'fraction_non_alphanumeric': 0.12835249042145594, 'fraction_numerical': 0.0038314176245210726, 'mean_word_length': 3.330568720379147, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '300380', 'n_tokens_mistral': 1216, 'n_tokens_neox': 1183, 'n_words': 332}