Beispiel #1
0
    def includes(self, image):
        """Checks if the item can be included in this sequence. i.e. does it share the same file name
        For example:
            s = Sequence(['fileA.0001.jpg', 'fileA.0002.jpg'])
            s.includes('fileA.0003.jpg')
            True
            s.includes('fileB.0003.jpg')
            False
        """

        # check if the seq exists
        if len(self) > 0:
            if not isinstance(image, AniImage):
                image = AniImage(image)
            # check if the image is the same as the last image, if not see if it can be a member
            if self[-1] != image:
                return self._is_member(self[-1], image)
            # check if the image is the same as the first image, if not see if it can be a member
            elif self[0] != image:
                return self._is_member(self[0], image)
            else:
                #  only image int he sequence
                if self[0] == image:
                    return True

        return True
Beispiel #2
0
    def create_sequences(self, image_path_list):
        """
        Builds a list of sequence(s) based reset a list of image paths. Stores in
        member variable seq_list. Clears the member variable if it has sequences in it.
        :param image_path_list: a list of paths to images, ex:
        C:\Images\sq180\s.1001.exr
        :return: Error if encountered, otherwise none
        """
        # reset sequence if exists
        if self.__seq_list:
            self.__seq_list = []

        # process any image paths that are directories and get images
        images_from_directories_list = []
        for image_path in image_path_list:
            if os.path.isdir(image_path):
                images = pyani.core.util.get_images_from_dir(image_path)
                # check if successfully got images, if not return the error
                if not isinstance(images, list):
                    return images
                images_from_directories_list.extend(images)

        # add directory images to main list
        if images_from_directories_list:
            image_path_list = images_from_directories_list

        # remove image paths that are directories from image list
        for index, image_path in enumerate(image_path_list):
            if os.path.isdir(image_path):
                image_path_list.pop(index)
        try:
            for image_path in image_path_list:
                # make an image object
                image = AniImage(image_path)
                # check if any sequences have been created
                if self.__seq_list:
                    for seq in self.__seq_list:
                        # sequence(s) exist, check if this image belongs to one or create a new sequence
                        if seq.includes(image):
                            seq.append(image)
                            break
                    else:
                        # image isn't part of an existing sequence, create a new one
                        self.__seq_list.append(AniImageSeq(image))

                else:
                    # no sequences yet, add image to a new sequence
                    self.__seq_list.append(AniImageSeq(image))
        except (AniImageError, AniImageSeqError, IndexError, ValueError) as e:
            error_msg = "Error creating image sequence from selection. Please see log. Error is: {0}".format(e)
            logger.exception(error_msg)
            return error_msg

        return None
Beispiel #3
0
    def __setitem__(self, index, image):
        """ Used to set a particular element in the sequence
        """
        # convert to an animage if its a string path
        if type(image) is not AniImage:
            image = AniImage(image)

        # check if image exists in sequence
        if self.includes(image):
            super(AniImageSeq, self).__setitem__(index, image)
            self.__frames = None
            self.__missing = None
        else:
            raise AniImageSeqError("Image is not a member of the image sequence.")
Beispiel #4
0
    def insert(self, index, image):
        """ Add another image to the sequence at the given index.
            :param image: a pyani.core.AnImage class object.
            :exc: `AniImageSeqError` raised if image is not a sequence member.
        """
        if type(image) is not AniImage:
            image = AniImage(image)

        if self.includes(image):
            super(AniImageSeq, self).insert(index, image)
            self.__frames = None
            self.__missing = None
        else:
            raise AniImageSeqError('Image {0} is not in this image sequence {1}'.format(image.path, self.path))
Beispiel #5
0
    def extend(self, images):
        """ Add images to the sequence.
            :param images: list of pyani.image.AniImage objects.
            :exc: `AniImageSeqError` raised if any images are not a sequence member.
        """
        for image in images:
            if type(image) is not AniImage:
                image = AniImage(image)

            if self.includes(image):
                super(AniImageSeq, self).append(image)
                self.__frames = None
                self.__missing = None
            else:
                raise AniImageSeqError('Image {0} is not in this image sequence {1}'.format(image.path, self.path))
Beispiel #6
0
    def append(self, image):
        """
        Adds another image to the sequence.
        :param image:  pyani.media.image.core.AniImage class object.
        :raises: an exception if image is not a sequence member.
        """
        if type(image) is not AniImage:
            image = AniImage(image)

        if self.includes(image):
            super(AniImageSeq, self).append(image)
            self.__frames = None
            self.__missing = None
        else:
            raise AniImageSeqError('Image {0} is not in this image sequence {1}'.format(image.path, self.path))
Beispiel #7
0
    def contains(self, image):
        """Checks for sequence membership.
        For example:
            s = Sequence(['fileA.0001.jpg', 'fileA.0002.jpg'])
            s.contains('fileA.0003.jpg')
            False
            s.contains('fileB.0003.jpg')
            False
        :param image: pyani.media.image.core.AniImage class object.
        :return: True if image is a sequence member.
        """
        if len(self) > 0:
            if not isinstance(image, AniImage):
                image = AniImage(image)
            return self.includes(image) and self.end_frame() >= image.frame >= self.start_frame()

        return False
Beispiel #8
0
    def __init__(self, images, strict_pad=True):
        """
        Constructor
        :param images: one or more pyani.image.AniImage objects
        :param strict_pad: option strict pad flag (True means padding must match)
        """

        # if pass a single image in, turn into a one element list
        if not isinstance(images, list):
            images = [images]

        super(AniImageSeq, self).__init__([AniImage(images.pop(0))])

        self.__strict_pad = strict_pad
        self.__missing = []     # list of integers representing missing frames
        self.__frames = None    # list of integers representing the frames in the sequence
        self.__name = None      # name of the sequence TODO: eventually this can be seq_shot when we get info

        while images:
            self.append(images.pop(0))
Beispiel #9
0
 def __contains__(self, image):
     super(AniImageSeq, self).__contains__(AniImage(image))
Beispiel #10
0
    def _copy_and_fill_seq(self, seq, user_frame_start, user_frame_end):
        """
        Copies existing frames to temp dir, and fills missing frames based of the frame hold member variable
        If frame hold is checked, holds existing frame until next existing frame. Otherwise fills with an image that
        says 'missing frame'. Uses user's input frame range, so if the sequence should be 1-20, but the first image
        is on frame 5, it will back fill frames 1-4 with 5. Conversely, if the range is 1-20, but the last existing
        frame is frame 15, fills 16-20 with 15.

        LOGIC :

        copy the existing frames to the temp directory. Get the user's frame range input.
        If the user start is before or after seq start (first existing image) then start is the user start. Otherwise
        its the seq start. Same for user frame range end, if its before or after the seq end (last existing image), use
        user inputted end. Then fill in all the missing frames by copying from the temp directory. All work is done
        in the temp directory to avoid changing the original image directory. Note that if the user gives multiple
        sequences frame start and end are set to the sequences frame start/end. Frame range input is ignored.

        :param seq: a pyani.image_seq.AniImageSeq object
        :param user_frame_start: the frame the user wants to start on
        :param user_frame_end: the frame the user wants to end on
        :return: a pyani.image_seq.AniImageSeq object of the copied images and any errors
        """

        # create the correct image version of the missing file image
        try:
            missing_frame_image = self.missing_frame_image[seq[0].ext]
            logger.info("Missing image format is {0}".format(seq[0].ext))
        except KeyError:
            missing_frame_image = convert_image(self.missing_frame_image['png'], seq[0].ext)
            logger.info("Missing image format is png")

        # name of files (up to frame portion of file name) in temp dir
        image_head_temp = os.path.join(self.temp_dir, seq.name + "_" + seq[0].base_name)

        seq_start = seq.start_frame()
        seq_end = seq.end_frame()

        # Get all missing frames
        existing_frames_list = [int(frame.frame) for frame in seq.frames()]

        # missing frames between user start and end, avoid missing frames outside user frame range
        missing_frames = [frame for frame in seq.missing() if user_frame_start <= frame <= user_frame_end]

        # user start is before sequence start
        if user_frame_start < seq_start:
            # get missing frames based reset user inputted start
            missing_temp = range(int(user_frame_start), int(seq_start))
            # make into frames with padding
            pad = user_frame_start.pad
            missing_temp_padded = [str(frame).rjust(pad, '0') for frame in missing_temp]
            # convert to frame objects
            try:
                missing_frames.extend([AniFrame(seq[0].path.replace(str(seq_start), str(f)))
                                       for f in missing_temp_padded])
            except AniFrameError as e:
                error_msg = "Problem creating missing frames. Error is {0}".format(e)
                logger.exception(error_msg)
                return None, error_msg

        # user end is after seq end
        if user_frame_end > seq_end:
            # get missing frames based reset user inputted end - a range of ints
            missing_temp = range(int(seq_end) + 1, int(user_frame_end) + 1)
            # make into frames with padding
            pad = user_frame_end.pad
            missing_temp_padded = [str(frame).rjust(pad, '0') for frame in missing_temp]
            # convert to frame objects
            try:
                missing_frames.extend([AniFrame(seq[-1].path.replace(str(seq_end), str(f)))
                                       for f in missing_temp_padded])
            except AniFrameError as e:
                error_msg = "Problem creating missing frames. Error is {0}".format(e)
                logger.exception(error_msg)
                return None, error_msg

        missing_frames = sorted(missing_frames)

        logger.info("Missing Frames are: {0}".format(missing_frames))

        # copy existing frames to missing

        # copy existing frames so that when we fill frames in, can copy from these. Don't want to change anything
        # in the original image directory. Also create an image object for the new copied image. Do after copy since
        # image must exist
        src = []
        dest = []
        for image in seq:
            # make sure images are in the user frame range, if not don't add to list or copy.
            if user_frame_start <= image.frame <= user_frame_end:
                image_renamed = "{0}.{1}.{2}".format(image_head_temp, image.frame, image.ext)
                src.append(image.path)
                dest.append(image_renamed)
        try:
            # if thread count max is smaller than the size of the existing frames, use thread count max, otherwise
            # size existing frames which is smaller than thread count
            if self._thread_count_max < len(src):
                thread_count = self._thread_count_max
            else:
                thread_count = len(src)
            # threaded ok, order of copy doesn't matter
            pyani.core.util.ThreadedCopy(src, dest, threads=thread_count)
        except (IOError, OSError, WindowsError) as e:
            error_msg = "Problem copying existing images in tmp dir. Error is {0}".format(e)
            logger.exception(error_msg)
            return None, error_msg

        try:
            # the copied images as string paths
            copied_image_list = [AniImage(image) for image in dest]
        except AniImageError as e:
            error_msg = "Problem creating existing images in tmp dir. Error is {0}".format(e)
            logger.exception(error_msg)
            return None, error_msg

        missing_dest = []
        missing_source = []
        # loop through missing frames, and copy from previous frame, skipping existing frames
        while missing_frames:
            # remove first missing frame from the list
            frame = missing_frames.pop(0)

            # check if missing image is before the seq start, happens when user inputs a start frame before the
            # sequence start - typically when bad frames render, and don't have the starting frame(s)
            if frame < seq_start:
                # as a padded frame
                frame_to_copy = seq_start
            # user start is after seq start
            elif frame > seq_start and frame == user_frame_start:
                # find closest frame - before this one
                for f in range(user_frame_start, seq_start-1, -1):
                    try:
                        first_existing_frame = AniFrame.from_int(f, seq.padding(formatted=False), seq[0].path)
                    except AniFrameError as e:
                        error_msg = "Problem creating AniFrame for first existing frame (unpadded) {0}." \
                                    " Error is {1}".format(str(f), e)
                        logger.exception(error_msg)
                        return None, error_msg
                    if os.path.exists(first_existing_frame.image_parent):
                        frame_to_copy = first_existing_frame
                        break
            # frames other than the start
            else:
                try:
                    # find the closest existing frame before this one
                    frame_to_copy_as_int = pyani.core.util.find_closest_number(
                        existing_frames_list, int(frame.frame), use_smallest=True
                    )
                    # convert the closest existing frame before current frame to a AniFrame object
                    frame_to_copy = AniFrame.from_int(
                        frame_to_copy_as_int, seq.padding(formatted=False), seq[0].path
                    )
                except AniFrameError as e:
                    error_msg = "Problem creating AniFrame for frame (unpadded) {0}. Error is {1}".format(
                        str(frame-1),
                        e
                    )
                    logger.exception(error_msg)
                    return None, error_msg
            # figure out which image we are using for the missing frame
            if self.frame_hold:
                # construct image path to copy - check for special case when user frame start is after seq_start
                # and was missing and have to copy from original directory
                if frame > seq_start and frame == user_frame_start:
                    image_to_copy = "{0}\\{1}.{2}.{3}".format(seq[0].dirname,seq[0].base_name,frame_to_copy,seq[0].ext)
                else:
                    image_to_copy = "{0}.{1}.{2}".format(image_head_temp, frame_to_copy,seq[0].ext)
            else:
                # using the 'missing frame' image
                image_to_copy = missing_frame_image

            # construct image path to copy to
            try:
                frame_padded = AniFrame.from_int(frame, seq.padding(formatted=False), seq[0].path)
            except AniFrameError as e:
                error_msg = "Problem creating AniFrame for frame {0}. Error is {1}".format(frame_padded, e)
                logger.exception(error_msg)
                return None, error_msg

            missing_image = "{0}.{1}.{2}".format(image_head_temp, frame_padded, seq[0].ext)
            # save a list of the images to copy
            missing_source.append(image_to_copy)
            # save a list of where we are copying the above images to
            missing_dest.append(missing_image)

        # if thread count max is smaller than the size of the existing frames, use thread count max, otherwise
        # size existing frames which is smaller than thread count
        if self._thread_count_max < len(src):
            thread_count = self._thread_count_max
        else:
            thread_count = len(src)
        # do threaded copy to fill missing frames. Threaded ok because removed dependency that copy has to be
        # sequential all missing frames copy off an existing frame. i.e. multiple missing frames may copy the same
        # image
        pyani.core.util.ThreadedCopy(missing_source, missing_dest, threads=thread_count)

        try:
            copied_image_list.extend([AniImage(image) for image in missing_dest])
            filled_sequence = AniImageSeq(sorted(copied_image_list))
        except (AniImageError, AniImageSeqError) as e:
            error_msg = "Problem creating missing images from existing images. Error is {0}".format(e)
            logger.exception(error_msg)
            return None, error_msg

        return filled_sequence, None
Beispiel #11
0
    def combine_sequences(self, progress_update):
        """
        Combines multiple sequences into one, and stores as a new sequence at the end of the sequence list. This
        preserves the original sequences to revert if the user un checks combine sequences. reports progress to ui
        :param progress_update : a pyqt progress dialog object
        :return error msg if can't combine, None if combine successfully
        """
        # make temp directory
        pyani.core.util.make_dir(self.temp_dir)

        flattened_sequences = []
        renamed_image_paths = []

        # check if a sequence exists and the combine flag is true, if not don't combine sequence lists
        if self.combine_seq and self.seq_list:
            # update progress
            progress_update.setLabelText("Flattening {0} Sequences Into One Sequence...".format(len(self.seq_list)))
            progress_update.setValue(25)
            QtWidgets.QApplication.processEvents()

            # build a regular python list of pyani.media.image.core.AniImage objects - must be self.__seq_list,
            # and not self.seq_list, because self.seq_list is an external representation and would
            # return the last sequence which it assume is the combined sequence when combined is checked. combined is
            # checked but we haven't built the combined sequence yet, see doc string. __seq_list will give us the
            # internal representation which is the sequences to combine
            for seq in self.__seq_list:
                for image in seq:
                    flattened_sequences.append(image)

            progress_update.setLabelText("Renaming {0} images...".format(len(flattened_sequences)))
            progress_update.setValue(50)
            QtWidgets.QApplication.processEvents()
            src = []
            # rename the image paths in the image objects and re-index the frames
            for index, image in enumerate(flattened_sequences):
                filename_replaced = image.name.replace(image.base_name, self.temp_image_name)
                new_frame = AniFrame.from_int(index+1, 4, filename_replaced)
                frame_reindexed = filename_replaced.replace(str(image.frame), str(new_frame))
                # fix the prefix so that all prefixes are the same - a dot '.'
                if not image.frame.prefix == ".":
                    last_position = frame_reindexed.rfind(image.frame.prefix)
                    frame_reindexed = frame_reindexed[:last_position] + "." + frame_reindexed[last_position+1:]
                new_image_name = os.path.join(self.temp_dir, frame_reindexed)
                renamed_image_paths.append(new_image_name)
                src.append(image.path)

            # if thread count max is smaller than the size of the existing frames, use thread count max, otherwise
            # size existing frames which is smaller than thread count
            if self._thread_count_max < len(src):
                thread_count = self._thread_count_max
            else:
                thread_count = len(src)
            progress_update.setLabelText("Copying Images to Temp Dir Using {0} Threads. This could take a while "
                                         "if the images are large in size.".format(str(thread_count)))
            progress_update.setValue(75)
            QtWidgets.QApplication.processEvents()
            try:
                # threaded ok, order of copy doesn't matter
                pyani.core.util.ThreadedCopy(src, renamed_image_paths, threads=thread_count)
            except (IOError, OSError) as e:
                error_msg = "There was an error combining the sequences during copy. Error reported {0}".format(e)
                logger.exception(error_msg)
                return error_msg

            try:
                # build a image sequence of the new named images and add to the end of the existing sequence list
                self.__seq_list.append(AniImageSeq([AniImage(image) for image in renamed_image_paths]))
            except (AniImageSeqError, AniImageError) as e:
                error_msg = "There was an error combining the sequences. Error reported {0}".format(e)
                logger.exception(error_msg)
                return error_msg

            return None