def __init__(self, target_image: Image, figures=None, score=None): self.relevant_image = False self.__img = None self.target_image = target_image with MeasureTime('creating one gen', level='debug'): if figures: self.figures = figures else: self.figures = [] for i in range(INDIVIDUAL_SIZE): x1 = (i % (SIZE[0] // DNA_IMAGE_SIZE[0])) * DNA_IMAGE_SIZE[0] y1 = (i // (SIZE[1] // DNA_IMAGE_SIZE[1])) * DNA_IMAGE_SIZE[1] x2 = x1 + DNA_IMAGE_SIZE[0] y2 = y1 + DNA_IMAGE_SIZE[1] fragmet_to_cmp = np.array( self.target_image.crop((x1, y1, x2, y2))) fig = Figure((x1, y1), img=None, fragmet_to_cmp=fragmet_to_cmp) self.figures.append(fig) self.score = score or sum(map(lambda f: f.score, self.figures))
def mutation(self, chance: float, share: float, mutate_worst: bool): """ Mutate population """ with MeasureTime('mutation'): to_mutate = self.population args_list = [(ind, chance, int(INDIVIDUAL_SIZE * share), mutate_worst) for ind in to_mutate] parallelize_task(Individual.mutate, args_list)
def __init__(self, target_image): self._id = urandom(4).hex().zfill(8) self.target_image = target_image self.size = POPULATION_SIZE logging.warning(f"Starting genetic alg with id {self._id}") with MeasureTime('creating population'): args_list = [(target_image,) for _ in range(self.size)] self.population = \ parallelize_task(Individual, args_list)
def _get_full_image(self) -> Image.Image: """ Return composed image of individual first images on foreground, last on background """ with MeasureTime('compose image', level='debug'): canvas = get_blank_image(SIZE) for figure in reversed(self.figures): canvas.alpha_composite(figure.img, figure.coordinate) return canvas
def crossover(self) -> List[Individual]: """ Crossover the population """ assert self.size % 2 == 0 with MeasureTime('crossover'): new_individuals = [ self.population[i].cross(self.population[self.size - i - 1]) for i in range(self.size // 2) ] return new_individuals
def run(self, show_img=True): """ Run genetic algorithm """ try: prev_score = INDIVIDUAL_SIZE mutation_chance = MUTATION_CHACE portion_of_mutation = PORTION_OF_MUTATION mutate_worst = True for iterNumber in range(ITERS): with MeasureTime('one iter', level='info'): self.evaluate() self.population.sort() if iterNumber % 100 == 0: best = self.best worse = self.worse title = f"#{iterNumber}. best is {best.score}, worse is {worse.score}" logging.warning(title) if show_img: show_image(best.img, title) directory = join(OUTPUT_DIR, self._id) create_dir(directory) image_name = f"{str(iterNumber // 100).zfill(4)}.png" best.img.save(join(directory, image_name)) delta = best.score - prev_score prev_score = best.score if delta > 1024 * 0.001: # switch to random mode mutate_worst = False mutation_chance = 1 / 30 portion_of_mutation = 5 / 1024 logging.warning(f"Delta is {delta}. New chance: {mutation_chance}, share: {portion_of_mutation}, mutate_worst={mutate_worst}") new_population = self.crossover() self.select() self.population.extend(new_population) self.size = len(self.population) assert self.size == POPULATION_SIZE self.mutation(mutation_chance, portion_of_mutation, mutate_worst) self.best.save(OUTPUT_DIR) except KeyboardInterrupt as e: self.best.save(OUTPUT_DIR) raise e
def select(self): """ Remove half of the population Assume that self.population is sorted """ save_index = self.get_save_index() with MeasureTime('select'): while self.size > POPULATION_SIZE//2: index_to_remove = randint(save_index + 1, self.size - 1) individual = self.population.pop(index_to_remove) del individual self.size -= 1
def mutate(self, chance, N, mutate_worst=True): """ Inplace mutation of individual """ if not check_chance(chance): return self.relevant_image = False with MeasureTime(f'mutation of {N} DNA', level='debug'): if mutate_worst: to_mutate = sorted(self.figures, reverse=True)[:N] else: to_mutate = np.random.permutation(self.figures)[:N] for dna in to_mutate: self.score -= dna.score dna.mutate() self.score += dna.score