def from_gdal_dataset(cls, dataset, band): """ Creates a new 1-band pyvips.Image from `dataset` at `band` dataset: GDAL Dataset band: Number of the band, starting from 1 """ with LibVips.disable_warnings(): filename = dataset.GetFileList()[0] image1 = Image.new_from_file(filename) # Extract the band image2 = image1.extract_band((band - 1), n=1) # Cast to the right datatype, if necessary datatype = dataset.GetRasterBand(band).NumPyDataType if VImageAdapter(image2).NumPyType() == datatype: return image2 types = dict((v, k) for k, v in cls.NUMPY_TYPES.items()) image3 = Image.new_from_memory(image2.write_to_memory(), width=image2.width, height=image2.height, bands=1, format=types[datatype]) return image3
def extract_imgs(img_path:str, label_path:str, output_path:str): '''读取图像并进行切分处理 Args: img_path(str): 要处理的图像路径 label_path(str): 对应的标签图像 output_path(str): 输出目录 ''' print('Loading source image from {}.'.format(img_path)) src_img = vipImage.new_from_file(img_path) label_img = None if label_path is not None: print('Loading label image from {}.'.format(label_path)) label_img = vipImage.new_from_file(label_path) # 输出图片基本信息 for img in [('Source Image', src_img), ('Label Image', label_img)]: print('{} info:'.format(img[0])) if img[1] is None: continue for key in ['width', 'height', 'bands']: print('\t{}: {}'.format(key, getattr(img[1], key))) # 将图像完全加载到内存,耗时可能会很长,内存小的机器慎用 print('Loading image to memory, it may take a few minutes.') src_img = load_to_memory(src_img) label_img = load_to_memory(label_img) if label_img is not None else None crop_and_save(src_img, label_img, output_path, img_path)
def from_gdal_dataset(cls, dataset, band): """ Creates a new 1-band pyvips.Image from `dataset` at `band` dataset: GDAL Dataset band: Number of the band, starting from 1 """ with LibVips.disable_warnings(): filename = dataset.GetFileList()[0] image1 = Image.new_from_file(filename) # Extract the band image2 = image1.extract_band((band - 1), n=1) # Cast to the right datatype, if necessary datatype = dataset.GetRasterBand(band).NumPyDataType if VImageAdapter(image2).NumPyType() == datatype: return image2 types = dict((v, k) for k, v in cls.NUMPY_TYPES.items()) image3 = Image.new_from_memory(image2.write_to_memory(), width=image2.width, height=image2.height, bands=1, format=types[datatype]) image3._buf = image2 return image3
def rescale(img: Image, scale: Union[float, Tuple[float, float]], kernel: str = Kernel.LINEAR) -> Image: if isinstance(scale, float): return img.resize(scale, kernel=kernel) else: scale, vscale = scale return img.resize(scale, vscale=vscale, kernel=kernel)
def add(self, im: pyvips.Image) -> pyvips.Image: """Use method which finds complementary color. Returns image """ text = pyvips.Image.text( self.text, width=im.width, dpi=300, font=f"{self.font}" ) text = text.rotate(self.rotate) # the position of the overlay in the image margin = 25 position = self.position if position == "top-left": x_pos = margin y_pos = margin elif position == "top-right": x_pos = im.width - text.width - margin y_pos = margin elif position == "bottom-right": x_pos = im.width - text.width - margin y_pos = im.height - text.height - margin elif position == "bottom-left": x_pos = margin y_pos = im.height - text.height - margin else: print(f"Incorrect watermark position: {position}") sys.exit(1) # find the non-alpha image bands if im.hasalpha(): no_alpha = im.extract_band(0, n=im.bands - 1) else: no_alpha = im # the pixels we will render the overlay on top of bg = no_alpha.crop(x_pos, y_pos, text.width, text.height) # mask the background with the text, so all non-text areas become zero, and find # the zero-excluding average avg = avgze(text.ifthenelse(bg, 0)) # for each band, find the opposing value mx = 255 if im.format == "uchar" else 65535 text_colour = [oppose(avg[i], mx) for i in range(len(avg))] # make an overlay ... we put solid colour into the image and set a faded version # of the text mask as the alpha overlay = bg.new_from_image(text_colour) overlay = overlay.bandjoin((text * self.opacity).cast("uchar")) # and composite that on to the original image im = im.composite(overlay, "over", x=x_pos, y=y_pos) return im
def getFontSizeByLanguage(string, font, renderer_lib, remove_height_offset=False): # get fontsize according to language string = cgi.escape(string) # Here preparing string to render if any special symbol found then convert into html code for render like(& to &) if int(renderer_lib) == 1: size = font.getsize(string) if remove_height_offset: size[1] -= font.getoffset(string)[1] else: size = pyImage.text(string, dpi=font['normal_font_size'], fontfile=font['normal_font_file']).width, pyImage.text(string, dpi=font['normal_font_size'], fontfile=font['normal_font_file']).height x = pyImage.text(string, dpi=font['normal_font_size'], fontfile=font['normal_font_file']) return size
def write_buffer(self, image, resolution): if VImageAdapter( image).BufferSize() >= self.IMAGE_BUFFER_DISK_THRESHOLD: logger.debug('Buffering resolution {0} to disk'.format(resolution)) vipsfile = Image.new_temp_file("%s.v") tempfile_image = image.write(vipsfile) return tempfile_image logger.debug('Buffering resolution {0} to memory'.format(resolution)) return Image.new_from_memory(image.write_to_memory(), image.width, image.height, image.bands, 'uchar')
def get_vips_field( vips_image: VIPSImage, field: str, default: Any = None ) -> Any: try: return vips_image.get_value(field) except VIPSError: return default
def _reprocess_image(queue: Queue) -> None: global stats while not queue.empty(): img_data = queue.get() img_data["filePath"] = f"{os.path.splitext(img_data['filePath'])[0]}.tif" tif_filename = os.path.basename(img_data["filePath"]) local_file = f"TEMP_{os.path.basename(img_data['id'])}" logger.info(f"Processing {img_data['id']}") if _download_source_file(img_data, local_file): image = _preprocess_image(img_data, local_file) if image: image.tiffsave(tif_filename, tile=True, pyramid=True, compression=config.COMPRESSION_TYPE, tile_width=config.PYTIF_TILE_WIDTH, tile_height=config.PYTIF_TILE_HEIGHT, \ xres=config.DPI_VALUE, yres=config.DPI_VALUE) # noqa new_tiff = Image.tiffload(tif_filename) _upload_files(img_data, local_file, tif_filename) gql.update_item(img_data['id'], new_tiff.height, new_tiff.width) os.remove(tif_filename) os.remove(local_file) logger.info(f'Completed {local_file}') else: gql.remove_missing_item(img_data['id']) Statistic.download_err(img_data) Statistic.attempted() queue.task_done()
def __init__(self, piece_theme: str, size: int = 70): piece_dir = os.path.join('data', 'piece', piece_theme) #: Maps pieces to their corresponding .svg filenames self.pieces_map = { 'r': 'bR', 'q': 'bQ', 'n': 'bN', 'k': 'bK', 'p': 'bP', 'b': 'bB', 'R': 'wR', 'Q': 'wQ', 'N': 'wN', 'K': 'wK', 'P': 'wP', 'B': 'wB' } # Reads the available piece theme's .svg images and save them as .png of appropraite size for piece in self.pieces_map: piece_path = os.path.join(piece_dir, self.pieces_map[piece] + '.svg') image = VipsImage.thumbnail(piece_path, size, height=size) image.write_to_file(f"Images/{ self.pieces_map[piece]}.png") #: Dictionary where pieces are the keys and corresponding PIL Images are the values self.piece_imgs = dict() for piece in self.pieces_map: self.piece_imgs[piece] = Image.open( f"Images/{ self.pieces_map[piece]}.png")
def write_buffer(self, image, resolution): if VImageAdapter(image).BufferSize() >= self.IMAGE_BUFFER_DISK_THRESHOLD: logger.debug( 'Buffering resolution {0} to disk'.format(resolution) ) vipsfile = Image.new_temp_file("%s.v") tempfile_image = image.write(vipsfile) return tempfile_image logger.debug( 'Buffering resolution {0} to memory'.format(resolution) ) return Image.new_from_memory( image.write_to_memory(), image.width, image.height, image.bands, 'uchar' )
def new_rgba(cls, width, height, ink=None): """Creates a new transparent RGBA image sized width × height.""" # Creating a placeholder image with new_from_memory (equivalent of the # old vipsCC frombuffer) creates an image # which is a few byes different when written back to memory, which means # we can't store and retrieve it from its hash. So instead we use a # temporary transparent 256x256 file for the initial image image = Image.new_from_file( os.path.join(os.path.dirname(os.path.realpath(__file__)), 'default_rgba.png')) image = image.copy( width=width, height=height, coding='none', # No coding and no compression interpretation='srgb', xres=2.835, yres=2.835, # Arbitrary 600 dpi xoffset=0, yoffset=0 # Working buffer ) if ink is not None: image.draw_rect([ink.r, ink.g, ink.b, ink.a], 0, 0, width, height, fill=True) return image
def cvtStandardImgFormat(self, savePath, fmt, compression=False): im_arr = self.getPixelArray() if fmt.endswith('jpg') and im_arr[0] > 65535 and im_arr[1] > 65535: print('Too Large size for JPEG-2000 format...') return elif fmt.endswith('png') and im_arr[0] > 10000 and im_arr[1] > 10000: print('Too Large size for PNG format...') return fname = os.path.basename(self.filename) sname = os.path.splitext(fname)[-1] im = Image.new_from_array(im_arr) if fmt.endswith('tiff'): if compression: im.write_to_file(savePath + '/' + sname + '.' + fmt, compression='lzw') else: im.write_to_file(savePath + '/' + sname + '.' + fmt, compression='lzw') else: im.write_to_file(savePath + '/' + sname + '.' + fmt)
def pyvips_loader(path: str, access=pyvips.Access.RANDOM, memory=True) -> Image: """ Strange performance gain when using Access.RANDOM """ return Image.new_from_file(path, access=access, memory=memory)
def add_to_image(self, im: pyvips.Image) -> pyvips.Image: """Add watermark to supplied image.""" LOG.info("Adding watermark '%s'", self.text) text = pyvips.Image.text( self.text, width=im.width, dpi=300, font=f"{self.font}" ) text = text.rotate(self.rotate) text = (text * self.opacity).cast("uchar") text = text.embed( self.x_margin, (im.height - text.height) - self.y_margin, im.width, im.height, ) if self.replicate: text = text.replicate( 1 + im.width / text.width, 1 + im.height / text.height ) text = text.crop(0, 0, im.width, im.height) # we want to blend into the visible part of the image and leave any alpha # channels untouched ... we need to split im into two parts # guess how many bands from the start of im contain visible colour information if im.hasalpha(): alpha = im.extract_band(im.bands - 1) im = im.extract_band(0, n=im.bands - 1) else: alpha = None if im.bands == 4: # cmyk text_color: Any = list(rgb_to_cmyk(self.fg_color)) elif im.bands == 3: # rgb text_color = list(self.fg_color) else: # mono text_color = rgb_to_grayscale(self.fg_color) LOG.info("Watermark fg_color: %s (original: %s)", text_color, self.fg_color) im = text.ifthenelse(text_color, im, blend=True) # reattach alpha if alpha: im = im.bandjoin(alpha) return im
def test_vips_to_numpy(): img = VIPSImage.new_from_array([[1, 2, 3], [4, 5, 6]]) arr = vips_to_numpy(img) h, w, d = arr.shape assert w == img.width assert h == img.height assert d == img.bands assert arr.dtype == vips_format_to_dtype[img.format]
def read_func(path, region, page=None, subifd=None): # noqa opts = dict(page=page) if subifd is not None: opts['subifd'] = subifd tiff_page = VIPSImage.tiffload(str(path), **opts) im = tiff_page.extract_area(region.left, region.top, region.width, region.height) return im
def __init__(self, original_image, output_raster_width, output_raster_height, total_seconds, fps, image_lib='cv'): self.file_path = original_image self.image_lib = None self.output_raster_width = output_raster_width self.output_raster_height = output_raster_height self.total_frames = int(float(total_seconds) * float(fps)) self.image_lib = image_lib self.fps = fps if self.image_lib == 'pillow': print('using Pillow') self.original_image = PImage.open(self.file_path) self.original_image_width, self.original_image_height = self.original_image.size if self.image_lib == 'vips': from pyvips import Image as VImage from gi.repository import Vips print('using vips') self.original_image = VImage.new_from_file(self.file_path) self.original_image_width = self.original_image.width self.original_image_height = self.original_image.height self.output_file_name = 'output' if self.image_lib == 'cv': print('using cv') self.original_image = cv2.imread(self.file_path, cv2.IMREAD_UNCHANGED) self.original_image_height, self.original_image_width, _ = self.original_image.shape self.processed_frames = 0 self.render_start_time = 0 self.render_status = 'queued' self.render_estimated_seconds_remaining = 0 self.render_estimated_total_seconds = 0 self.render_fps = 0 self.prores_mez = False self.ffmpeg = shutil.which('ffmpeg') if not self.ffmpeg: self.ffmpeg = '/usr/local/bin/ffmpeg' self.convert = shutil.which('convert') if not self.convert: self.convert = '/usr/local/bin/convert' self.water_mark = False
def vips_image_to_numpy(img: Image) -> np.ndarray: """ https://libvips.github.io/pyvips/intro.html#numpy-and-pil """ np_3d = np.ndarray(buffer=img.write_to_memory(), dtype=FORMAT_TO_DTYPE[img.format], shape=[img.height, img.width, img.bands]) return np_3d
def numpy_to_vips(np_array: np.ndarray, width: Optional[int] = None, height: Optional[int] = None, n_channels: Optional[int] = None) -> VIPSImage: """ Convert a Numpy array to a VIPS image. Parameters ---------- np_array : array-like Numpy array to convert. If 1D, it is expected it contains flattened image data. width : int (optional) Width of the image, must be given if `np_array` is 1D, otherwise inferred from shape. height : int (optional) Height of the image, must be given if `np_array` is 1D, otherwise inferred from shape. n_channels : int (optional) n_channels of the image, must be given if `np_array` is 1D, otherwise inferred from shape. Returns ------- image VIPS image representation of the array Raises ------ ValueError If it is impossible to convert provided array. """ if not np_array.flags['C_CONTIGUOUS']: np_array = np.ascontiguousarray(np_array) if np_array.ndim > 3: raise NotImplementedError elif np_array.ndim > 1: if np_array.ndim == 2: height_, width_ = np_array.shape n_channels_ = 1 else: height_, width_, n_channels_ = np_array.shape width = width if width is not None else width_ height = height if height is not None else height_ n_channels = n_channels if n_channels is not None else n_channels_ if width * height * n_channels != np_array.size: raise ValueError(f"Cannot convert {np_array} to VIPS image") flat = np_array.reshape(np_array.size) vips_format = dtype_to_vips_format[str(np_array.dtype)] return VIPSImage.new_from_memory(flat.data, width, height, n_channels, vips_format)
def pyvips_resize_by_size(img: Image, size: Tuple[int, int], kernel: str = Kernel.LINEAR) -> Image: """ size -> height, width """ # w, h = size h, w = size scale = w / img.width vscale = h / img.height return img.resize(scale, vscale=vscale, kernel=kernel)
def _extract_channels(im: VIPSImage, c: Optional[Union[int, List[int]]]) -> VIPSImage: if c is None or im.bands == len(c): return im elif type(c) is int or len(c) == 1: if len(c) == 1: c = c[0] return im.extract_band(c) else: channels = list(itemgetter(*c)(im)) im = channels[0].bandjoin(channels[1:]) return im
def vips_thumbnail( self, width: int, height: int, **loader_options ) -> VIPSImage: """Get VIPS thumbnail using vips shrink-on-load features.""" filename = self.vips_filename_with_options( str(self.format.path), **loader_options ) image = cached_vips_file(self.format) if image.interpretation in ("grey16", "rgb16"): # Related to https://github.com/libvips/libvips/issues/1941 ? return VIPSImage.thumbnail( filename, width, height=height, size=VIPSSize.FORCE, linear=True ).colourspace(image.interpretation) return VIPSImage.thumbnail( filename, width, height=height, size=VIPSSize.FORCE )
def generate_properties(filename): print(f'Generating {filename} ...') with open(filename, 'w') as f: f.write(' // Auto-generated properties\n') # all magic properties tmp_file = Image.new_temp_file('%s.v') all_properties = tmp_file.get_fields() for name in all_properties: cpp_name = cppize(name) f.write(f' .property("{cpp_name}", &Image::{cpp_name})\n')
def bandreduction(bands: List[VIPSImage], reduction: ChannelReduction) -> VIPSImage: if reduction == ChannelReduction.ADD: return VIPSImage.sum(bands).cast(bands[0].format) elif reduction == ChannelReduction.MAX: return Operation.call('bandrank', bands, index=len(bands) - 1) elif reduction == ChannelReduction.MIN: return Operation.call('bandrank', bands, index=0) elif reduction == ChannelReduction.MED: return Operation.call('bandrank', bands, index=-1) else: raise ValueError(f"{reduction} not implemented")
def from_numpy_array(cls, array, width, height, bands, format): """ Returns a new pyvips.Image created from a NumPy `array` of pixel data. array: The NumPy array width: Integer dimension height: Integer dimension bands: Number of bands in the buffer format: Band format (all bands must be the same format) """ array = array.astype(cls.NUMPY_TYPES[format]) buf = memoryview(array) image = Image.new_from_memory(buf, width, height, bands, format) return image
def load_image(self, image_id): """Load the specified image and return a [H,W,3] Numpy array. """ # Load image with Vips because if ignores EXIF rotation (as it should). image = self.vips_image_to_numpy_array(VipsImage.new_from_file(self.image_info[image_id]['path'])) # image = skimage.io.imread(self.image_info[image_id]['path']) # If grayscale. Convert to RGB for consistency. if image.ndim != 3 or image.shape[-1] == 1: image = skimage.color.gray2rgb(image.squeeze()) # If has an alpha channel, remove it for consistency if image.shape[-1] == 4: image = image[..., :3] return image
def process_image(self, imageId, proposals): try: image = VipsImage.new_from_file(self.images[imageId]) if bool(self.scale_factors) != False: scale_factor = self.scale_factors[imageId] image = image.resize(scale_factor) proposals = np.round( np.array(proposals, dtype=np.float32) * scale_factor).astype(int) masks = [] for proposal in proposals: mask = np.zeros((image.height, image.width), dtype=np.uint8) cv2.circle(mask, (proposal[0], proposal[1]), proposal[2], 1, -1) masks.append(mask.astype(np.bool)) image_paths = [] mask_paths = [] mean_pixels = [] for i, proposal in enumerate(proposals): image_file = '{}_{}.jpg'.format(imageId, i) image_crop, mask_crops = self.generate_crop( image, masks, proposal) mask_file = self.save_mask(mask_crops, image_file, self.training_masks_path) image_crop.write_to_file(os.path.join( self.training_images_path, image_file), strip=True, Q=95) image_paths.append(image_file) mask_paths.append(mask_file) np_crop = np.ndarray(buffer=image_crop.write_to_memory(), shape=[ image_crop.height, image_crop.width, image_crop.bands ], dtype=np.uint8) mean_pixels.append( np_crop.reshape((-1, image.bands)).mean(axis=0)) except VipsError as e: print('Image #{} is corrupt! Skipping...'.format(imageId)) return False, False, False return image_paths, mask_paths, mean_pixels
def read_window(self, region, out_width, out_height, c: Optional[Union[int, List[int]]] = None, **other): tier = self.format.pyramid.most_appropriate_tier( region, (out_width, out_height)) region = region.scale_to_tier(tier) page = tier.data.get('page_index') tiff_page = VIPSImage.jp2kload(str(self.format.path), page=page) im = tiff_page.extract_area(region.left, region.top, region.width, region.height) return self._extract_channels(im, c)
def read_tile(self, tile, c: Optional[Union[int, List[int]]] = None, **other): tier = tile.tier page = tier.data.get('page_index') tiff_page = VIPSImage.jp2kload(str(self.format.path), page=page) # There is no direct access to underlying tiles in vips # But the following computation match vips implementation so that only the tile # that has to be read is read. # https://github.com/jcupitt/tilesrv/blob/master/tilesrv.c#L461 # TODO: is direct tile access significantly faster ? im = tiff_page.extract_area(tile.left, tile.top, tile.width, tile.height) return self._extract_channels(im, c)
def from_numpy_array(cls, array, width, height, bands, format): """ Returns a new pyvips.Image created from a NumPy `array` of pixel data. array: The NumPy array width: Integer dimension height: Integer dimension bands: Number of bands in the buffer format: Band format (all bands must be the same format) """ array = array.astype(cls.NUMPY_TYPES[format]) buf = memoryview(array) image = Image.new_from_memory(buf, width, height, bands, format) # Hold on to the numpy array to prevent garbage collection image._numpy_array = array return image
def create_post_file_kwargs(self, post, force_file=False, no_file=False): if post.id in self._prepared_post_kwargs and not force_file and not no_file: return self._prepared_post_kwargs[post.id] post.file.seek(0) file = InputFile(post.file, filename=f'{post.id}.{post.file_extension}') kwargs = {} if force_file: self.logger.info(f'┏ {post.id}: Preparing document as {post.file_extension}') kwargs['document'] = file func = danbooru_bot.updater.bot.send_document return func, kwargs elif no_file: self.logger.info(f'┏ {post.id}: Forced no file') func = danbooru_bot.updater.bot.send_message return func, kwargs elif post.is_image: width, height = post.image_width, post.image_height self.logger.info(f'┏ {post.id}: Preparing photo') if post.file_size > 10 * 1024**2 or width + height > 10000: post.file.seek(0) image = Image.new_from_buffer(post.file.read(), '') while True: while width + height > 10000: width, height = int(width * .75), int(height * .75) if (scale := width / image.width) != 1: # type: ignore image = image.resize(scale) # type: ignore self.logger.info(f'┃ Resizing to a scale of {scale:.2f} [{image.width}x{image.height}]') with BytesIO() as out: out.write(image.write_to_buffer('.jpg')) # type: ignore out.seek(0) new_length = len(out.read()) if new_length > 10 * 1024**2: image = image.resize(.9) # type: ignore continue self.logger.info(f'┃ Reduced file size from {post.file_size / 1024**2:.2f}Mb to {new_length / 1024**2:.2f}Mb') out.seek(0) file = InputFile(out, filename=f'{post.id}.jpg') break kwargs['photo'] = file func = danbooru_bot.updater.bot.send_photo
def vips_to_numpy(vips_image: VIPSImage) -> np.ndarray: """ Convert a VIPS image to a Numpy array. Parameters ---------- vips_image : VIPSImage VIPS image to convert Returns ------- image Array representation of VIPS image. Shape is always (height, width, bands). """ return np.ndarray( buffer=vips_image.write_to_memory(), dtype=vips_format_to_dtype[vips_image.format], shape=[vips_image.height, vips_image.width, vips_image.bands])
def new_rgba(cls, width, height, ink=None): """Creates a new transparent RGBA image sized width × height.""" # Creating a placeholder image with new_from_memory (equivalent of the # old vipsCC frombuffer) creates an image # which is a few byes different when written back to memory, which means # we can't store and retrieve it from its hash. So instead we use a # temporary transparent 256x256 file for the initial image image = Image.new_from_file( os.path.join( os.path.dirname(os.path.realpath(__file__)), 'default_rgba.png' ) ) image = image.copy( width=width, height=height, coding='none', # No coding and no compression interpretation='srgb', xres=2.835, yres=2.835, # Arbitrary 600 dpi xoffset=0, yoffset=0 # Working buffer ) if ink is not None: image.draw_rect( [ink.r, ink.g, ink.b, ink.a], 0, 0, width, height, fill=True ) return image
def image(self): if self._image is None: self._image = Image.new_from_file(self.inputfile) return self._image