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
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
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)
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()
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()
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)
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