예제 #1
0
    def process_and_save_files(self):
        """
        process files in self.files, according to its width and height
        :return: None
        """
        print('Processing and saving files ...')
        start_time = time.time()
        self.structure.remove_existing_files()
        self.structure.make_folders()
        self.images = []

        total = len(self.files)
        files_chunks = []
        start = 0
        while start < total - 1:
            end = start + settings.MAX_CACHE_PROCESSED_IMAGES
            files_chunks.append(self.files[start:end])
            start = end

        total_chunks = len(files_chunks)
        for chunk_index, chunk in enumerate(files_chunks):
            processed_images = []
            total_files = len(chunk)
            for file_index, file in enumerate(chunk):
                database_item = DatabaseImageItem(file)
                processed_images.append(database_item)
                self.images.append(
                    ImageItem(database_item, self.width, self.height))
                utilities.print_progress(file_index + 1,
                                         total_files,
                                         curr_chunk=chunk_index + 1,
                                         total_chunks=total_chunks)
            utilities.save(processed_images,
                           self.structure.get_list_name(chunk_index))
        utilities.print_done(time.time() - start_time)
예제 #2
0
    def load(folder, width, height):

        print('Loading database from {}'.format(folder))

        database_structure = utilities.get_database_structure(folder)

        database = ImageDatabase(width, height, folder)

        start_time = time.time()
        chunks = database_structure.get_list_names()
        total = len(chunks)

        print('Loading {} chunks from {}'.format(
            total, database_structure.image_folder))

        database.images = []
        for index, chunk in enumerate(chunks):
            database.images += [
                ImageItem(image, database.width, database.height)
                for image in utilities.load(chunk)
            ]
            utilities.print_progress(index + 1, total)
            if settings.MAX_CHUNKS_USE and settings.MAX_CHUNKS_USE == index + 1:
                print('Reached limit for max chunks use')
                break
        utilities.print_done(time.time() - start_time)

        return database
예제 #3
0
    def create_database(self):

        print('Creating database | {} | '.format(self.database_path), end='')

        # remove existing database if any
        if os.path.isdir(self.database_path):
            shutil.rmtree(self.database_path)

        os.mkdir(self.database_path)

        pool = Pool()
        file_size = len(self.files)
        database_item_generator = functools.partial(
            DatabaseItem,
            width=settings.DATABASE_IMAGE_WIDTH,
            height=settings.DATABASE_IMAGE_HEIGHT)
        print('total {} '.format(file_size))

        data_paths = []
        cache_images = []
        cache_images_size = 0

        print(' >>> waiting for arrival of chunks ...', end='')

        for index, items in enumerate(
                pool.imap_unordered(database_item_generator, self.files)):
            cache_images.append(items)  # database item
            cache_images_size += 1

            if (cache_images_size >=
                    settings.DATABASE_CHUNK_SIZE) or index + 1 == file_size:
                data_path = self.get_save_image_path()
                data_paths.append(data_path)
                utilities.save(cache_images, data_path)
                cache_images = []
                cache_images_size = 0

            utilities.print_progress(index + 1, file_size)

        database_meta = {
            'number of files': file_size,
            'chunk size': settings.DATABASE_CHUNK_SIZE,
            'files': self.files,
            'paths': data_paths,
            'image width': settings.DATABASE_IMAGE_WIDTH,
            'image height': settings.DATABASE_IMAGE_HEIGHT,
            'chunk count': self.save_image_count,
            'file type': settings.DATABASE_FILE_TYPE,
            'database path': self.database_path,
            'meta file path': self.database_meta_file
        }

        # save meta
        utilities.save(database_meta, self.database_meta_file)

        utilities.print_done()
예제 #4
0
def main():
    parser = argparse.ArgumentParser(description='build image from images')
    parser.add_argument('-src', '--source', help='the image to stimulate')
    parser.add_argument('-s',
                        '--size',
                        type=int,
                        help='the size of each pieces')
    parser.add_argument(
        '-f',
        '--folder',
        help='the folder containing images used to stimulate the source')
    parser.add_argument(
        '-d',
        '--dest',
        help='the base name of the output file, not including extension')
    parser.add_argument('-r',
                        '--repeat',
                        action='store_true',
                        help='allow build with repeating images')
    parser.add_argument('-fa',
                        '--factor',
                        type=int,
                        help='result size factor compared to original')
    args = parser.parse_args()

    input_file = args.source
    database_folder = args.folder
    src = Image.open(input_file).convert('RGB')
    source, background = make_from(src,
                                   database_folder,
                                   args.size,
                                   args.factor,
                                   use_repeat=args.repeat)

    print('Blending & saving images ... ')

    folder = args.dest
    if not os.path.isdir(folder):
        os.mkdir(folder)

    background_file = folder + '/background_{}.jpg'
    background.save(
        background_file.format('repeat' if args.repeat else 'no_repeat'))

    output_file = folder + '/{}.jpg'
    blend_range = 10
    for index, blend_percent in enumerate(range(0, blend_range)):
        blend_percent = blend_percent / blend_range
        image = Image.blend(source, background, blend_percent)
        image.save(output_file.format(blend_percent))
        utilities.print_progress(index + 1, blend_range)
    utilities.print_done(folder)
예제 #5
0
def _build_without_repeat(source, background, matcher, chunk_indexes, method):
    _build_ = functools.partial(_match_one,
                                background=background,
                                source=source,
                                matcher=matcher,
                                use_repeat=False,
                                method=method)
    total = len(chunk_indexes)
    for index, chunk_index in enumerate(chunk_indexes):
        _build_(chunk_index)
        utilities.print_progress(index + 1, total)
    utilities.print_done()
    return background
예제 #6
0
def make_from(source, folder, size, factor, use_repeat=True):
    src_width, src_height = source.size
    width = int(src_width * factor)
    height = int(src_height * factor)
    source = source.resize((width, height))

    pieces_required = math.ceil(width / size) * math.ceil(height / size)

    print('=' * 50)
    print('factor:', factor)
    print('result dimension:', width, height)
    print('total pieces:', pieces_required)
    print('repeat:', use_repeat)
    print('algorithm:', settings.COLOR_DIFF_METHOD)
    print('=' * 50)

    if size < 1 or size < 1:
        raise ValueError(
            'width or height for each small piece of images is less than 1px: {} {} < {}'
            .format(width, height, size))

    database = _load_database(folder, size, use_repeat, pieces_required)
    database.process_images()

    print('=' * 50)
    print('Database size:', database.size)
    print('=' * 50)

    chunk_count = 0
    background = Image.new(source.mode, source.size, 'black')
    print('building image from database ...')
    start_time = time.time()
    for h in range(0, height, size):
        for w in range(0, width, size):
            btmx = w + size
            btmy = h + size
            if btmx > width:
                btmx = width
            if btmy > height:
                btmy = height
            curr_chunk = source.crop((w, h, btmx, btmy))
            best_match = database.find_closest(
                curr_chunk, use_repeat, method=settings.COLOR_DIFF_METHOD)
            background.paste(best_match, (w, h))
            chunk_count += 1
            utilities.print_progress(chunk_count, pieces_required)
    utilities.print_done(time.time() - start_time)

    return source, background
예제 #7
0
    def update_existing_database(self):
        print('Updating existing database')
        meta_dict = utilities.load(self.database_meta_file)

        files_to_add = [
            file for file in self.files if file not in meta_dict['files']
        ]
        total_files = len(files_to_add)
        if total_files == 0:
            print('There is no new images found ... [ done ]')
            return
        database_width = meta_dict['image width']
        database_height = meta_dict['image height']
        max_chunk_size = meta_dict['chunk size']
        chunk_count = meta_dict['chunk count']
        file_type = meta_dict['file type']
        database_path = meta_dict['database path']
        cache_images = []
        num_of_cache_images = 0
        data_paths = []

        for index, file in enumerate(files_to_add):
            cache_images.append(
                DatabaseItem(file, database_width, database_height))
            num_of_cache_images += 1
            if num_of_cache_images >= max_chunk_size:
                chunk_count += 1
                data_path = database_path + os.path.sep + chunk_count + file_type
                utilities.save(cache_images, data_path)
                data_paths.append(data_path)
                cache_images = []
                num_of_cache_images = 0
            utilities.print_progress(index + 1, total_files)
        utilities.print_done()

        # save remaining cached images
        if cache_images:  # if not empty
            chunk_count += 1
            data_path = database_path + os.path.sep + chunk_count + file_type
            utilities.save(cache_images, data_path)
            data_paths.append(data_path)

        meta_dict['number of files'] += len(files_to_add)
        meta_dict['files'] += files_to_add
        meta_dict['paths'] += data_paths
        meta_dict['chunk count'] += chunk_count

        utilities.save(meta_dict, meta_dict['meta file path'])
예제 #8
0
def _build(source, matcher, step, use_repeat, method):
    source_width, source_height = source.size

    total_chunks = math.ceil(source_width / step) * math.ceil(
        source_height / step)

    if matcher.size < total_chunks and not use_repeat:
        raise ValueError(
            'Database does not have enough images: {} < {}'.format(
                matcher.size, total_chunks))

    print('')  # new line
    print('Gathering chunks | {}'.format(total_chunks))

    curr_chunk_num = 0
    chunk_indexes = []
    for upper in range(0, source_height, step):
        for left in range(0, source_width, step):
            right = left + step
            lower = upper + step
            if right > source_width:
                right = source_width
            if lower > source_height:
                lower = source_height
            chunk_indexes.append((left, upper, right, lower))
            # curr_chunk = source.crop((left, upper, right, lower))
            # chunks.append(curr_chunk)
            # best_match = matcher.find_closest(curr_chunk, method=method)
            # if not use_repeat:
            #     matcher.remove(best_match)
            # background.paste(best_match.image, (left, upper))
            curr_chunk_num += 1
            utilities.print_progress(curr_chunk_num, total_chunks)
    utilities.print_done()

    background = Image.new(source.mode, source.size, 'black')

    print('building image | {} | {} x {} | [{}] -> {} '.format(
        method, background.width, background.height, matcher.size,
        total_chunks))
    if use_repeat:
        return _build_with_repeat(source, background, matcher, chunk_indexes,
                                  method)
    else:
        return _build_without_repeat(source, background, matcher,
                                     chunk_indexes, method)
예제 #9
0
    def generate_color_space(self):

        if not self.rgb_image_dict:
            raise ValueError('Please call process_images first')

        total = len(self.rgb_image_dict)
        print('Generating color space | {}'.format(total))
        start_time = time.time()
        color_space = [[[[] for _ in range(256)] for _ in range(256)]
                       for _ in range(256)]  # 256 x 256 x 256 arrays
        for index, item in enumerate(self.rgb_image_dict.items()):
            r, g, b = item[0]
            color_space[r][g][b].append(item[1])
            utilities.print_progress(index + 1, total)
        self.images = None
        utilities.print_done(time.time() - start_time)
        start_time = time.time()
        print('cleaning color space ...', end='')
        self.color_space = utilities.remove_empty(color_space)
        print(' [ done ] => {0:.2f}s'.format(time.time() - start_time))
예제 #10
0
def _build_with_repeat(source, background, matcher, chunk_indexes, method):
    _build_ = functools.partial(_match_one,
                                background=background,
                                source=source,
                                matcher=matcher,
                                use_repeat=True,
                                method=method)
    # This is actually slower
    # pool = Pool()
    # total = len(chunk_indexes)
    # for index, _ in enumerate(pool.imap_unordered(_build_, chunk_indexes, chunksize=1)):
    #     utilities.print_progress(index + 1, total)
    # utilities.print_done()
    # return background

    total = len(chunk_indexes)
    for index, chunk_index in enumerate(chunk_indexes):
        _build_(chunk_index)
        utilities.print_progress(index + 1, total)
    utilities.print_done()
    return background
예제 #11
0
    def check_and_load_database(self):

        # do some checking first
        if not os.path.isdir(self.database_path):
            raise ValueError('Database does not exists')

        if not os.path.isfile(self.database_meta_file):
            raise ValueError('Corrupted database: missing meta file')

        meta_dict = utilities.load(self.database_meta_file)
        database_chunk_size = meta_dict['chunk size']
        if database_chunk_size != settings.DATABASE_CHUNK_SIZE:
            raise ValueError(
                'Existing database has different chunk size signature | database {} | program {}'
                .format(database_chunk_size, settings.DATABASE_CHUNK_SIZE))

        if settings.DATABASE_IMAGE_WIDTH != meta_dict['image width']:
            raise ValueError(
                'Existing database has different image width signature | database {} | program {}'
                .format(settings.DATABASE_IMAGE_WIDTH,
                        meta_dict['image width']))

        if settings.DATABASE_IMAGE_HEIGHT != meta_dict['image height']:
            raise ValueError(
                'Existing database has different image height signature | database {} | program {}'
                .format(settings.DATABASE_IMAGE_HEIGHT,
                        meta_dict['image height']))

        # now process the database
        num_of_database_images = meta_dict['number of files']
        num_of_files_found = len(self.files)

        if num_of_database_images != num_of_files_found:

            if num_of_database_images == 0:
                self.create_and_load_database()
                return

            while True:
                print('Files found: {} | Database size: {}'.format(
                    num_of_files_found, num_of_database_images))
                print('[1] use existing database')
                print('[2] create new database')
                print('[3] update existing database')
                res = input('Please enter a number:')
                if res == '1':
                    break
                elif res == '2':
                    self.create_and_load_database()
                    return
                elif res == '3':
                    self.update_existing_database()
                    meta_dict = utilities.load(
                        self.database_meta_file)  # update for below use
                    break
                print('Please provide your answer as \'y\' or \'n\'')

        images_list_paths = meta_dict['paths']
        images_list_size = len(images_list_paths)

        print('Loading database | {} | chunks {} | size per chunk {}'.format(
            self.database_path, images_list_size, meta_dict['chunk size']))

        self.loaded_image_items = []

        for index, images_path in enumerate(images_list_paths):
            try:
                database_items = utilities.load(images_path)
            except AttributeError as e:
                raise ValueError(
                    'Database Object has different signature, did you change your code structure? | {}'
                    .format(e))
            self.loaded_image_items += [
                ImageItem(item, self.width, self.height)
                for item in database_items
            ]
            utilities.print_progress(index + 1, images_list_size)
        utilities.print_done()