Image Processing Techniques Report
Asrith Reddy Patlolla
Roll No: 2022102050
1 Introduction
This report details various image processing techniques implemented using Python,
focusing on PIL (Python Imaging Library) and NumPy for manipulating images.
Each section describes a specific technique, its implementation, key learnings,
and outputs where applicable.
2 Reading and Writing an Image
2.1 Implementation
The process involves:
1. Loading an image using PIL’s [Link]() function.
2. Converting the image to a NumPy array.
3. Converting the array back to an image.
4. Saving the resulting image.
2.2 Code Snippet
image path = ’ . / a s s e t s / check . j p g ’
image = Image . open ( image path )
i m a g e a r r a y = np . a r r a y ( image )
i m a g e f r o m a r r a y = Image . f r o m a r r a y ( i m a g e a r r a y )
output path = ’ . / a s s e t s / read writev1 . jpg ’
image from array . save ( output path )
print ( ” image i s r e a d and w r i t t e n back i n t o a image ” )
1
2.3 Learnings
This process demonstrates the fundamental workflow in image processing: load,
convert, process (optional), convert back, and save. In this case, no processing
was done, effectively preserving the original image.
Figure 1: sample image
2
Figure 2: outpupt image
3
3 Adjusting Image Brightness
3.1 Implementation
The brightness adjustment involves:
1. Loading the image and converting it to a NumPy array.
2. Multiplying each pixel value by a factor k.
3. Using nested loops to iterate through each pixel.
4. Clipping values to ensure they remain within the 0-255 range.
3.2 Code Snippet
image path = ’ . / a s s e t s / check . j p g ’
image = Image . open ( image path )
i m a g e a r r a y = np . a r r a y ( image )
k = 1.5
h e i g h t , width = i m a g e a r r a y . shape [ : 2 ]
a d j u s t e d a r r a y = np . z e r o s l i k e ( i m a g e a r r a y )
f o r i in range ( h e i g h t ) :
f o r j in range ( width ) :
a d j u s t e d a r r a y [ i , j ] = np . c l i p ( i m a g e a r r a y [ i , j ] ∗ k , 0 , 2 5 5 )
i m a g e f r o m a r r a y = Image . f r o m a r r a y ( a d j u s t e d a r r a y . a s t y p e ( np . u i n t 8 ) )
output path = ’ . / a s s e t s / brightv2 . jpg ’
image from array . save ( output path )
print ( ” b r i g h t n e s s has been a d j u s t e d ” )
3.3 Learnings
This method demonstrates pixel-wise operations and the importance of value
clamping in image processing to prevent overflow or underflow of pixel values.
3.4 Images
4
Figure 3: sample image
Figure 4: britghter image with k =1.5
5
Figure 5: lower brightness image with k =0.5
6
4 Contrast Adjustment
4.1 Implementation
The contrast adjustment process includes:
1. Calculating a histogram of pixel intensities.
2. Determining new minimum and maximum values (a low and a high) based
on a small percentage of ignored pixels.
3. Applying a linear transformation to stretch the intensity range.
4.2 Code Snippet
# F l a t t e n t h e image and s o r t t h e p i x e l v a l u e s
flattened image = image array . f l a t t e n ()
s o r t e d p i x e l s = np . s o r t ( f l a t t e n e d i m a g e )
# C a l c u l a t e t h e number o f p i x e l s t o i g n o r e on each end
t o t a l p i x e l s = len ( s o r t e d p i x e l s )
i g n o r e p i x e l s = int ( t o t a l p i x e l s ∗ i g n o r e p e r c e n t a g e )
# Find t h e low and h i g h p i x e l v a l u e s
a low = s o r t e d p i x e l s [ i g n o r e p i x e l s ]
a h i g h = s o r t e d p i x e l s [− i g n o r e p i x e l s ]
# Adjust the contras t
f o r i in range ( h e i g h t ) :
f o r j in range ( width ) :
f o r c in range ( c h a n n e l s ) :
p i x e l v a l u e = image array [ i , j , c ]
i f p i x e l v a l u e < a low :
a d j u s t e d a r r a y [ i , j , c ] = a min
e l i f pixel value > a high :
a d j u s t e d a r r a y [ i , j , c ] = a max
else :
a d j u s t e d v a l u e = a min + ( p i x e l v a l u e − a l o w ) ∗ ( ( a max − a min
a d j u s t e d a r r a y [ i , j , c ] = int (max( a min , min( a max , a d j u s t e d v a l u
This code snippet demonstrates the core functionality of the contrast ad-
justment algorithm:
1. First, it flattens the image array and sorts all pixel values. This allows us
to easily identify threshold values based on percentiles of the pixel value
distribution.
7
2. It then calculates how many pixels to ignore at each end of the distribution,
based on the ignore percentage. This helps exclude extreme outliers
that could skew the contrast adjustment.
3. The low and high threshold values (a low and a high) are determined by
selecting values from the sorted array at positions corresponding to the
ignore percentage.
4. The main adjustment loop iterates over each pixel and channel in the
image:
• Pixels below a low are set to the minimum value (a min).
• Pixels above a high are set to the maximum value (a max).
• Pixels between a low and a high are linearly stretched to fill the full
range from a min to a max.
5. The linear stretching is performed using the formula:
amax − amin
adjusted value = amin + (pixel value − alow ) ·
ahigh − alow
This maps the input range [alow , ahigh ] to the output range [amin , amax ].
6. The final adjusted value is clamped to the range [0, 255] to ensure it’s a
valid 8-bit pixel value.
This approach effectively enhances the contrast of the image by expanding
the most common range of pixel values to cover the full available dynamic range,
while avoiding issues with extreme outliers.
4.3 Learnings
This technique improves upon simple linear contrast stretching by ignoring ex-
treme outliers, resulting in a more balanced contrast enhancement.
4.4 Images
8
Figure 6: sample image
9
Figure 7: image with better contrast
10
5 Color to Grayscale Conversion
5.1 Implementation
Four methods were implemented:
1. Average method: (R + G + B) / 3
2. Luminosity method: 0.2989 * R + 0.5870 * G + 0.1140 * B
3. Lightness method: (max(R, G, B) + min(R, G, B)) / 2
4. Single channel method: Using only one color channel (e.g., green)
5.2 Code Snippet
image path = ’ . / a s s e t s / l a n d s c a p e . j p g ’
image = Image . open ( image path )
i m a g e a r r a y = np . a r r a y ( image )
h e i g h t , width , c h a n n e l s = i m a g e a r r a y . shape
gray image a r r a y a v g = np . z e r o s ( ( h e i g h t , width ) , dtype=np . u i n t 8 )
gray image a r r a y l u m = np . z e r o s ( ( h e i g h t , width ) , dtype=np . u i n t 8 )
gray image a r r a y l i g h t = np . z e r o s ( ( h e i g h t , width ) , dtype=np . u i n t 8 )
gray image a r r a y s i n g l e = np . z e r o s ( ( h e i g h t , width ) , dtype=np . u i n t 8 )
def average method ( i m a g e a r r a y , g r a y image array ) :
f o r i in range ( h e i g h t ) :
f o r j in range ( width ) :
r , g , b = image array [ i , j ] . a s t y p e ( np . i n t 3 2 )
gray value = ( r + g + b) // 3
gray image array [ i , j ] = gray value
def l u m i n o s i t y m e t h o d ( i m a g e a r r a y , g r a y i m a g e a r r a y ) :
f o r i in range ( h e i g h t ) :
f o r j in range ( width ) :
r , g , b = image array [ i , j ]
g r a y v a l u e = int ( 0 . 2 9 8 9 ∗ r + 0 . 5 8 7 0 ∗ g + 0 . 1 1 4 0 ∗ b )
gray image array [ i , j ] = gray value
def l i g h t n e s s m e t h o d ( i m a g e a r r a y , g r a y i m a g e a r r a y ) :
f o r i in range ( h e i g h t ) :
f o r j in range ( width ) :
r , g , b = i m a g e a r r a y [ i , j ] . a s t y p e ( np . i n t 3 2 )
g r a y v a l u e = (max( r , g , b ) + min( r , g , b ) ) // 2
gray image array [ i , j ] = gray value
11
def s i n g l e c h a n n e l m e t h o d ( i m a g e a r r a y , g r a y i m a g e a r r a y , c h a n n e l =1):
f o r i in range ( h e i g h t ) :
f o r j in range ( width ) :
gray image array [ i , j ] = image array [ i , j , channel ]
average method ( i m a g e a r r a y , g r a y i m a g e a r r a y a v g )
luminosity method ( image array , gray image array lum )
lightness method ( image array , g r a y i m a g e a r r a y l i g h t )
s i n g l e c h a n n e l m e t h o d ( i m a g e a r r a y , g r a y i m a g e a r r a y s i n g l e , c h a n n e l =1)
# Using g r e e n c h a n n e l
gray i m a g e a v g = Image . f r o m a r r a y ( g r a y i m a g e a r r a y a v g )
gray i m a g e l u m = Image . f r o m a r r a y ( g r a y i m a g e a r r a y l u m )
gray i m a g e l i g h t = Image . f r o m a r r a y ( g r a y i m a g e a r r a y l i g h t )
gray i m a g e s i n g l e = Image . f r o m a r r a y ( g r a y i m a g e a r r a y s i n g l e )
output path avg = ’ ./ a s s e t s / grey avg landscape . jpg ’
output path lum = ’ . / a s s e t s / grey lum landscape . jpg ’
output path light = ’ ./ a s s e t s / g r e y l i g h t l a n d s c a p e . jpg ’
output pa th si ngl e = ’ ./ a s s e t s / g r e y s i n g l e l a n d s c a p e . jpg ’
gray image avg . save ( output path avg )
gray image lum . save ( output path lum )
gray i m a g e l i g h t . save ( o u t p u t p a t h l i g h t )
gray i m a g e s i n g l e . save ( o u t p u t p a t h s i n g l e )
print ( ” c o l o u r t o g r e y i s done ” )
5.3 Learnings
Each method produces slightly different results:
• The average method is simple but doesn’t account for human perception.
• The luminosity method considers human perception of color.
• The lightness method can produce more extreme results.
• The single channel method can be useful for specific applications but may
lose important information.
5.4 Images
12
Figure 8: sample image
Figure 9: Average meth
13
Figure 10: Luminosity method
Figure 11: Lightness method
14
Figure 12: Single channel method
15
6 Grayscale to Pseudo-color
6.1 Implementation
This process involves:
1. Defining a color mapping function with multiple color points.
2. Interpolating between these points based on the grayscale value.
3. Applying this mapping to each pixel of the grayscale image.
6.2 Code Snippet: Sophisticated Pseudo-Color Mapping
def s o p h i s t i c a t e d c o l o r m a p ( g r a y v a l u e ) :
color points = [
(0 , (0 , 0 , 0)) , # Black
(64 , (0 , 0 , 255)) , # Blue
( 1 2 8 , ( 0 , 2 5 5 , 0 ) ) , # Green
( 1 9 2 , ( 2 5 5 , 2 5 5 , 0 ) ) ,# Y e l l o w
(255 , (255 , 0 , 0)) # Red
]
f o r i in range ( len ( c o l o r p o i n t s ) − 1 ) :
i f g r a y v a l u e <= c o l o r p o i n t s [ i + 1 ] [ 0 ] :
t = ( gray value − color points [ i ] [ 0 ] ) / ( color points [ i +1][0] − colo
r = int ( c o l o r p o i n t s [ i ] [ 1 ] [ 0 ] + t ∗ ( c o l o r p o i n t s [ i + 1 ] [ 1 ] [ 0 ] − c o l o r
g = int ( c o l o r p o i n t s [ i ] [ 1 ] [ 1 ] + t ∗ ( c o l o r p o i n t s [ i + 1 ] [ 1 ] [ 1 ] − c o l o r
b = int ( c o l o r p o i n t s [ i ] [ 1 ] [ 2 ] + t ∗ ( c o l o r p o i n t s [ i + 1 ] [ 1 ] [ 2 ] − c o l o r
return ( r , g , b )
return c o l o r p o i n t s [ − 1 ] [ 1 ]
g r a y i m a g e = Image . open ( image path ) . c o n v e r t ( ’L ’ )
g r a y i m a g e a r r a y = np . a r r a y ( g r a y i m a g e )
h e i g h t , width = g r a y i m a g e a r r a y . shape
p s e u d o c o l o r i m a g e a r r a y = np . z e r o s ( ( h e i g h t , width , 3 ) , dtype=np . u i n t 8 )
f o r i in range ( h e i g h t ) :
f o r j in range ( width ) :
gray value = gray image array [ i , j ]
r , g , b = sophisticated color map ( gray value )
pseudo color image array [ i , j ] = [ r , g , b ]
p s e u d o c o l o r i m a g e = Image . f r o m a r r a y ( p s e u d o c o l o r i m a g e a r r a y )
pseudo color image . save ( output path )
16
This code snippet demonstrates a sophisticated pseudo-color mapping algo-
rithm:
1. The sophisticated color map function defines a color gradient with five
key points:
• 0: Black (0, 0, 0)
• 64: Blue (0, 0, 255)
• 128: Green (0, 255, 0)
• 192: Yellow (255, 255, 0)
• 255: Red (255, 0, 0)
2. For each gray value, the function determines which segment of the gradient
it falls into and calculates a smooth transition between the two nearest
color points.
3. The transition is calculated using linear interpolation:
gray value − color pointi
t=
color pointi+1 − color pointi
channel value = color pointi + t · (color pointi+1 − color pointi )
Where channel value is calculated separately for R, G, and B.
4. The main process loads a grayscale image and creates a new RGB image
array of the same size.
5. It then iterates over each pixel in the grayscale image:
• Retrieves the gray value of the pixel.
• Passes this value to the sophisticated color map function to get
the corresponding RGB color.
• Assigns this RGB color to the corresponding pixel in the new image
array.
6. Finally, it creates an image from the new array and saves it as a pseudo-
color representation of the original grayscale image.
This approach creates a visually appealing pseudo-color image by mapping
grayscale values to a smooth, multi-point color gradient. It enhances the visual
distinction between different intensity levels in the original grayscale image.
6.3 Learnings
This technique can be used to enhance the visual representation of grayscale
images, making subtle differences more apparent through color.
6.4 Images
17
Figure 13: sample image
Figure 14: output image
18
7 Green Screen Replacement
7.1 Implementation
The process includes:
1. Loading two images: the foreground (with green screen) and the back-
ground.
2. Defining a range of green colors to be replaced.
3. Replacing pixels within this green range with corresponding pixels from
the background image.
7.2 Code Snippet: Green Screen Replacement
image path1 = ’ . / a s s e t s / t h o r . j p g ’
image1 = Image . open ( image path1 ) . c o n v e r t ( ’RGB ’ )
image path2 = ’ . / a s s e t s / c o n s t r u c t i o n . j p g ’
image2 = Image . open ( image path2 ) . c o n v e r t ( ’RGB ’ )
i m a g e a r r a y 1 = np . a r r a y ( image1 )
i m a g e a r r a y 2 = np . a r r a y ( image2 )
h e i g h t 1 , width1 = i m a g e a r r a y 1 . shape [ : 2 ]
h e i g h t 2 , width2 = i m a g e a r r a y 2 . shape [ : 2 ]
def m a n u a l r e s i z e ( i m a g e a r r a y , ne w h ei g ht , new width ) :
o l d h e i g h t , o l d w i d t h = i m a g e a r r a y . shape [ : 2 ]
r e s i z e d i m a g e = np . z e r o s ( ( n ew h ei g ht , new width , 3 ) , dtype=np . u i n t 8 )
f o r i in range ( n e w h e i g h t ) :
f o r j in range ( new width ) :
o r i g i = int ( i ∗ o l d h e i g h t / n e w h e i g h t )
o r i g j = int ( j ∗ o l d w i d t h / new width )
resized image [ i , j ] = image array [ o r i g i , o r i g j ]
return r e s i z e d i m a g e
i f ( h e i g h t 1 , width1 ) != ( h e i g h t 2 , width2 ) :
i m a g e a r r a y 2 = m a n u a l r e s i z e ( i m a g e a r r a y 2 , h e i g h t 1 , width1 )
a d j u s t e d a r r a y = np . z e r o s l i k e ( i m a g e a r r a y 1 )
g r e e n l o w e r = np . a r r a y ( [ 0 , 1 0 0 , 0 ] )
g r e e n u p p e r = np . a r r a y ( [ 1 0 0 , 2 5 5 , 1 0 0 ] )
f o r i in range ( h e i g h t 1 ) :
f o r j in range ( width1 ) :
p i x e l = image array1 [ i , j ]
19
i f np . a l l ( p i x e l >= g r e e n l o w e r ) and np . a l l ( p i x e l <= g r e e n u p p e r ) :
adjusted array [ i , j ] = image array2 [ i , j ]
else :
adjusted array [ i , j ] = image array1 [ i , j ]
i m a g e f r o m a r r a y = Image . f r o m a r r a y ( a d j u s t e d a r r a y . a s t y p e ( np . u i n t 8 ) )
image from array . save ( output path )
This code snippet demonstrates a green screen replacement algorithm:
1. Two images are loaded: a foreground image (presumably with a green
screen) and a background image.
2. A manual resizing function is defined:
• It uses nearest-neighbor interpolation to resize an image.
• The scaling factor is calculated as:
old height old width
scalei = , scalej =
new height new width
• Each pixel in the new image is mapped to the closest pixel in the
original image.
3. If the images are not the same size, the background image is resized to
match the foreground image.
4. Green screen replacement is performed:
• Green color range is defined:
lower = [0, 100, 0], upper = [100, 255, 100]
• For each pixel in the foreground image:
– If the pixel color falls within the green range, it’s replaced with
the corresponding pixel from the background image.
– Otherwise, the original pixel is kept.
5. The resulting image is created from the adjusted array and saved.
This approach replaces the green screen in the foreground image with the
background image. The manual resizing function ensures that the background
image fits the dimensions of the foreground image, while the color-based re-
placement selectively swaps pixels based on their green component.
The green screen detection uses a simple color range check:
is green = (0 ≤ R ≤ 100) ∧ (100 ≤ G ≤ 255) ∧ (0 ≤ B ≤ 100)
This method provides a basic green screen effect, though it may struggle
with subtle green variations or green elements that should be preserved in the
foreground image.
20
7.3 Learnings
This technique demonstrates the principle behind chroma key compositing used
in video production. The challenge lies in accurately defining the ”green” range
to avoid artifacts.
7.4 Images
Figure 15: Green screen image image
Figure 16: Background Image
21
Figure 17: output image after replacement
22
8 Video Processing
8.1 Implementation
Two main functions were implemented:
1. video to images(): Extracts frames from a video file.
2. images to video(): Combines a sequence of images into a video.
8.2 Code Snippet: Video Processing
import cv2
def v i d e o t o i m a g e s ( v i d e o p a t h ) :
cap = cv2 . VideoCapture ( v i d e o p a t h )
images = [ ]
while True :
r e t , frame = cap . r e a d ( )
i f not r e t :
break
images . append ( frame )
cap . r e l e a s e ( )
return images
def i m a g e s t o v i d e o ( images , ou tp u t p at h , f p s =30):
i f not images :
r a i s e V a l u e E r r o r ( ”No images t o w r i t e t o v i d e o . ” )
h e i g h t , width , = images [ 0 ] . shape
f o u r c c = cv2 . V i d e o W r i t e r f o u r c c ( ∗ ’XVID ’ )
out = cv2 . VideoWriter ( ou t pu t p at h , f o u r c c , f p s , ( width , h e i g h t ) )
f o r image in images :
out . w r i t e ( image )
out . r e l e a s e ( )
v i d e o p a t h = ’ . / a s s e t s / v i d . mp4 ’
output path = ’ . / a s s e t s / output vid . avi ’
images = v i d e o t o i m a g e s ( v i d e o p a t h )
i m a g e s t o v i d e o ( images , o u t p u t p a t h )
This code snippet demonstrates video processing using OpenCV:
1. The video to images function:
23
• Opens a video file using [Link].
• Iteratively reads frames from the video until the end is reached.
• Stores each frame in a list.
• Returns the list of frames (images).
2. The images to video function:
• Takes a list of images, an output path, and a frames per second (fps)
value.
• Creates a VideoWriter object with the following parameters:
– Output path
– Codec (XVID in this case)
– FPS
– Frame size (width and height)
• Writes each image in the list to the video.
• Releases the VideoWriter object.
3. The main process:
• Defines input video path and output video path.
• Calls video to images to extract frames from the input video.
• Calls images to video to create a new video from the extracted
frames.
This approach allows for frame-by-frame processing of videos.
This method provides a way to manipulate videos on a frame-by-frame basis,
allowing for various image processing techniques to be applied to individual
frames before reconstructing the video.
8.3 Learnings
This process illustrates that videos are essentially sequences of images, and
understanding this allows for frame-by-frame processing of video content.
9 Fade Transition Effect
9.1 Implementation
The fade effect is created by:
1. Loading two images.
2. Gradually blending them over a series of frames.
3. Writing these blended frames to a video file.
24
9.2 Code Snippet
import cv2
image1 path = ’ . / a s s e t s / check . j p g ’
image2 path = ’ . / a s s e t s / g r e y l u m v 1 . j p g ’
image1 = Image . open ( image1 path ) . c o n v e r t ( ’RGB ’ )
image2 = Image . open ( image2 path ) . c o n v e r t ( ’RGB ’ )
i f image1 . s i z e != image2 . s i z e :
r a i s e V a l u e E r r o r ( ” Images must be t h e same s i z e . ” )
i m a g e 1 a r r a y = np . a r r a y ( image1 )
i m a g e 2 a r r a y = np . a r r a y ( image2 )
f p s = 30
duration = 1
total frames = fps ∗ duration
o u t p u t p a t h = ’ . / a s s e t s / f a d e t r a n s i t i o n v 1 . mp4 ’
f o u r c c = cv2 . V i d e o W r i t e r f o u r c c ( ∗ ’ mp4v ’ )
v i d e o w r i t e r = cv2 . VideoWriter ( o ut p ut p at h , f o u r c c , f p s , ( image1 . width , image1 . h
Here, the images are converted to NumPy arrays for pixel-wise operations.
The code also defines the video parameters, such as frames per second (‘fps‘),
duration, and output path. It initializes a ‘VideoWriter‘ object to create a video
file.
f o r n in range ( t o t a l f r a m e s ) :
t = n / ( t o t a l f r a m e s − 1)
b l e n d e d i m a g e a r r a y = ( ( 1 − t ) ∗ i m a g e 1 a r r a y + t ∗ i m a g e 2 a r r a y ) . a s t y p e ( np .
frame = cv2 . c v t C o l o r ( b l e n d e d i m a g e a r r a y , cv2 .COLOR RGB2BGR)
v i d e o w r i t e r . w r i t e ( frame )
video writer . release ()
print ( ”Fade t r a n s i t i o n has been done ” )
This loop generates the fade transition by blending the two images over the
specified number of frames. The blend ratio (‘t‘) gradually changes from 0 to
1, creating a smooth transition from ‘image1‘ to ‘image2‘. Each blended frame
is written to the video file. After processing all frames, the video file is saved,
and a confirmation message is printed.
25
9.3 Learnings
This technique demonstrates how simple mathematical operations can create
smooth visual transitions between images.
9.4 Images
Figure 18: First image
26
Figure 19: second Image
27
10 Conclusion
These implementations cover a wide range of image processing techniques, from
basic manipulations to more complex effects. Each method offers insights into
different aspects of digital image representation and manipulation, providing a
foundation for more advanced image processing applications.
28