def __init__(self, box): """ Args: box: 4-tuple specifying the left, top, right, and bottom coords. """ BaseFilter.__init__(self) if box[2] <= box[0] or box[3] <= box[1]: raise RuntimeError('Specified box has zero width or height') self.box = box def process(self, image): """ Args: image: The image to process. Returns: a single image, or a list containing one or more images. """ BaseFilter.process(self, image) if self.box[2] > image.size[0] or self.box[3] > image.size[1]: raise RuntimeError('Crop coordinates exceed image bounds') return image.crop(self.box)
def process(self, image): """ Args: image: The image to process. Returns: a single image, or a list containing one or more images. """ BaseFilter.process(self, image) # Create the mask around the source image mask = image.split()[-1] if image.mode[-1] != 'A' or isSimpleBBox(mask): mask = createMask(image, threshold=self._threshold, fillHoles=True, backgroundColor=self.background, blurRadius=self._blurRadius, maskScale=self._maskScale) # Process each value newImages = [] for value in self._values: if value is None: value = self.background bg = ImageChops.constant(image, value) newImage = Image.composite(image.split()[0], bg, mask) newImage.putalpha(image.split()[-1]) newImages.append(newImage) if len(newImages) == 1: return newImages[0] else: return newImages
def __init__(self, level=1): """ Args: level: Number of times to blur. """ BaseFilter.__init__(self) self.level = level def process(self, image): """ Args: image: The image to process. Returns: a single image, or a list containing one or more images. """ BaseFilter.process(self, image) mask = image.split()[1] for i in range(self.level): sharpness_enhancer = ImageEnhance.Sharpness(image.split()[0]) image = sharpness_enhancer.enhance(0.0) image.putalpha(mask) return image
def __init__(self, value=None, threshold=10, maskScale=1.0, blurRadius=0.0): """ Args: value: If None, the background is filled in with the background color. Otherwise, it is filled with value. If value is a list, then this filter will return multiple images, one for each value """ BaseFilter.__init__(self) if hasattr(value, '__len__'): self._values = value else: self._values = [value] self._threshold = threshold self._maskScale = maskScale self._blurRadius = blurRadius def getOutputCount(self): """Return the number of images returned by each call to process(). If the filter creates multiple simultaneous outputs, return a tuple: (outputCount, simultaneousOutputCount). """ return len(self._values) def process(self, image): """ Args: image: The image to process. Returns: a single image, or a list containing one or more images. """ BaseFilter.process(self, image) # Create the mask around the source image mask = image.split()[-1] if image.mode[-1] != 'A' or isSimpleBBox(mask): mask = createMask(image, threshold=self._threshold, fillHoles=True, backgroundColor=self.background, blurRadius=self._blurRadius, maskScale=self._maskScale) # Process each value newImages = [] for value in self._values: if value is None: value = self.background bg = ImageChops.constant(image, value) newImage = Image.composite(image.split()[0], bg, mask) newImage.putalpha(image.split()[-1]) newImages.append(newImage) if len(newImages) == 1: return newImages[0] else: return newImages
def process(self, image): """ Args: image: The image to process. Returns: a single image, or a list containing one or more images. """ BaseFilter.process(self, image) if self.box[2] > image.size[0] or self.box[3] > image.size[1]: raise RuntimeError('Crop coordinates exceed image bounds') return image.crop(self.box)
def process(self, image): """ Args: image: The image to process. Returns: a single image, or a list containing one or more images. """ BaseFilter.process(self, image) if self.mode != 'gray': raise RuntimeError("EqualizeHistogram only supports grayscale images.") if self.region == 'bbox': bbox = image.split()[1].getbbox() croppedImage = image.crop(bbox) croppedImage.load() alpha = croppedImage.split()[1] croppedImage = ImageOps.equalize(croppedImage.split()[0]) croppedImage.putalpha(alpha) image.paste(croppedImage, bbox) elif self.region == 'mask': bbox = image.split()[1].getbbox() croppedImage = image.crop(bbox) croppedImage.load() alpha = croppedImage.split()[1] # Fill in the part of the cropped image outside the bounding box with # uniformly-distributed noise noiseArray = \ numpy.random.randint(0, 255, croppedImage.size[0]*croppedImage.size[1]) noiseImage = Image.new('L', croppedImage.size) noiseImage.putdata(noiseArray) compositeImage = Image.composite(croppedImage, noiseImage, alpha) # Equalize the composite image compositeImage = ImageOps.equalize(compositeImage.split()[0]) # Paste the part of the equalized image within the mask back # into the cropped image croppedImage = Image.composite(compositeImage, croppedImage, alpha) croppedImage.putalpha(alpha) # Paste the cropped image back into the full image image.paste(croppedImage, bbox) elif self.region == 'all': alpha = image.split()[1] image = ImageOps.equalize(image.split()[0]) image.putalpha(alpha) return image
def process(self, image): """ Args: image: The image to process Returns: a single image, or a list containing one or more images """ BaseFilter.process(self, image) if self.mode != 'gray': raise RuntimeError("NormalizeContrast only supports grayscale images.") if self.region == 'bbox': bbox = image.split()[1].getbbox() croppedImage = image.crop(bbox) croppedImage.load() alpha = croppedImage.split()[1] croppedImage = \ ImageOps.autocontrast(croppedImage.split()[0], cutoff=self.cutoff) croppedImage.putalpha(alpha) image.paste(croppedImage, image.bbox) elif self.region == 'mask': bbox = image.split()[1].getbbox() croppedImage = image.crop(bbox) croppedImage.load() alpha = croppedImage.split()[1] # Fill in the part of the cropped image outside the bounding box with a # uniform shade of gray grayImage = ImageChops.constant(croppedImage, 128) compositeImage = Image.composite(croppedImage, grayImage, alpha) # Equalize the composite image compositeImage = ImageOps.autocontrast(compositeImage.split()[0], cutoff=self.cutoff) # Paste the part of the equalized image within the mask back # into the cropped image croppedImage = Image.composite(compositeImage, croppedImage, alpha) croppedImage.putalpha(alpha) # Paste the cropped image back into the full image image.paste(croppedImage, bbox) elif self.region == 'all': alpha = image.split()[1] image = ImageOps.autocontrast(image.split()[0], cutoff=self.cutoff) image.putalpha(alpha) return image
def process(self, image): """ TODO check bounding box """ BaseFilter.process(self, image) image = image.convert("LA") matrix = [1, 0, self.x_axis, 0, 1, self.y_axis, 0, 0, 1] translated_image = image.transform(image.size, Image.AFFINE, matrix) # Create a new larger image to hold the translated image # It is filled with the background color and an alpha value of 0 outputImage = Image.new("LA", translated_image.size, (self.background, 0)) # Paste the translated image into the new image, using the image's # alpha channel as a mask # This effectively just fills the area around the rotation with the # background color, and imports the alpha channel from the translated image outputImage.paste(translated_image, None, translated_image.split()[1]) outputImage = outputImage.convert("L") return outputImage
def process(self, image): """ TODO check bounding box """ BaseFilter.process(self, image) image = image.convert('LA') matrix = [1, 0, self.x_axis, 0, 1, self.y_axis, 0, 0, 1] translated_image = image.transform(image.size, Image.AFFINE, matrix) # Create a new larger image to hold the translated image # It is filled with the background color and an alpha value of 0 outputImage = Image.new('LA', translated_image.size, (self.background, 0)) # Paste the translated image into the new image, using the image's # alpha channel as a mask # This effectively just fills the area around the rotation with the # background color, and imports the alpha channel from the translated image outputImage.paste(translated_image, None, translated_image.split()[1]) outputImage = outputImage.convert('L') return outputImage
def __init__(self, region='all', mode=None): """ Args: region: Options are 'all' (equalize the entire image), 'bbox' (equalize just the portion of the image within the bounding box), and 'mask' (equalize just the portion of the image within the mask). mode: ** DEPRECATED ** Alias for 'region'. """ BaseFilter.__init__(self) if mode is not None: region = mode if region not in ('all', 'bbox', 'mask'): raise RuntimeError("Not a supported region (options are 'all', 'bbox', and 'mask')") self.region = region def process(self, image): """ Args: image: The image to process. Returns: a single image, or a list containing one or more images. """ BaseFilter.process(self, image) if self.mode != 'gray': raise RuntimeError("EqualizeHistogram only supports grayscale images.") if self.region == 'bbox': bbox = image.split()[1].getbbox() croppedImage = image.crop(bbox) croppedImage.load() alpha = croppedImage.split()[1] croppedImage = ImageOps.equalize(croppedImage.split()[0]) croppedImage.putalpha(alpha) image.paste(croppedImage, bbox) elif self.region == 'mask': bbox = image.split()[1].getbbox() croppedImage = image.crop(bbox) croppedImage.load() alpha = croppedImage.split()[1] # Fill in the part of the cropped image outside the bounding box with # uniformly-distributed noise noiseArray = \ numpy.random.randint(0, 255, croppedImage.size[0]*croppedImage.size[1]) noiseImage = Image.new('L', croppedImage.size) noiseImage.putdata(noiseArray) compositeImage = Image.composite(croppedImage, noiseImage, alpha) # Equalize the composite image compositeImage = ImageOps.equalize(compositeImage.split()[0]) # Paste the part of the equalized image within the mask back # into the cropped image croppedImage = Image.composite(compositeImage, croppedImage, alpha) croppedImage.putalpha(alpha) # Paste the cropped image back into the full image image.paste(croppedImage, bbox) elif self.region == 'all': alpha = image.split()[1] image = ImageOps.equalize(image.split()[0]) image.putalpha(alpha) return image
def __init__(self, x_axis=0, y_axis=0): BaseFilter.__init__(self) self.x_axis = x_axis # horizontal self.y_axis = y_axis # vertical
def __init__(self, size=None, sizes=None, method='fit', simultaneous=False, highQuality=False): """ Args: size: Target size. Either a tuple (width, height), specifying an absolute size in pixels, or a single value, specifying a scale factor to apply to the current size. sizes: List of target sizes, for creating multiple output images for each input image. Each entry in the list must satisfy the requirements for the 'size' parameter, above. 'size' and 'sizes' may not be used together. method: Method to use for generating new images, one of: 'fit' : scale and pad to match new size, preserving aspect ratio 'crop' : scale and crop the image to fill the new size 'stretch' : stretch the image to fill new size, ignoring aspect ratio 'center' : center the original image in the new size without scaling simultaneous: Whether the images should be sent out of the sensor simultaneously. highQuality: Whether to use high-quality sampling for resizing instead of nearest neighbor. If highQuality is True, antialiasing is used for downsampling and bicubic interpolation is used for upsampling. Example usage: Resize the incoming image to fit within (320, 240) by scaling so that the image fits exactly within (320, 240) but the aspect ratio is maintained, and padding with the sensor's background color: Resize(size=(320, 240)) Scale the image to three different sizes: 100% of the original size, 50% of the original size, and 25% of the original size, and send the three images out of the sensor simultaneously as multiple scales: Resize(sizes=(1.0, 0.5, 0.25), simultaneous=True) Pad the image to fit in a larger image of size (640, 480), centering it in the new image: Resize(size=(640, 480), method='center') """ BaseFilter.__init__(self) if (not size and not sizes) or (size and sizes): raise RuntimeError("Must specify either 'size' or 'sizes'") if size: sizes = [size] if type(sizes) not in (list, tuple): raise ValueError("Sizes must be a list or tuple") if type(sizes) is tuple: sizes = list(sizes) for i, size in enumerate(sizes): if type(size) in (list, tuple): if len(size) > 2: raise ValueError("Size is too long (must be a scalar or 2-tuple)") elif type(size) in (int, float): if size <= 0: raise ValueError("Sizes must be positive numbers") sizes[i] = [size] else: raise TypeError("Sizes must be positive numbers") if method not in ('fit', 'crop', 'stretch', 'center'): raise ValueError("Unknown method " "(options are 'fit', 'crop', 'stretch', and 'center')") self.sizes = sizes self.method = method self.simultaneous = simultaneous self.highQuality = highQuality def process(self, image): """ Args: image: The image to process. Returns: a single image, or a list containing one or more images. """ BaseFilter.process(self, image) sizes = [] for i, size in enumerate(self.sizes): if len(size) == 1: # Convert scalar sizes to absolute sizes in pixels sizes.append((int(round(image.size[0]*float(size[0]))), int(round(image.size[1]*float(size[0]))))) else: sizes.append((int(size[0]), int(size[1]))) newImages = [] for size in sizes: if image.size == size: newImage = image elif self.method == 'fit': # Resize the image to fit in the target size, preserving aspect ratio targetRatio = size[0] / float(size[1]) imageRatio = image.size[0] / float(image.size[1]) if imageRatio > targetRatio: xSize = size[0] scale = size[0] / float(image.size[0]) ySize = int(scale * image.size[1]) else: ySize = size[1] scale = size[1] / float(image.size[1]) xSize = int(scale * image.size[0]) newImage = self._resize(image, (xSize, ySize)) # Pad with the background color if necessary if newImage.size != size: paddedImage = Image.new('LA', size, self.background) paddedImage.paste(newImage, ((size[0] - newImage.size[0])/2, (size[1] - newImage.size[1])/2)) newImage = paddedImage elif self.method == 'crop': # Resize the image to fill the new size targetRatio = size[0] / float(size[1]) imageRatio = image.size[0] / float(image.size[1]) if imageRatio > targetRatio: # Original image is too wide scale = size[1] / float(image.size[1]) newSize = (int(scale * image.size[0]), size[1]) cropStart = ((newSize[0] - size[0]) / 2, 0) else: # Original image is too tall scale = size[0] / float(image.size[0]) newSize = (size[0], int(scale * image.size[1])) cropStart = (0, (newSize[1] - size[1]) / 2) newImage = self._resize(image, newSize) # Crop if necessary if newSize != size: newImage = newImage.crop((cropStart[0], cropStart[1], cropStart[0] + size[0], cropStart[1] + size[1])) elif self.method == 'stretch': # Resize the image to each target size, ignoring aspect ratio newImage = self._resize(image, size) elif self.method == 'center': # Center the original image in the new image without rescaling it newImage = Image.new('LA', size, self.background) x = (size[0] - image.size[0]) / 2 y = (size[1] - image.size[1]) / 2 newImage.paste(image, (x, y)) newImages.append(newImage) if not self.simultaneous: if len(newImages) == 1: return newImages[0] else: return newImages else: return [newImages] def getOutputCount(self): """Return the number of images returned by each call to process(). If the filter creates multiple simultaneous outputs, return a tuple: (outputCount, simultaneousOutputCount). """ if not self.simultaneous: return len(self.sizes) else: return 1, len(self.sizes) def _resize(self, image, size): """Resize the image with the appropriate sampling method. """ if self.highQuality: if size < image.size: return image.resize(size, Image.ANTIALIAS) else: return image.resize(size, Image.BICUBIC) else: return image.resize(size)
def process(self, image): """ Args: image: The image to process. Returns: a single image, or a list containing one or more images. """ BaseFilter.process(self, image) sizes = [] for i, size in enumerate(self.sizes): if len(size) == 1: # Convert scalar sizes to absolute sizes in pixels sizes.append((int(round(image.size[0]*float(size[0]))), int(round(image.size[1]*float(size[0]))))) else: sizes.append((int(size[0]), int(size[1]))) newImages = [] for size in sizes: if image.size == size: newImage = image elif self.method == 'fit': # Resize the image to fit in the target size, preserving aspect ratio targetRatio = size[0] / float(size[1]) imageRatio = image.size[0] / float(image.size[1]) if imageRatio > targetRatio: xSize = size[0] scale = size[0] / float(image.size[0]) ySize = int(scale * image.size[1]) else: ySize = size[1] scale = size[1] / float(image.size[1]) xSize = int(scale * image.size[0]) newImage = self._resize(image, (xSize, ySize)) # Pad with the background color if necessary if newImage.size != size: paddedImage = Image.new('LA', size, self.background) paddedImage.paste(newImage, ((size[0] - newImage.size[0])/2, (size[1] - newImage.size[1])/2)) newImage = paddedImage elif self.method == 'crop': # Resize the image to fill the new size targetRatio = size[0] / float(size[1]) imageRatio = image.size[0] / float(image.size[1]) if imageRatio > targetRatio: # Original image is too wide scale = size[1] / float(image.size[1]) newSize = (int(scale * image.size[0]), size[1]) cropStart = ((newSize[0] - size[0]) / 2, 0) else: # Original image is too tall scale = size[0] / float(image.size[0]) newSize = (size[0], int(scale * image.size[1])) cropStart = (0, (newSize[1] - size[1]) / 2) newImage = self._resize(image, newSize) # Crop if necessary if newSize != size: newImage = newImage.crop((cropStart[0], cropStart[1], cropStart[0] + size[0], cropStart[1] + size[1])) elif self.method == 'stretch': # Resize the image to each target size, ignoring aspect ratio newImage = self._resize(image, size) elif self.method == 'center': # Center the original image in the new image without rescaling it newImage = Image.new('LA', size, self.background) x = (size[0] - image.size[0]) / 2 y = (size[1] - image.size[1]) / 2 newImage.paste(image, (x, y)) newImages.append(newImage) if not self.simultaneous: if len(newImages) == 1: return newImages[0] else: return newImages else: return [newImages] def getOutputCount(self): """Return the number of images returned by each call to process(). If the filter creates multiple simultaneous outputs, return a tuple: (outputCount, simultaneousOutputCount). """ if not self.simultaneous: return len(self.sizes) else: return 1, len(self.sizes) def _resize(self, image, size): """Resize the image with the appropriate sampling method. """ if self.highQuality: if size < image.size: return image.resize(size, Image.ANTIALIAS) else: return image.resize(size, Image.BICUBIC) else: return image.resize(size)
def process(self, image): """ Args: image: The image to process. Returns: a single image, or a list containing one or more images. """ BaseFilter.process(self, image) if not self.expand and self.targetRatio: # Pad the image to the aspect ratio of the sensor # This allows us to rotate in expand=False without cutting off parts # of the image unnecessarily # Unlike expand=True, the object doesn't get smaller ratio = (image.size[0] / float(image.size[1])) if ratio < self.targetRatio: # Make image wider size = (int(image.size[0] * self.targetRatio / ratio), image.size[1]) newImage = Image.new('LA', size, (self.background, 0)) newImage.paste(image, ((newImage.size[0] - image.size[0])/2, 0)) image = newImage elif ratio > self.targetRatio: # Make image taller size = (image.size[0], int(image.size[1] * ratio / self.targetRatio)) newImage = Image.new('LA', size, (self.background, 0)) newImage.paste(image, (0, (newImage.size[1] - image.size[1])/2)) image = newImage if self.highQuality: resample = Image.BICUBIC else: resample = Image.NEAREST outputs = [] for angle in self.angles: # Rotate the image, which expands it and pads it with black and a 0 image = image.convert('LA') # alpha value rotatedImage = image.rotate(angle, resample=resample, expand=self.expand) # Create a new larger image to hold the rotated image # It is filled with the background color and an alpha value of 0 outputImage = Image.new('LA', rotatedImage.size, (self.background, 0)) # Paste the rotated image into the new image, using the rotated image's # alpha channel as a mask # This effectively just fills the area around the rotation with the # background color, and imports the alpha channel from the rotated image outputImage.paste(rotatedImage, None, rotatedImage.split()[1]) outputs.append(outputImage.convert('L')) return outputs def getOutputCount(self): """ Return the number of images returned by each call to process(). If the filter creates multiple simultaneous outputs, return a tuple: (outputCount, simultaneousOutputCount). """ return len(self.angles)
def __init__(self, region='all', cutoff=0): """ Args: region: Options are 'all' (equalize the entire image), 'bbox' (equalize just the portion of the image within the bounding box), and 'mask' (equalize just the portion of the image within the mask) cutoff: Number of pixels to clip from each end of the histogram before rescaling it """ BaseFilter.__init__(self) if region not in ('all', 'bbox', 'mask'): raise RuntimeError("Not a supported region (options are 'all', 'bbox', and 'mask')") if type(cutoff) != int or cutoff < 0: raise RuntimeError("'cutoff' must be a positive integer") self.region = region self.cutoff = cutoff def process(self, image): """ Args: image: The image to process Returns: a single image, or a list containing one or more images """ BaseFilter.process(self, image) if self.mode != 'gray': raise RuntimeError("NormalizeContrast only supports grayscale images.") if self.region == 'bbox': bbox = image.split()[1].getbbox() croppedImage = image.crop(bbox) croppedImage.load() alpha = croppedImage.split()[1] croppedImage = \ ImageOps.autocontrast(croppedImage.split()[0], cutoff=self.cutoff) croppedImage.putalpha(alpha) image.paste(croppedImage, image.bbox) elif self.region == 'mask': bbox = image.split()[1].getbbox() croppedImage = image.crop(bbox) croppedImage.load() alpha = croppedImage.split()[1] # Fill in the part of the cropped image outside the bounding box with a # uniform shade of gray grayImage = ImageChops.constant(croppedImage, 128) compositeImage = Image.composite(croppedImage, grayImage, alpha) # Equalize the composite image compositeImage = ImageOps.autocontrast(compositeImage.split()[0], cutoff=self.cutoff) # Paste the part of the equalized image within the mask back # into the cropped image croppedImage = Image.composite(compositeImage, croppedImage, alpha) croppedImage.putalpha(alpha) # Paste the cropped image back into the full image image.paste(croppedImage, bbox) elif self.region == 'all': alpha = image.split()[1] image = ImageOps.autocontrast(image.split()[0], cutoff=self.cutoff) image.putalpha(alpha) return image
def __init__(self, x_axis=0, y_axis=0): BaseFilter.__init__(self) self.x_axis = x_axis # horizontal self.y_axis = y_axis # vertical
def __init__(self, angles=[0], expand=False, targetRatio=None, highQuality=True): """ Args: angles: List of angles by which to rotate, in degrees. expand: Whether to expand the output image to contain the entire rotated image. If False, the output image will match the dimensions of the input image, but cropping may occur. targetRatio: Ratio of the sensor. If specified, used if expand == False to grow the image to the target ratio to avoid unnecessary clipping. highQuality: Whether to use bicubic interpolation for rotating. instead of nearest neighbor. """ BaseFilter.__init__(self) self.angles = angles self.expand = expand self.targetRatio = targetRatio self.highQuality = highQuality if not expand: for i, angle in enumerate(angles): if angle != 0 and angle % 90 == 0: angles[i] -= .01 # Oh, PIL... def process(self, image): """ Args: image: The image to process. Returns: a single image, or a list containing one or more images. """ BaseFilter.process(self, image) if not self.expand and self.targetRatio: # Pad the image to the aspect ratio of the sensor # This allows us to rotate in expand=False without cutting off parts # of the image unnecessarily # Unlike expand=True, the object doesn't get smaller ratio = (image.size[0] / float(image.size[1])) if ratio < self.targetRatio: # Make image wider size = (int(image.size[0] * self.targetRatio / ratio), image.size[1]) newImage = Image.new('LA', size, (self.background, 0)) newImage.paste(image, ((newImage.size[0] - image.size[0])/2, 0)) image = newImage elif ratio > self.targetRatio: # Make image taller size = (image.size[0], int(image.size[1] * ratio / self.targetRatio)) newImage = Image.new('LA', size, (self.background, 0)) newImage.paste(image, (0, (newImage.size[1] - image.size[1])/2)) image = newImage if self.highQuality: resample = Image.BICUBIC else: resample = Image.NEAREST outputs = [] for angle in self.angles: # Rotate the image, which expands it and pads it with black and a 0 image = image.convert('LA') # alpha value rotatedImage = image.rotate(angle, resample=resample, expand=self.expand) # Create a new larger image to hold the rotated image # It is filled with the background color and an alpha value of 0 outputImage = Image.new('LA', rotatedImage.size, (self.background, 0)) # Paste the rotated image into the new image, using the rotated image's # alpha channel as a mask # This effectively just fills the area around the rotation with the # background color, and imports the alpha channel from the rotated image outputImage.paste(rotatedImage, None, rotatedImage.split()[1]) outputs.append(outputImage.convert('L')) return outputs def getOutputCount(self): """ Return the number of images returned by each call to process(). If the filter creates multiple simultaneous outputs, return a tuple: (outputCount, simultaneousOutputCount). """ return len(self.angles)
def process(self, image): """ Args: image: The image to process Returns: a single image, or a list containing one or more images """ # Get our random state back saveState = numpy.random.get_state() numpy.random.set_state(self._randomState) # Send through parent class first BaseFilter.process(self, image) alpha = image.split()[1] # ----------------------------------------------------------------------- # black and white if self.mode == 'bw': # For black and white images, our doBackground pixels are 255 and our figure pixels # are 0. pixels = numpy.array(image.split()[0].getdata(), dtype=int) noise = numpy.random.random(len(pixels)) # get array of floats from 0 to 1 if self.doForeground and self.doBackground: noise = numpy.array(noise < self.noiseLevel, dtype=int) * 255 pixels -= noise pixels = numpy.abs(pixels) else: # "Flip" self.noiseLevel percent of the foreground pixels # We only want to add noise to the figure, so we will flip some percent of the # 0 pixels. if self.doForeground: noise = numpy.array(noise < self.noiseLevel, dtype=int) * 255 pixels |= noise # "Flip" self.noiseLevel percent of the background pixels # We only want to add noise to the background, so we will flip some percent of the # 255 pixels. if self.doBackground: noise = numpy.array(noise > self.noiseLevel, dtype=int) * 255 pixels &= noise # ----------------------------------------------------------------------- # gray-scale elif self.mode == 'gray': pixels = numpy.array(image.split()[0].getdata(), dtype=int) noise = numpy.random.random(len(pixels)) # get array of floats from 0 to 1 # Add +/- self.noiseLevel to each pixel noise = (noise - 0.5) * 2 * self.noiseLevel * 256 mask = numpy.array(alpha.getdata(), dtype=int) != 0 if self.doForeground and self.doBackground: pixels += noise elif self.doForeground: pixels[mask != 0] += noise[mask != 0] elif self.doBackground: pixels[mask == 0] += noise[mask == 0] pixels = pixels.clip(min=0, max=255) else: raise "AddNoise Filter: this image mode not supported" # write out the new pixels newimage = Image.new(image.mode, image.size) newimage.putdata(pixels) newimage.putalpha(alpha) # If generating dynamic noise, change our random state each time. if self.dynamic: self._randomState = numpy.random.get_state() # Restore random state numpy.random.set_state(saveState) return newimage