コード例 #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 regenerate_midis(self, generate_txt: bool = False):
        """
        Regenerate MIDI files from internally stored sequences.

        :param generate_txt: Parameter that controls the generation of .txt files that contain music21.stream data.
        :return:
        """
        self.__clear_files(self.__regen_path)

        pf = "Regenerating MIDI files to " + self.__regen_path + ":"
        end = len(self.__sequences)

        for progress, sequence in enumerate(self.__sequences):
            print_progress(progress, end, prefix=pf, suffix=sequence)
            stream_to_file(
                sequence,
                self.__regen_path + "regenerated_" + str(progress) + ".mid")
            if generate_txt:
                file = open(
                    self.__regen_path + "regenerated_" + str(progress) +
                    ".txt", "a")
                for element in sequence[0]:
                    file.write("{" + str(element.offset) + "}" + " - " +
                               str(element) + "\n")
                file.close()
            print_progress(progress + 1, end, prefix=pf, suffix=sequence)
コード例 #3
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
コード例 #4
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()
コード例 #5
0
ファイル: main.py プロジェクト: Redcxx/Pictures-to-Picture
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)
コード例 #6
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
コード例 #7
0
ファイル: main.py プロジェクト: Redcxx/Pictures-to-Picture
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
コード例 #8
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'])
コード例 #9
0
    def __clear_files(self, clear_path):
        """
        Clear physical files in given directory.

        :param clear_path: Describes a directory that has to be purged.
        :return:
        """
        clear_files = listdir(clear_path)

        pf = "Removing files in " + clear_path + ":"
        end = len(clear_files)

        for progress, file in enumerate(clear_files):
            print_progress(progress, end, prefix=pf, suffix=file)
            remove(clear_path + file)
            print_progress(progress + 1, end, prefix=pf, suffix=file)
コード例 #10
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)
コード例 #11
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))
コード例 #12
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
コード例 #13
0
    def prepare_data(self):
        """
        Prepares MIDIs, changes them to music21 streams that are then processed and added onto tensorflow tensors.

        Method starts with calling internal MidiSplitter that divides MIDIs into instrumental parts.
        These parts, in order to be readable by music21 toolkit, have to physically exist on hard drive.
        Split MIDIs contain data on time signatures and tempos. MIDI programs (instruments) that are not
        fully supported by music21 module, therefore are stored as comments in music21.instrument.Instrument() classes
        found outside in a dictionary that is then used to reassign these programs when corresponding streams are
        created. These comments become useful in later parts of input data creation process.

        :return:
        """
        # Input files are split into multiple instrument tracks and saved as separate MIDIs.
        input_files = listdir(self.__input_path)

        pf = "Splitting MIDI files:"
        end = len(input_files)

        for progress, file in enumerate(input_files):
            print_progress(progress, end, prefix=pf, suffix=file)
            self.__splitter.split_midi(file)
            print_progress(progress + 1, end, prefix=pf, suffix=file)

        # Split files are turned into music21 sequences.
        split_files = listdir(self.__split_path)

        pf = "Assigning instruments for parts:"
        end = len(split_files)

        for progress, file in enumerate(split_files):
            print_progress(progress, end, prefix=pf, suffix=file)
            sequence = mu.converter.parse(self.__split_path + file,
                                          quantizePost=True)
            # Processed sequence is now reassigned its instrument that have potentially lost during conversion.
            self.__reassign_program(sequence, file)
            self.__sequences.append(sequence)
            print_progress(progress + 1, end, prefix=pf, suffix=file)
コード例 #14
0
    def prepare_data(self,
                     subsequence_length: int = 16,
                     subsequence_width: int = 6):
        """
        Final training data preparations. Final lists are created that can be converted into tensorflow.Tensor classes.

        Function iterates through loaded sequences, extracts necessary parameters
        and creates measures from these sequences. These measures are then transformed into
        two-dimensional lists of size [subsequence_length + 1, subsequence_width].
        Subsequence_length describes how many notes can be stored in one measure
        (+1 is for tag_line that sits in the beginning of every subsequence and gives more
        information concerning the measure structure).

        :param subsequence_length:
        :param subsequence_width:
        :return:
        """
        # data is reset
        self.__input = []
        self.__target = []

        if subsequence_width >= TAG_LINE_LEN:

            highest_width_loss = 0
            highest_length_loss = 0

            pf = "Splitting parts to subsequences: "
            end = len(self.__sequences)

            for progress, sequence in enumerate(self.__sequences):
                print_progress(progress, end, prefix=pf, suffix=sequence)
                # divide each sequence into measures that are defined by time signature
                measures = sequence[0].makeMeasures()

                # saving tempo (dynamically changing tempos are not supported)
                boundaries = sequence[0].metronomeMarkBoundaries()
                if len(boundaries) == 1:
                    metronome = float(boundaries[0][2].number)
                    uniform = True
                else:
                    metronome = 0.0
                    uniform = False

                # saving initial time signature
                time_signature = sequence[0].timeSignature

                # saving tag line values instrument values
                instrument = sequence[0].getInstrument()
                is_drum = float(
                    instrument.editorial.comments[0].is_drum == "True")
                program = float(instrument.editorial.comments[0].true_program)

                # subsequences are unique to each midi track,
                # these same subsequences will be divided into training and target data
                # (subsequent subsequences)
                subsequences = []

                for measure in measures:
                    # in case if the object is not a measure, it's discarded
                    if type(measure) is not mu.stream.Measure:
                        measures.remove(measure)
                        continue

                    # deleting empty measure and skipping to the next one
                    if measure.notes.highestOffset == 0.0:
                        measures.remove(measure)
                        continue

                    # creating subsequence and measures dictionary for each measure
                    # dictionary is created and cleared but used only if uniform is False
                    subsequence = []
                    metronomes = {}

                    # detecting changes in time signatures
                    if measure.timeSignature is not None:
                        time_signature = measure.timeSignature

                    # tag_line is a list composed of "instruction values"
                    # that are found at the very beginning of every input vector
                    tag_line = [
                        float(measure.offset), program, is_drum,
                        float(time_signature.numerator),
                        float(time_signature.denominator)
                    ]
                    tag_line += ([0.0] * (subsequence_width - TAG_LINE_LEN))
                    subsequence.append(tag_line)

                    # extracting metronome boundaries for each measure
                    # as they have different offsets compared to the initial metronome boundaries check
                    if uniform is False:
                        boundaries = measure.metronomeMarkBoundaries()
                        for boundary in boundaries:
                            metronomes[float(boundary[0])] = boundary[2].number

                    # adding elements to subsequence
                    for element in measure.notes.sorted:
                        offset = float(element.offset)
                        if uniform is False:
                            key = min(metronomes.keys(),
                                      key=lambda x: abs(x - offset))
                            metronome = metronomes[key]
                        length = float(element.quarterLength)
                        line = [offset, metronome, length]
                        # adding cord pitches to width
                        for pitch in element.pitches:
                            line.append(float(pitch.midi))

                        # zero-padding width
                        diff = subsequence_width - len(line)
                        if diff > 0:
                            for i in range(diff):
                                line.append(0.0)
                        if diff < 0:
                            if len(line) > highest_width_loss:
                                highest_width_loss = len(line)
                            for i in range(abs(diff)):
                                line.pop()

                        subsequence.append(line)

                    # zero-padding length
                    diff = subsequence_length + 1 - len(subsequence)
                    if diff > 0:
                        for i in range(diff):
                            subsequence.append([0.0] * subsequence_width)
                    elif diff < 0:
                        if len(subsequence) - 1 > highest_length_loss:
                            highest_length_loss = len(subsequence) - 1
                        for i in range(abs(diff)):
                            subsequence.pop()

                    subsequences.append(subsequence)

                # assigning input and target data to subsequent subsequences
                expected_offset = 0.0
                past_subsequence = None
                for subsequence in subsequences:
                    tag_line = subsequence[0]
                    current_offset = tag_line[0]
                    if current_offset == expected_offset and past_subsequence is not None:
                        self.__input.append(past_subsequence)
                        self.__target.append(subsequence)
                    past_subsequence = subsequence
                    expected_offset = current_offset + (tag_line[3] /
                                                        tag_line[4] * 4)

                print_progress(progress + 1, end, prefix=pf, suffix=sequence)

            if highest_width_loss > 0:
                print("Subsequence width (" + str(subsequence_width) +
                      ") may result in data losses. "
                      "Minimum width of " + str(highest_width_loss) +
                      " is recommended")
            if highest_length_loss > 0:
                print("Subsequence length (" + str(subsequence_length) +
                      ") may result in data losses. "
                      "Minimum length of " + str(highest_length_loss) +
                      " is recommended")
        else:
            print("Subsequence width (" + str(subsequence_width) +
                  ") cannot be smaller than " + str(TAG_LINE_LEN))
コード例 #15
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()