def robertsFilters(image): """ Apply Roberts filters in X and Y directions Parameters ---------- image: ndarray((h, w, 3)) Returns ------- ndarray((h, w, 3)) """ colourers.info(f'Applying Roberts filter in X and Y directions') Kx = np.array([[1, 0], [0, -1]], np.float32) Ky = np.array([[0, 1], [-1, 0]], np.float32) Ix = optimizedConv2D(image, Kx) Iy = optimizedConv2D(image, Ky) G = np.hypot(Ix, Iy) G = G / G.max() * 255 theta = np.arctan2(Iy, Ix) return (G, theta)
def kirschFilters(image): """ Apply Kirsch filters in X and Y directions Parameters ---------- image: ndarray((h, w, 3)) Returns ------- ndarray((h, w, 3)) """ colourers.info(f'Applying Kirsch filter in X and Y directions') Kx = np.array([[-3, -3, 5], [-3, 0, 5], [-3, -3, 5]], np.float32) Ky = np.array([[-3, -3, -3], [-3, 0, -3], [5, 5, 5]], np.float32) Ix = optimizedConv2D(image, Kx) Iy = optimizedConv2D(image, Ky) G = np.hypot(Ix, Iy) G = G / G.max() * 255 theta = np.arctan2(Iy, Ix) return (G, theta)
def nonMaxSuppression(image, D): """ Remove irrelevant pixels using the matrix of angles Parameters ---------- image: ndarray((h, w, 3)) D: ndarray((h, w, 3)) Returns ------- ndarray((h, w, 3)) """ colourers.info( f'Removing non maxima pixels using gradient directions matrix') M, N, dim = image.shape Z = np.zeros((M, N, dim), dtype=np.int32) angle = D * 180. / np.pi angle[angle < 0] += 180 for i in range(1, M - 1): for j in range(1, N - 1): try: q = 255 r = 255 # angle 0 if all( np.logical_or( [all(0 <= angle[i, j]), all(angle[i, j] < 22.5)], [ all(157.5 <= angle[i, j]), all(angle[i, j] <= 180) ])): q = image[i, j + 1] r = image[i, j - 1] # angle 45 elif all((22.5 <= angle[i, j] < 67.5)): q = image[i + 1, j - 1] r = image[i - 1, j + 1] # angle 90 elif all((67.5 <= angle[i, j] < 112.5)): q = image[i + 1, j] r = image[i - 1, j] # angle 135 elif all((112.5 <= angle[i, j] < 157.5)): q = image[i - 1, j - 1] r = image[i + 1, j + 1] if all(np.logical_and((image[i, j] >= q), (image[i, j] >= r))): Z[i, j] = image[i, j] else: Z[i, j] = 0, 0, 0 except IndexError as e: pass return Z
def hysteresis(image, weakPixel, strongPixel): """ Applying hysteresis for reducing errors and sharpening edges. Parameters ---------- image: ndarray((h, w, 3)) weakPixel: int strongPixel: int Returns ------- ndarray((h, w, 3)) """ colourers.info( f'Performing hysteresis using weak pixel of {weakPixel} and strong pixel of {strongPixel}' ) M, N, dim = image.shape weak = weakPixel strong = strongPixel for i in range(1, M - 1): for j in range(1, N - 1): if all(image[i, j] == weak): try: if all( np.logical_or( np.logical_or( np.logical_or( (image[i + 1, j - 1] == strong), (image[i + 1, j] == strong)), np.logical_or( (image[i + 1, j + 1] == strong), (image[i, j - 1] == strong))), np.logical_or( np.logical_or( (image[i, j + 1] == strong), (image[i - 1, j - 1] == strong)), np.logical_or( (image[i - 1, j] == strong), (image[i - 1, j + 1] == strong))))): image[i, j] = strong, strong, strong else: image[i, j] = 0, 0, 0 except IndexError as e: pass return image
def threshold(image, lowThresholdRatio=0.05, highThresholdRatio=0.09, weakPix=25, strongPix=255): """ Double thresholding the image by using the low and high threshold and the weak and strong pixel values. Parameters ---------- image: ndarray((h, w, 3)) lowThresholdRatio: float highThresholdRatio: float weakPix: int strongPix: int Returns ------- ndarray((h, w, 3)) """ colourers.info( f'Performing double threshold to detect weak and strong pixels with a threshold of {lowThresholdRatio}-{highThresholdRatio}' ) highThreshold = image.max() * highThresholdRatio lowThreshold = highThreshold * lowThresholdRatio M, N, dim = image.shape res = np.zeros((M, N, dim), dtype=np.int32) weak = np.int32(weakPix) strong = np.int32(strongPix) iStrong, jStrong, _ = np.where(image >= highThreshold) iZeros, jZeros, _ = np.where(image < lowThreshold) iWeak, jWeak, _ = np.where((image <= highThreshold) & (image >= lowThreshold)) res[iStrong, jStrong] = strong, strong, strong res[iWeak, jWeak] = weak, weak, weak return res
def gaborKernel(size, sigma=8.0, theta=np.pi/4, lam=10.0, gamma=0.5, psi=3): """ Generate a gabor kernel depending on the size, sigma, theta, lambda, gamma, psi respectively about the Gabor kernel formula. Parameters ---------- size: int sigma: float theta: float lam: float gamma: float psi: float Returns ------ ndarray((h, w)) """ sigmaX = sigma sigmaY = float(sigma) / gamma xmax = max(abs(size * sigmaX * np.cos(theta)), abs(size * sigmaY * np.sin(theta))) xmax = np.ceil(max(1, xmax)) ymax = max(abs(size * sigmaX * np.sin(theta)), abs(size * sigmaY * np.cos(theta))) ymax = np.ceil(max(1, ymax)) xmin = -xmax ymin = -ymax (y, x) = np.meshgrid(np.arange(ymin, ymax + 1), np.arange(xmin, xmax + 1)) xTheta = x * np.cos(theta) + y * np.sin(theta) yTheta = -x * np.sin(theta) + y * np.cos(theta) gb = np.exp(-.5 * (xTheta ** 2 / sigmaX ** 2 + yTheta ** 2 / sigmaY ** 2)) * np.cos(2 * np.pi / lam * xTheta + psi) colourers.info(f'Creating gabor kernel of size {gb.shape[0]} with sigma of {sigma}') return gb
def gaussianKernel(size, sigma=1): """ Generate a gaussian kernel depending on the size and the sigma Parameters ---------- size: int sigma: float Returns ------ ndarray((h, w)) """ colourers.info(f'Creating gaussian kernel of size {size} with sigma of {sigma}') size = int(size) // 2 x, y = np.mgrid[-size:size+1, -size:size+1] normal = 1 / (2.0 * np.pi * sigma**2) g = np.exp(-((x**2 + y**2) / (2.0 * sigma ** 2))) * normal return g
import os, sys, re from os.path import dirname, join, abspath sys.path.insert(0, abspath(join(dirname(__file__), '..'))) from processors import printers as Printers, transformers as Transformers, filters as Filters from utils import helpers as hp, colourers, gaussianKernel, gaborKernel from formats import BMP, PNG import shutil shutil.rmtree('output', ignore_errors=True) os.mkdir('output') colourers.info('Using lena_couleur.bmp as a testing image') filename = '../lena_couleur.bmp' colourers.info('Creating the BMP class from filename') bmp = BMP(filename) colourers.success('Created BMP class successfully') colourers.info('Header printing') Printers.printHeader(bmp) colourers.success('Header printed successfully') colourers.info('Printing the color of the first pixel') Printers.printPixel(bmp, 0, 0) colourers.success('Successfully printed the color of the first pixel') colourers.info('Printing the histogram of the image') Printers.printHistogram(bmp) colourers.success('Successfully printed the histogram')
def imageProcessing(): """ Process a given image in parameter (BMP or PNG) """ # Parser initialization parser = argparse.ArgumentParser(description=colourers.toCyan('Image processor for reading/writing images into BMP/PNG formats and applying transformations on it.')) # Formats Parser group = parser.add_argument_group(colourers.toGreen('formats')) formatParser = group.add_mutually_exclusive_group(required=True) formatParser.add_argument('--bmp', type=str, metavar=colourers.toRed('<bmp file name>'), help=colourers.toMagenta('bmp file to parse')) formatParser.add_argument('--png', type=str, metavar=colourers.toRed('<png file name>'), help=colourers.toMagenta('png file to parse')) # Printers Parser group = parser.add_argument_group(colourers.toYellow('printers')) printers = group.add_mutually_exclusive_group() printers.add_argument('--header', help=colourers.toMagenta('print the file format header'), action='store_true') printers.add_argument('--print-color', '-pc', type=int, nargs=2, metavar=(colourers.toRed('<width>'), colourers.toRed('<height>')), help=colourers.toMagenta('pixel to print')) printers.add_argument('--histogram', action='store_true', help=colourers.toMagenta('print histogram associated')) printers.add_argument('--output', '-o', type=str, metavar=colourers.toRed('<output file>'), help=colourers.toMagenta('image output file')) # Transformers Parser transformers = parser.add_argument_group(colourers.toBlue('transformers')) transformers.add_argument('--half', action='store_true', help='applying the filter on one half of the image') transformers.add_argument('--rotate', '-r', type=int, choices=[90, 180, 270], metavar=colourers.toRed('<degree of rotation>'), help=colourers.toMagenta('rotate the image')) transformers.add_argument('--scale', '-s', type=int, nargs='+', action=required_length(1, 2), metavar=(colourers.toRed('<scaleRatio> | [<width>'), colourers.toRed('<height>')), help=colourers.toMagenta('scale/shrink the image')) transformers.add_argument('--contrast', '-c', type=float, metavar=colourers.toRed('<contrast factor>'), help=colourers.toMagenta('apply a factor contrast')) transformers.add_argument('--grayscale', '-gs', action='store_true', help=colourers.toMagenta('to grayscale image')) transformers.add_argument('--binary', '-b', action='store_true', help=colourers.toMagenta('to binary image')) transformers.add_argument('--invert', '-i', action='store_true', help=colourers.toMagenta('to inverted image, equivalent to --contrast -1')) transformers.add_argument('--channel', type=str, choices=['blue', 'green', 'red'], metavar=colourers.toRed('<channel>'), nargs='+', action=required_length(1, 2), help=colourers.toMagenta('to the specified channel')) # Filters Parser filters = parser.add_argument_group(colourers.toCyan('filters')) filters.add_argument('--edge-detection', '-ed', type=str, choices=['canny', 'sobel', 'prewitt', 'roberts', 'kirsch'], metavar=colourers.toRed('<filter name>'), help=colourers.toMagenta('perform an edge detection')) filters.add_argument('--retrieve-color', '-rv', action='store_true', help=colourers.toMagenta('retrieve the colors of a grayscale image')) filters.add_argument('--edge-enhancement', '-ee', action='store_true', help=colourers.toMagenta('applying increased edge enhancement filter')) filters.add_argument('--sharpen', action='store_true', help=colourers.toMagenta('sharpening the image')) filters.add_argument('--unsharp', action='store_true', help=colourers.toMagenta('unsharp the image')) filters.add_argument('--denoise', action='store_true', help=colourers.toMagenta('denoise the image')) filters.add_argument('--texture-detection', '-td', action='store_true', help=colourers.toMagenta('applying texture detection (Gabor Filter)')) filters.add_argument('--blur', type=str, choices=['simple', 'more', 'average', 'gaussian', 'motion'], metavar=colourers.toRed('<type of blur>'), help=colourers.toMagenta('perform the selected blur')) filters.add_argument('--blur-iteration', '-bi', type=int, default=1, metavar=colourers.toRed('<number of iteration>'), help=colourers.toMagenta('apply N times the blur function')) filters.add_argument('--emboss', action='store_true', help=colourers.toMagenta('perform an embossing filter')) filters.add_argument('--overlap', type=str, nargs='+', metavar=colourers.toRed('<image to overlap>'), help=colourers.toMagenta('overlap an image given on the selected image')) # Args parsing args = parser.parse_args() filename = "" # BMP Block if args.bmp: filename = args.bmp if not os.path.isfile(filename): colourers.error('"{}" does not exist !'.format(filename)) sys.exit(-1) colourers.success('Success Opening {}...'.format(filename)) bmp = BMP(filename) half = args.half if args.print_color: width, height = args.print_color colourers.info(f'Printing pixel color of ({width}, {height})') Printers.printPixel(bmp, width, height) sys.exit(0) elif args.header: colourers.info(f'Printing BMP header of {bmp.filename}') Printers.printHeader(bmp) sys.exit(0) elif args.histogram: colourers.info(f'Printing color histogram of {bmp.filename}') Printers.printHistogram(bmp) sys.exit(0) if (args.rotate or args.scale or args.contrast or args.grayscale or args.binary or args.channel or args.edge_detection or args.retrieve_color or args.edge_enhancement or args.blur or args.emboss or args.overlap or args.texture_detection or args.denoise or args.sharpen or args.unsharp): if not hp.atLeastOne(args.output, ( args.rotate, args.scale, args.contrast, args.grayscale, args.binary, args.channel, args.edge_detection, args.retrieve_color, args.edge_enhancement, args.blur, args.emboss, args.overlap, args.texture_detection, args.denoise, args.sharpen, args.unsharp )): parser.error('--rotate/--scale/--contrast/--grayscale/--binary/--channel/--edge-detection/--retrieve-color/--edge-enhancement/--blur/--emboss/--overlap/--texture-detection/--denoise/--sharpen/--unsharp and --output must be given together') if args.rotate: degree = args.rotate colourers.info(f'Rotating image to {degree} degree') bmp.imageData = Transformers.rotate(bmp, degree) if args.scale: if len(args.scale) == 2: width, height = args.scale colourers.info(f'Scaling image to {width}x{height} pixels') bmp.imageData = Transformers.scale(bmp, height, width) else: scaleRatio = args.scale[0] colourers.info(f'Scaling image to {scaleRatio} scale ratio') height = int(hp.readLittleEndian(bmp.height)) width = int(hp.readLittleEndian(bmp.width)) bmp.imageData = Transformers.scale(bmp, height * scaleRatio, width * scaleRatio) if args.contrast: factor = args.contrast colourers.info(f'Applying a factor contrast of {factor}') bmp.imageData = Transformers.contrast(bmp, factor) if args.grayscale: colourers.info(f'Applying grayscale mask to the image') bmp.imageData = Transformers.grayscale(bmp, half) if args.binary: colourers.info(f'Applying binary mask to the image') bmp.imageData = Transformers.binary(bmp, half) if args.invert: colourers.info(f'Inverting image colours') bmp.imageData = Transformers.invert(bmp, half) if args.channel: if len(args.channel) == 2: c1, c2 = args.channel colourers.info(f'Keeping only {c1} and {c2} channels of the image') bmp.imageData = Transformers.toChannel(bmp, [c1, c2], half) else: channel = args.channel[0] colourers.info(f'Keeping only {channel} channel of the image') bmp.imageData = Transformers.toChannel(bmp, channel, half) if args.denoise: colourers.info(f'Denoising the image') bmp.imageData = Filters.wienerFilter(bmp.imageData, gaussianKernel(9, sigma=0.33), K=10) if args.texture_detection: colourers.info(f'Applying texture detection (Gabor Filter)') bmp.imageData = Filters.gaborFilter(bmp.imageData, gaborKernel(0)) if args.edge_enhancement: colourers.info(f'Applying increased edge enhancement filter') bmp.imageData = Filters.iee(bmp.imageData) if args.edge_detection: filterName = args.edge_detection if filterName == 'canny': colourers.info(f'Performing Canny filter for edge detection') bmp.imageData = Filters.ced(bmp.imageData, sigma=0.33, kernelSize=9, weakPix=50) if filterName == 'sobel': colourers.info(f'Performing Sobel filter for edge detection') bmp.imageData = Filters.sed(bmp.imageData, sigma=0.33, kernelSize=9) if filterName == 'prewitt': colourers.info(f'Performing Prewitt filter for edge detection') bmp.imageData = Filters.ped(bmp.imageData, sigma=0.33, kernelSize=9) if filterName == 'roberts': colourers.info(f'Performing Roberts filter for edge detection') bmp.imageData = Filters.red(bmp.imageData, sigma=0.33, kernelSize=9) if filterName == 'kirsch': colourers.info(f'Performing Kirsch filter for edge detection') bmp.imageData = Filters.ked(bmp.imageData, sigma=0.33, kernelSize=9) if args.sharpen: colourers.info(f'Sharpening the image') bmp.imageData = Filters.sharpen(bmp.imageData) if args.unsharp: colourers.info(f'Unsharpening the image') bmp.imageData = Filters.unsharp(bmp.imageData) if args.retrieve_color: colourers.info(f'Retrieving color') bmp.imageData = Filters.retrieveColor(bmp.imageData) if args.blur: blurType = args.blur colourers.info(f'Performing a {blurType} blur') for _ in range(args.blur_iteration): blurFunc = Filters.blur.switcher.get(blurType) bmp.imageData = blurFunc(bmp.imageData) if args.emboss: colourers.info(f'Performing emboss filter') bmp.imageData = Filters.emboss(bmp.imageData) if args.overlap: overlappers = [] for ov in args.overlap: overlappers.append(BMP(ov).imageData) colourers.info(f'Performing an overlapping between {bmp.filename} and {args.overlap}') bmp.imageData = Filters.overlap(bmp.imageData, overlappers) if args.output: outputFile = args.output hp.saveBMP(bmp, bmp.imageData, outputFile) colourers.success(f'Succesfully saved into {outputFile}') sys.exit(0) parser.error('Give at least one more argument') # PNG Block else: filename = args.png if not os.path.isfile(filename): print('"{}" does not exist'.format(filename), file=sys.stderr) sys.exit(-1) print('Success Opening {}...'.format(filename)) png = PNG(filename)