Skip to content

Latest commit

 

History

History
438 lines (291 loc) · 17.5 KB

EXPLANATIONS.md

File metadata and controls

438 lines (291 loc) · 17.5 KB

Digital Image - Bitmap processing tool

To go back to the description of those features -> Click here

Table of Contents

Feature Implementations

We will mainly use Colored Lena to explain the features.

We don't need to specify the image directory wether it is located within images directory.

--bmp image_name.bmp

Each bitmap images has 1 or 3 color channels. It means that we have 2-dimensional arrays or 3-dimensional arrays.

  • The first dimension contains the image height
  • The second dimension contains the image width
  • The third dimension contains pixels written on 3 bytes [255 255 255] (blue green red)

Generate each feature within a test directory

Back to description

--test_features command allows you to generated all features that requires output.

It automatically saves every generated images to this path: images/test/{feature-name}/{image-name}


Display Bitmap header

Back to description

--bmp image_name.bmp command displays the input bitmap image header which contains information about its signature, file size, image size, image height, image length, color palette and so on.

bitmap order and size:

  • The header size (offbits) is 54 bytes.
  • The palette size is variable.
  • The image part starts after the palette size if it exists.

Rotate image

Back to description

--rotate {90, 180, 270} command allows you to rotate an image by rotating bytes along its axis 0 and 1 (height and width) by 90° or 190° or 270°.


Resize image

Back to description

--resize ratio_value command allows you to resize an image by a ratio value. It means that the new image size will be: (height x ratio_value, width x ratio_value)

--resize height_value width_value command allows to resize an image by specifying a dimension. It means that the new image dimension will be: (height_value, width_value)

to resize the image, we browse the entire original image and copy the pixels into the new image of different dimensions.


Contrast adjustment

Back to description

--contrast contrast_value command allows you to generate a new image after modifying the contrast of the image by updating each color channel of each pixel through this formula:

factor = (259 * (contrast + 255)) / (255 * (259 - contrast))

contrast value is defined within [0, 255] interval


Color to grayscale

Back to description

--grayscale sepia command allows you to generated a new sepia image by applying a specific formula for each color channels.

green channel = round(0.272*pixel[2] + 0.534*pixel[1] + 0.131*pixel[0])

blue channel = round(0.349*pixel[2] + 0.686*pixel[1] + 0.168*pixel[0])

red channel = round(0.393*pixel[2] + 0.769*pixel[1] + 0.189*pixel[0])

--grayscale mean command allows you to generated a new mean grayscale image by applying a specific formula for each color channels.

all channels = (pixel[0] + pixel[1] + pixel[2])/3

--grayscale luminance command allows you to generated a new grayscale image depending on luminance by applying a specific formula for each color channels.

all channels = (0.0722channel_luminance(pixel[0]/255) + 0.7152channel_luminance(pixel[1]/255) + 0.2126*channel_luminance(pixel[2]/255)) * 255 

To better understand the difference between mean and luminance method

Original

Mean method and Luminance method

Luminance method is much more accurate than mean method.


Color to black & white (binary)

Back to description

--blackwhite command allows you to generated a new binary (Black=0, White=255) image by applying a specific formula for each color channels.

all channels = 255 if mean(pixel[0] + pixel[1] + pixel[2]) > 127

all channels = 0 if mean(pixel[0] + pixel[1] + pixel[2]) <= 127


Color to negative

Back to description

--negative command allows you to generated a new negative image by applying a specific formula for each color channels.

all channels = 255 - pixel_value


Keep color channel

back to description

--colorchannel {g, b, r, gb, gr, br} command allows you to generated a new image where only 1 or 2 color channels are kept by inhibiting color channels that are not wanted. That is to say by setting up inhibited color to 0 value (black).

green-image = inhibit blue + red channels

blue-image = inhibit red + green channels

red-image = inhibit green + blue channels

green-blue-image = inhibit red channel

green-red-image = inhibit blue channel

red-blue-image = inhibit green channel


Brightness adjustment

back to description

--brightness brightness_value command allows you to generate a new image after modifying the brightness of the image by updating each color channel of each pixel through this formula:

all pixels = brightness + pixel

brightness value is defined within [0, 255] interval


Flip image

back to description

--flip command allows you to generate a new flipped image by flipping every image pixels along its axis=1 (vertical).


Convolution matrix

General intuition

Convolution matrices allow you to apply filters in image processing. For example the edge detection filter, blur filter, edge reinforcement filter or even the emboss filter.

The principle of the convolution matrix is ​​to go through all the pixels of the original image (except the pixels located at the edge of the image) and to apply the “kernel” or “mask” matrix.

For each kernel applied, the value of the new pixel is recalculated for each of its channels. In this example, we apply the same kernel for each of these channels.

The 3x3 kernel applied in this example is: 
[[0 1 2],
 [2 2 0],
 [0 1 2]]

Considering that the image covered by the kernel is: 
[[a b c],
 [d e f],
 [g h i]]

The calculation for each new pixels goes like this:
new pixel = 0*a + 1*b + 2*c + 2*d + 2*e + 0*f + 0*g + 1*h + 2*i


My implementation

My method is much faster than general intuition. Let's start by giving you some intuition.

As before, in order to apply the convolution, we will scan the original image pixel by pixel and calculate the convolution product. This method is long, especially as the image is large!

So how can you speed up the process?

I chose to use memory. Actually, if the original image is of width w and height h, I decided not to do (w-2) * (h-2) iterations to which is multiplied by 3 * 3 iterations for each image pixel iterations to apply the kernel but I instead chose to only do 9 iterations.

Let's take an example


Imagine having an image which is: 2400 x 1600 (width x height). Convolution matrix application would have needed: ((2400-2) * (1600-2)) * (3*3) = 34 488 036 total iterations.

How does it works?

Convolution matrix | filter(input_image, kernel)


1. Take an image of dimension 2400 x 1600 (width x height) as input. 2. Initialize a list of 9 empty 3-dimension array of the size of the input image. -> The shape of this list would be a 4-dimension array such as (9, 2400, 1600, 3). - 9 copies - 2400 width - 1600 height - 3 color channels 3. Kernel application a. for each kernel indexes (i,j) make a copy of the input image shifted by 1 pixel depending on kernel indexes (i,j). b. during this process, add also the kernel value by multiplying all the copied image by the value of the kernel at index (i, j). -> By creating shifted image copies, pixels end up outside the image. There are several methods to manage the edges. - add black pixels (pixel value = 0) along the edges of the image. - use the carousel method by rolling pixels. -> I chose to use the carousel method. It means that the convolution matrix which use pixels located on the edge of the image will depend on the pixels located opposite the image. c. Finally, sum the 9 copies of shifted images in order to obtain our input image again but with the application of the convolution matrix with the kernel.

Filter: Edge-detection

back to description

--filter edge-detection command allows you to apply the edge detection filter on the input image through convolution matrix application.

In order to correctly detect the edges of the image, you need to apply two kernels to detect horizontal edges and vertical edges of the image.

See my convolution matrix filter implementation

horizontal_kernel: [[1, 2, 1], 
                    [0, 0, 0], 
                    [-1, -2, -1]]
          
Vertical_kernel: [[-1, 0, 1], 
                  [-2, 0, 2], 
                  [-1, 0, 1]]

Edge-detection filter application:


1. Apply filter function with horizontal and vertical kernel res1 = filter(input_image, horizontal_kernel) res2 = filter(input_image, vertical_kernel) 2. combine both res1 and res2 in order to get the edge-detected image generated by applying this formula: new_image = sqrt(res12 + res22)
  • Horizontal kernel and Vertical kernel separated

  • Horizontal kernel and Vertical kernel combined


Filter: Edge-reinforcement

back to description

--filter edge-reinforcement command allows you to apply the edge reinforcement filter on the input image through convolution matrix application.

See my convolution matrix filter implementation

reinforcement_kernel: [[0, -1, 0], 
                       [-1, 5, -1], 
                       [0, -1, 0]]
              

Edge-reinforcement filter application:


1. Apply filter function with reinforcement kernel new_image = filter(input_image, reinforcement_kernel)

Filter: Blur

back to description

--filter blur command allows you to apply the blur filter on the input image through convolution matrix application.

See my convolution matrix filter implementation

blur_kernel: [[1/256, 4/256, 6/256, 4/256, 1/256],
              [4/256, 16/256, 24/256, 16/256, 4/256],
              [6/256, 24/256, 36/256, 24/256, 6/256],
              [4/256, 16/256, 24/256, 16/256, 4/256],
              [1/256, 4/256, 6/256, 4/256, 1/256]]
              

blur filter application:


1. Apply filter function with blur kernel new_image = filter(input_image, blur_kernel)

Filter: Emboss

back to description

--filter emboss command allows you to apply the emboss filter on the input image through convolution matrix application.

See my convolution matrix filter implementation

emboss_kernel: [[-2, -1, 0], 
                [-1, -1, -1], 
                [0, 1, 2]]
              

Emboss filter application:


1. Apply filter function with emboss kernel new_image = filter(input_image, emboss_kernel)

Overlay two images

back to description

--overlay {maximum, minimum} command allows you to mix two images together.

1. Before covering one image with another, they must have the same dimension. 
   To do this, we do a preprocessing step to resize one image so that it is the same size as the other. 
2. We go through the whole dimension of an image and we recover either the maximum or the minimum of the two images depending on the {option} in order to build the new image. 

Image colorization

back to description

--colorize {hue_value} command allows you to colorize an image by changing its hue. Hue value is a number defined within [0°, 360°] interval.

In order to be able to modify the hue parameter of an image, we must change the base and switch from the RGB color space to HSV color space.

  • byte order within a pixel for RGB color space: [Blue, Green, Red]
  • byte order within a pixel for HSV color space: [Brightness, Saturation, Hue]

Image colorization application:


1. Convert RGB color space to HSV color space 2. Edit Hue channel of each pixel by hue_value 3. Convert back from HSV color space to RGB color space

Photomaton

back to description

--photomaton {value} command allows you to create a 'Photomaton' from an image by shifting pixels on the new image.

The images must be square, that is to say image height = image width

{value} corresponds to the number of time we will split the image into 4 parts.


New pixel position table

Position in line Position in column New pixel position
Even Even Top Left
Even Odd Top Right
Odd Even Bottom Left
Odd Odd Bottom Right

Photomaton application:


1. Let's do some preprocessing, you have to resize the input image in square shape. 2. Browse every pixel of the original image and a new pixel will be appened to the new image according to the New pixel position table.