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
Exemple #8
0
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')
Exemple #9
0
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)