Ejemplo n.º 1
0
    def __init__(self, parent=None, mw=None, seq=None, refimage=None):
        super(SequenceGrabber, self).__init__(parent)
        self.mw = mw
        self.seq = seq
        self.frame = None
        self.active_time = None

        try:
            ibuf = ImageBuf(self.seq[0].path)
        except Exception as ex:
            print(ex)
            return
        spec = ibuf.spec()

        im = Image.new("RGB", (spec.width, spec.height), (0, 0, 0))
        draw = ImageDraw.Draw(im)
        font = ImageFont.truetype('/Library/Fonts/Arial Bold.ttf', 48)
        draw.text((spec.width / 3, spec.height / 2), "No Image", font=font)
        self.blank_qimage = ImageQt(im)

        wksavepath = "/tmp"
        wksavepath = wksavepath + "/sequencemissing.jpg".format(self.mw.APPID)
        self.blank_qimage.save(wksavepath)

        wkqim = QImage(wksavepath)
        self.blank_qimage = wkqim
Ejemplo n.º 2
0
    def read_img_metadata(cls, img_file: Path) -> dict:
        img_buf = ImageBuf(img_file.as_posix())
        img_dict = dict()

        if not img_buf:
            LOGGER.error(oiio.geterror())
            return img_dict

        for param in img_buf.spec().extra_attribs:
            img_dict[param.name] = param.value

        cls.close_img_buf(img_buf, img_file)

        return img_dict
Ejemplo n.º 3
0
    def toQImage(self, filepath):

        ibuf = ImageBuf(filepath)
        try:
            bufok = ibuf.read(subimage=0,
                              miplevel=0,
                              force=True,
                              convert=oiio.UINT8)
        except Exception as ex:
            print(ex)
            return None
        if not bufok:
            return None
        spec = ibuf.spec()

        width = spec.width
        height = spec.height

        # Expect the channel to be RGB from the beginning.
        # It might not work if it is a format like ARGB.
        # qimage = QtGui.QImage(width, height, QtGui.QImage.Format_RGB888)
        roi = oiio.ROI(0, width, 0, height, 0, 1, 0, 3)
        try:
            orgimg = Image.fromarray(ibuf.get_pixels(oiio.UINT8, roi))
            # for ImageQt source format error
            if orgimg.mode in self.mw.shot.NO_SUPPORT_IMAGEQT:
                orgimg = orgimg.convert('RGB')
            if self.mw.thumbnail_bright != self.mw.THUMB_DEFALUT_BRIGHT:
                eim = ImageEnhance.Brightness(orgimg)
                orgimg = eim.enhance(self.mw.thumbnail_bright)

            qimage = ImageQt(orgimg)
            # workaround https://bugreports.qt.io/browse/PYSIDE-884
            # output QImage to a file and reRead qimage
            wksavepath = QStandardPaths.writableLocation(
                QStandardPaths.TempLocation)
            wksavepath = wksavepath + "/{0}/sequencegrab.jpg".format(
                self.mw.APPID)
            qimage.save(wksavepath, "jpg")
            wkqim = QImage(wksavepath)
            qimage = wkqim
            os.remove(wksavepath)

        except Exception as ex:
            print(ex)
            return None
        return (qimage)
Ejemplo n.º 4
0
def processTextures(excludeFolders):
    allTextures = folder.getFiles(rootFolder,
                                  excludeFolders,
                                  textureExts,
                                  all=True)
    selectedTextures = []

    for texture in allTextures:

        directory = Path(texture).parent
        filename = Path(texture).stem
        extension = Path(texture).suffix

        IMG_CACHE = oiio.ImageCache.create(True)

        frameBufferOrig = ImageBuf(str(texture))
        spec = frameBufferOrig.spec()

        if (spec.tile_width == spec.width or spec.tile_width == 0
                or frameBufferOrig.nmiplevels == 1):
            if Path(directory,
                    filename + mipmapPrefix + mipmapExtension).exists():
                pass
            else:
                selectedTextures.append(texture)

        IMG_CACHE.invalidate(str(texture))

    if len(selectedTextures) == 0:

        showUI("", "Nothing to do...")

    if len(selectedTextures) is not 0:

        showUI(
            "Searching for Files",
            "Found " + str(len(selectedTextures)) +
            " files in scanline/No MipMap or not .exr format. We will make some :)",
        )
        time.sleep(4)

        for texture in tqdm(
                selectedTextures,
                desc="Complete",
                ncols=width,
                position=1,
                unit="file",
                ascii=True,
                bar_format=barFormat,
        ):

            directory = Path(texture).parent
            filename = Path(texture).stem
            extension = Path(texture).suffix

            showUI("Make tiled / MipMapped .exr",
                   "Current File: " + filename + extension)

            IMG_CACHE = oiio.ImageCache.create(True)

            frameBufferOrig2 = ImageBuf(str(texture))

            outPutFile = str(
                Path(directory, filename + mipmapPrefix + mipmapExtension))

            writeFramebuffer = threadAndStatus(writeTexture,
                                               [frameBufferOrig2, outPutFile],
                                               "Saving", 0, False)

            IMG_CACHE.invalidate(str(texture))

        showUI("", Fore.GREEN + "All textures converted...")

    tqdm.write(prefix + "Press Enter to exit or close the Terminal")
    wait_key()
Ejemplo n.º 5
0
def processHDRs():
    """Main entry point of the app."""

    if not Path(hdrPrevFolder).exists():
        Path(hdrPrevFolder).mkdir(parents=True)
    if not Path(hdrBlurFolder).exists():
        Path(hdrBlurFolder).mkdir(parents=True)

    hdrFiles = folder.getFiles(hdrFolder, "", hdrExts, all=False)
    previewFiles = folder.getFiles(hdrPrevFolder, "", hdrPrevExts, all=False)

    hdrFilesTiling = []
    hdrFilesBlurring = []
    hdrFilesPreview = []

    hdrFilesPreviewNames = []

    for previewFile in previewFiles:

        directory = Path(previewFile).parent
        filename = Path(previewFile).stem
        extension = Path(previewFile).suffix

        hdrFilesPreviewNames.append(filename)

    for hdrFile in hdrFiles:

        directory = Path(hdrFile).parent
        filename = Path(hdrFile).stem
        extension = Path(hdrFile).suffix

        IMG_CACHE = oiio.ImageCache.create(True)

        frameBufferOrig = ImageBuf(str(hdrFile))
        spec = frameBufferOrig.spec()

        if blurredPrefix not in filename:
            if Path(hdrBlurFolder,
                    filename + blurredPrefix + hdrExtension).exists():
                pass
            else:
                hdrFilesBlurring.append(hdrFile)
        if (spec.tile_width == spec.width or spec.tile_width == 0
                or frameBufferOrig.nmiplevels == 1):
            hdrFilesTiling.append(hdrFile)
        if filename not in hdrFilesPreviewNames:
            hdrFilesPreview.append(hdrFile)

        IMG_CACHE.invalidate(str(hdrFile))

    if (len(hdrFilesBlurring) == 0 and len(hdrFilesTiling) == 0
            and len(hdrFilesPreview) == 0):

        showUI("", "Nothing to do...")

    if len(hdrFilesTiling) is not 0:

        showUI(
            "Searching for Files",
            "Found " + str(len(hdrFilesTiling)) +
            " files in scanline/No MipMap or not .exr format. We will make some :)",
        )
        time.sleep(4)

        for hdrFileTiling in tqdm(
                hdrFilesTiling,
                desc="Complete",
                ncols=width,
                position=2,
                unit="file",
                ascii=True,
                bar_format=barFormat,
        ):

            directory = Path(hdrFileTiling).parent
            filename = Path(hdrFileTiling).stem
            extension = Path(hdrFileTiling).suffix

            showUI("Make tiled / MipMapped .exr",
                   "Current File: " + filename + extension)

            IMG_CACHE = oiio.ImageCache.create(True)

            frameBufferOrig2 = ImageBuf(str(hdrFileTiling))
            spec2 = frameBufferOrig2.spec()

            outPutFile = str(
                Path(directory, filename + tiledPrefix + hdrExtension))

            if spec2.width > hdrWidth:

                newHeight = calculateResizeHeight(spec2.width, spec2.height,
                                                  hdrWidth)
                resizedFramebuffer = threadAndStatus(
                    resizeHDR,
                    [frameBufferOrig2, hdrWidth, newHeight],
                    "Resizing",
                    1,
                    True,
                )

                writeFramebuffer = threadAndStatus(
                    writeEXR, [resizedFramebuffer, outPutFile], "Saving", 0,
                    False)

            else:
                writeFramebuffer = threadAndStatus(
                    writeEXR, [frameBufferOrig2, outPutFile], "Saving", 0,
                    False)

            IMG_CACHE.invalidate(str(hdrFileTiling))

            # if Path(hdrFileTiling).exists():
            #     Path(hdrFileTiling).unlink()
            #     Path(hdrFileTiling).with_suffix(hdrExtension)
            #     Path(outPutFile).rename(hdrFileTiling)

            if errorFlag == 0:
                # If file exists, delete it
                if Path(hdrFileTiling).exists():
                    newFile = Path(hdrFileTiling).with_suffix(hdrExtension)
                    Path(hdrFileTiling).unlink()
                    Path(outPutFile).resolve().rename(newFile)

                    if hdrFileTiling in hdrFilesPreview:
                        hdrFilesPreview.remove(hdrFileTiling)
                        hdrFilesPreview.append(newFile)
                    if hdrFileTiling in hdrFilesBlurring:
                        hdrFilesBlurring.remove(hdrFileTiling)
                        hdrFilesBlurring.append(newFile)
                    tqdm.write(prefix + Fore.GREEN +
                               "Successfully replaced the original file.")

                else:
                    tqdm.write(
                        prefix + Fore.RED +
                        "Error: %s not found. Could not delete the File." %
                        hdrFileTiling)

            else:
                tqdm.write(
                    prefix + Fore.RED +
                    "Something went wrong on conversion. File not deleted.")

        showUI("", Fore.GREEN + "All HDRs converted...")

    if len(hdrFilesBlurring) is not 0:

        showUI(
            __title__,
            "Found " + str(len(hdrFilesBlurring)) +
            " files with no blurred partners. We will make some :)",
        )
        time.sleep(4)

        for hdrFileBlurring in tqdm(
                hdrFilesBlurring,
                desc="Complete",
                ncols=width,
                position=3,
                unit="file",
                ascii=True,
                bar_format=barFormat,
        ):

            directory = Path(hdrFileBlurring).parent
            filename = Path(hdrFileBlurring).stem
            extension = Path(hdrFileBlurring).suffix

            showUI("Blurring HDRs", "Current File: " + filename + extension)

            IMG_CACHE = oiio.ImageCache.create(True)

            frameBufferOrig2 = ImageBuf(str(hdrFileBlurring))
            spec2 = frameBufferOrig2.spec()

            outPutFile = str(
                Path(hdrBlurFolder, filename + blurredPrefix + hdrExtension))

            newHeight = calculateResizeHeight(spec2.width, spec2.height,
                                              hdrBlurWidth)

            resizedFramebuffer = threadAndStatus(
                resizeHDR,
                [frameBufferOrig2, hdrBlurWidth, newHeight],
                "Resizing",
                2,
                True,
            )
            blurredFramebuffer = threadAndStatus(blurImage,
                                                 [resizedFramebuffer],
                                                 "Blurring", 1, True)
            writeFramebuffer = threadAndStatus(
                writeEXR, [blurredFramebuffer, outPutFile], "Saving", 0, False)

            IMG_CACHE.invalidate(str(hdrFileBlurring))

            # hdrFilesPreview.append(outPutFile)

        showUI("", Fore.GREEN + "All HDRs blurred...")

    if len(hdrFilesPreview) is not 0:

        showUI(
            "Searching for Files",
            "Found " + str(len(hdrFilesPreview)) +
            " files with no Preview-JPGs. We will make some :)",
        )
        time.sleep(4)

        for hdrFilePreview in tqdm(
                hdrFilesPreview,
                desc="Complete",
                ncols=width,
                position=3,
                unit="file",
                ascii=True,
                bar_format=barFormat,
        ):

            directory = Path(hdrFilePreview).parent
            filename = Path(hdrFilePreview).stem
            extension = Path(hdrFilePreview).suffix

            showUI("Thumbnail creation",
                   "Current File: " + filename + extension)

            IMG_CACHE = oiio.ImageCache.create(True)

            frameBufferOrig2 = ImageBuf(str(hdrFilePreview))
            spec2 = frameBufferOrig2.spec()

            outPutFile = str(Path(hdrPrevFolder,
                                  filename + thumbnailExtension))

            sRGBBuffer = threadAndStatus(convertColor,
                                         [frameBufferOrig2, "linear", "sRGB"],
                                         "Lin2sRGB", 2, True)

            newHeight = calculateResizeHeight(spec2.width, spec2.height,
                                              thumbnailWidth)

            resizedFramebuffer = threadAndStatus(
                resizeHDR, [sRGBBuffer, thumbnailWidth, newHeight], "Resizing",
                1, True)
            writeFramebuffer = threadAndStatus(
                writeJPG, [resizedFramebuffer, outPutFile], "Saving", 0, False)

            IMG_CACHE.invalidate(str(hdrFilePreview))

        showUI("", Fore.GREEN + "All previews generated...")

    tqdm.write(prefix + "Press Enter to exit or close the Terminal")
    wait_key()
Ejemplo n.º 6
0
class ImageCompare(object):
    """Image comparison using OpenImageIO. It creates a difference image.

    Args:
        image_a (str): File path to image
        image_b (str): File path to image to compare against. The baseline

    Attributes:
        debug (bool): Debug mode to output image processing when
            comparing images
        fail_threshold (float): Threshold value for failures
        warn_threshold (float): Threshold value for warnings
        image_a_buffer (ImageBuf): Image buffer
        image_b_buffer (ImageBuf): Image buffer
    """

    def __init__(self, image_a, image_b):

        self.debug = False
        self.fail_threshold = 0.1
        self.warn_threshold = 0.01

        self.image_a_buffer = ImageBuf()
        self.image_b_buffer = ImageBuf()

        # remove alpha channel from input images
        ImageBufAlgo.channels(
            self.image_a_buffer,
            ImageBuf(image_a),
            ('R', 'G', 'B')
        )
        ImageBufAlgo.channels(
            self.image_b_buffer,
            ImageBuf(image_b),
            ('R', 'G', 'B'),
        )

        # protected
        self._image_a_location = image_a
        self._image_b_location = image_b
        self._file_ext = os.path.splitext(image_a)[-1]
        self._compare_results = CompareResults()

    def compare(self, diff_image_location=None, blur=10, raise_exception=True):
        """Compare the two given images

        Args:
            diff_image_location (str): file path for difference image.
                Written only if there are failures
            blur (float): image blur to apply before comparing
        """

        if not diff_image_location:
            diff_image_location = os.path.dirname(self._image_a_location)

        self.blur_images(blur)
        ImageBufAlgo.compare(
            self.image_a_buffer,
            self.image_b_buffer,
            self.fail_threshold,
            self.warn_threshold,
            self._compare_results,
        )
        diff_buffer = self.create_diff_buffer()

        if self.debug:
            self.image_a_buffer.write(
                '{}/{}_debug{}'.format(
                    diff_image_location,
                    os.path.basename(self._image_a_location),
                    self._file_ext,
                )
            )
            self.image_b_buffer.write(
                '{}/{}_debug{}'.format(
                    diff_image_location,
                    os.path.basename(self._image_b_location),
                    self._file_ext,
                )
            )

        if self._compare_results.nfail > 0:
            ImageBufAlgo.color_map(diff_buffer, diff_buffer, -1, 'inferno')
            remap_buffer = ImageBuf()
            multiplier = 5
            ImageBufAlgo.mul(
                remap_buffer,
                diff_buffer,
                (multiplier, multiplier, multiplier, 1.0),
            )
            ImageBufAlgo.add(remap_buffer, self.image_a_buffer, remap_buffer)
            msg = report_msg.format(
                failures=self._compare_results.nfail,
                warn=self._compare_results.nwarn,
                meanerror=self._compare_results.meanerror,
                rmserror=self._compare_results.rms_error,
                psnr=self._compare_results.PSNR
            )

            remap_buffer.write(
                '{}/{}-{}_diff{}'.format(
                    diff_image_location,
                    os.path.basename(self._image_a_location),
                    os.path.basename(self._image_b_location),
                    self._file_ext,
                )
            )
            self.image_a_buffer.write(
                '{}/{}_debug{}'.format(
                    diff_image_location,
                    '1_a',
                    self._file_ext,
                )
            )
            self.image_b_buffer.write(
                '{}/{}_debug{}'.format(
                    diff_image_location,
                    '1_b',
                    self._file_ext,
                )
            )
            if raise_exception:
                raise ImageDifferenceError(msg)
            else:
                print(msg)

    def create_diff_buffer(self):
        """Create a difference image buffer from image_a and image_b

        Returns:
            ImageBuf: new difference image buffer
        """
        diff_buffer = ImageBuf(self.image_a_buffer.spec())
        ImageBufAlgo.sub(diff_buffer, self.image_a_buffer, self.image_b_buffer)
        ImageBufAlgo.abs(diff_buffer, diff_buffer)

        return diff_buffer

    def _blur(self, source, size=1.0):
        """Apply gaussian blur to given image

        Args:
            source (ImageBuf): Image buffer which to blur
            size (float): Blur size

        Return:
            ImageBuf: Blurred image
        """
        source = self._open(source)
        kernel = ImageBuf(source.spec())
        ImageBufAlgo.make_kernel(
            kernel,
            "gaussian",
            size, size
        )
        blurred = ImageBuf(source.spec())
        ImageBufAlgo.convolve(blurred, source, kernel)

        return blurred

    def _dilate(self, source):
        dilate = ImageBuf(source.spec())
        ImageBufAlgo.dilate(
            dilate,
            source,
            4,
            4,
        )
        return dilate

    def _open(self, source, size=3):
        erode = ImageBuf(source.spec())
        ImageBufAlgo.erode(erode, source, size, size)
        dilate = ImageBuf(source.spec())
        ImageBufAlgo.dilate(dilate, erode, size, size)

        return dilate

    def _median(self, source, size=5):
        size = int(size)
        median = ImageBuf(source.spec())
        ImageBufAlgo.median_filter(
            median,
            source,
            size,
            size
        )
        return median

    def blur_images(self, size):
        """Blur test images with given size

        Args:
            size (float): Blur size
        """
        self.image_a_buffer = self._blur(self.image_a_buffer, size)
        self.image_b_buffer = self._blur(self.image_b_buffer, size)
Ejemplo n.º 7
0
class DecyrptoMatte:
    """ Most of this code is shamelessly stolen from original cryptomatte_arnold unit tests under BSD-3 license
        https://github.com/Psyop/CryptomatteArnold
        https://github.com/Psyop/Cryptomatte
    """
    empty_pixel_threshold = 5  # Minimum number of opaque pixels a matte must contain
    empty_value_threshold = 0.2  # Minimum sum of all coverage values of all pixels

    def __init__(self, logger, img_file: Path, alpha_over_compositing=False):
        global LOGGER
        LOGGER = logger
        if logger is None:
            logging.basicConfig(level=logging.DEBUG)
            LOGGER = logging.getLogger(__name__)

        self.alpha_over_compositing = alpha_over_compositing

        self.img_file = img_file
        self.img = ImageBuf(img_file.as_posix())
        self.metadata_cache = {}
        self.manifest_cache = {}

    def shutdown(self):
        """ Release resources """
        try:
            del self.metadata_cache
            del self.manifest_cache
            self.img.clear()
            del self.img
            oiio.ImageCache().invalidate(self.img_file.as_posix())
        except Exception as e:
            LOGGER.error('Error closing img buf: %s', e)

    def _create_manifest_cache(self, metadata):
        """ Store the manifest contents from extracted metadata """
        if not self.manifest_cache:
            manifest = [
                m for k, m in metadata.items() if k.endswith('manifest')
            ]
            if manifest:
                self.manifest_cache = json.loads(manifest[0])

    def list_layers(self):
        """ List available ID layers of this cryptomatte image """
        metadata = self.crypto_metadata()
        layer_names = list()

        self._create_manifest_cache(metadata)
        LOGGER.info('Found Cryptomatte with %s id layers',
                    len(self.manifest_cache))

        # List ids in cryptomatte
        for layer_name, id_hex_str in self.manifest_cache.items():
            LOGGER.debug('ID: %s: %s', layer_name, id_hex_str)
            layer_names.append(layer_name)

        return layer_names

    def crypto_metadata(self) -> dict:
        """ Returns dictionary of key, value of cryptomatte metadata """
        if self.metadata_cache:
            return self.metadata_cache

        metadata = {
            a.name: a.value
            for a in self.img.spec().extra_attribs
            if a.name.startswith("cryptomatte")
        }

        for key in metadata.keys():
            if key.endswith("/manif_file"):
                sidecar_path = os.path.join(os.path.dirname(self.img.name),
                                            metadata[key])
                with open(sidecar_path) as f:
                    metadata[key.replace("manif_file", "manifest")] = f.read()

        self.metadata_cache = metadata
        return metadata

    def sorted_crypto_metadata(self):
        """
        Gets a dictionary of the cryptomatte metadata, interleved by cryptomatte stream.

        for example:
            {"crypto_object": {"name": crypto_object", ... }}

        Also includes ID coverage pairs in subkeys, "ch_pair_idxs" and "ch_pair_names".
        """
        img_md = self.crypto_metadata()
        cryptomatte_streams = {}

        for key, value in img_md.items():
            prefix, cryp_key, cryp_md_key = key.split("/")
            name = img_md["/".join((prefix, cryp_key, "name"))]
            cryptomatte_streams[name] = cryptomatte_streams.get(name, {})
            cryptomatte_streams[name][cryp_md_key] = value

        for cryp_key in cryptomatte_streams:
            name = cryptomatte_streams[cryp_key]["name"]
            ch_id_coverages = []
            ch_id_coverage_names = []
            channels_dict = {
                ch: i
                for i, ch in enumerate(self.img.spec().channelnames)
            }
            for i, ch in enumerate(self.img.spec().channelnames):
                if not ch.startswith(name):
                    continue
                if ch.startswith("%s." % name):
                    continue
                if ch.endswith(".R"):
                    red_name = ch
                    green_name = "%s.G" % ch[:-2]
                    blue_name = "%s.B" % ch[:-2]
                    alpha_name = "%s.A" % ch[:-2]

                    red_idx = i
                    green_idx = channels_dict[green_name]
                    blue_idx = channels_dict[blue_name]
                    alpha_idx = channels_dict[alpha_name]

                    ch_id_coverages.append((red_idx, green_idx))
                    ch_id_coverages.append((blue_idx, alpha_idx))
                    ch_id_coverage_names.append((red_name, green_name))
                    ch_id_coverage_names.append((blue_name, alpha_name))
            cryptomatte_streams[cryp_key]["ch_pair_idxs"] = ch_id_coverages
            cryptomatte_streams[cryp_key][
                "ch_pair_names"] = ch_id_coverage_names
        return cryptomatte_streams

    def get_mattes_by_names(self, layer_names: List[str]) -> dict:
        id_to_names = dict()

        if not self.manifest_cache:
            self._create_manifest_cache(self.crypto_metadata())

        for name in layer_names:
            if name in self.manifest_cache:
                id_val = self.hex_str_to_id(self.manifest_cache.get(name))
                id_to_names[id_val] = name

        id_mattes_by_name = dict()
        for id_val, id_matte in self._get_mattes_per_id(
                list(id_to_names.keys())).items():
            id_mattes_by_name[id_to_names.get(id_val)] = id_matte

        return id_mattes_by_name

    def _get_mattes_per_id(self, target_ids: List[float]) -> dict:
        """
            Get a alpha coverage matte for every given id
            as dict {id_value[float]: coverage_matte[np.array]}

            Matte arrays are single channel two dimensional arrays(shape: image_height, image_width)
        """
        if not target_ids:
            return dict()

        img_nested_md = self.sorted_crypto_metadata()

        w, h = self.img.spec().width, self.img.spec().height

        start = time.time()
        id_mattes = self._iterate_image(0, 0, w, h, img_nested_md, target_ids)

        # Purge mattes below threshold value
        for id_val in target_ids:
            v, p = id_mattes[id_val].max(), id_mattes[id_val].any(
                axis=-1).sum()

            if v < self.empty_value_threshold and p < self.empty_pixel_threshold:
                LOGGER.debug('Purging empty coverage matte: %s %s', v, p)
                id_mattes.pop(id_val)

        # --- DEBUG info ---
        LOGGER.debug(
            f'Iterated image : {w:04d}x{h:04d} - with {len(target_ids)} ids.')
        LOGGER.debug(
            f'Id Matte extraction finished in {time.time() - start:.4f}s')

        return id_mattes

    def _iterate_image(self, start_x: int, start_y: int, width: int,
                       height: int, img_nested_md: dict, target_ids: list):
        id_mattes = {
            id_val: np.zeros((height, width), dtype=np.float32)
            for id_val in target_ids
        }

        for y in range(start_y, start_y + height):
            for x in range(start_x, start_x + width):
                result_pixel = self.img.getpixel(x, y)

                for cryp_key in img_nested_md:
                    result_id_cov = self._get_id_coverage_dict(
                        result_pixel, img_nested_md[cryp_key]["ch_pair_idxs"])

                    high_rank_id, coverage_sum = 0.0, 0.0

                    for id_val, coverage in result_id_cov.items():
                        if id_val in id_mattes:
                            # Sum coverage per id
                            id_mattes[id_val][y][x] += coverage
                            # Sum overall coverage for this pixel of all ids
                            coverage_sum += coverage

                            if not high_rank_id:
                                # Store the id with the highest rank
                                # for this pixel (first entry in result_id_cov)
                                high_rank_id = id_val

                    # Highest ranked Id will be set fully opaque for the whole pixel
                    # if multiple Ids are contributing to this pixel
                    # getting matte ready for alpha over operations eg. Photoshop
                    if self.alpha_over_compositing and high_rank_id:
                        if id_mattes[high_rank_id][y][x] != coverage_sum:
                            id_mattes[high_rank_id][y][x] = coverage_sum

            if not y % 256:
                LOGGER.debug('Reading cryptomatte at vline: %s (%sx%s)', y,
                             width, height)

        return id_mattes

    @staticmethod
    def _get_id_coverage_dict(pixel_values, ch_pair_idxs):
        return {
            pixel_values[x]: pixel_values[y]
            for x, y, in ch_pair_idxs if (x != 0.0 or y != 0.0)
        }

    @staticmethod
    def mm3hash_float(name) -> float:
        hash_32 = mmh3.hash(name)
        exp = hash_32 >> 23 & 255
        if (exp == 0) or (exp == 255):
            hash_32 ^= 1 << 23

        packed = struct.pack('<L', hash_32 & 0xffffffff)
        return struct.unpack('<f', packed)[0]

    @staticmethod
    def hex_str_to_id(id_hex_string: str) -> float:
        """ Converts a manifest hex string to a float32 id value """
        packed = struct.Struct("=I").pack(int(id_hex_string, 16))
        return struct.Struct("=f").unpack(packed)[0]

    @staticmethod
    def id_to_hex_str(id_float: float) -> str:
        return "{0:08x}".format(
            struct.unpack('<I', struct.pack('<f', id_float))[0])

    @staticmethod
    def id_to_rgb(id_float):
        """ This takes the hashed id and converts it to a preview color """
        import ctypes
        bits = ctypes.cast(ctypes.pointer(ctypes.c_float(id_float)),
                           ctypes.POINTER(ctypes.c_uint32)).contents.value

        mask = 2**32 - 1
        return [
            0.0,
            float((bits << 8) & mask) / float(mask),
            float((bits << 16) & mask) / float(mask)
        ]

    @classmethod
    def layer_hash(cls, layer_name):
        """ Convert a layer name to hash hex string """
        return cls.id_to_hex_str(cls.mm3hash_float(layer_name))[:-1]

    @classmethod
    def merge_matte_and_rgb(cls,
                            matte: np.ndarray,
                            rgb_img: np.ndarray = None):
        """ Merge matte and rgb img array to rgba img array"""
        h, w = matte.shape
        rgba = np.empty((h, w, 4), dtype=matte.dtype)

        if rgb_img is None:
            rgba[:, :, 3] = rgba[:, :, 2] = rgba[:, :, 1] = rgba[:, :,
                                                                 0] = matte
        else:
            rgba[:, :, 3] = matte
            rgba[:, :, 2] = rgb_img[:, :, 2]
            rgba[:, :, 1] = rgb_img[:, :, 1]
            rgba[:, :, 0] = rgb_img[:, :, 0]

        return rgba