To go back to the description of those features -> Click here
- Digital Image - Bitmap processing tool
- Table of Contents
- Feature Implementations
- Generate each feature within a test directory
- Display Bitmap header
- Rotate image
- Resize image
- Contrast adjustment
- Color to grayscale
- Color to black & white (binary)
- Color to negative
- Keep color channel
- Brightness adjustment
- Flip image
- Convolution matrix
- Filter: Edge-detection
- Filter: Edge-reinforcement
- Filter: Blur
- Filter: Emboss
- Overlay two images
- Image colorization
- Photomaton
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)
--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}
--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 {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 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 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
--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.
--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]) > 127all channels = 0 if mean(pixel[0] + pixel[1] + pixel[2]) <= 127
--negative
command allows you to generated a new negative image by applying a specific formula for each color channels.
all channels = 255 - pixel_value
--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 channelsblue-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 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
command allows you to generate a new flipped image by flipping every image pixels along its axis=1 (vertical).
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 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.
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?
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
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]]
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
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]]
1. Apply filter function with reinforcement kernel new_image = filter(input_image, reinforcement_kernel)
--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]]
1. Apply filter function with blur kernel new_image = filter(input_image, blur_kernel)
--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]]
1. Apply filter function with emboss kernel new_image = filter(input_image, emboss_kernel)
--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.
--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]
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 {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.
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 |
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.