Image processing is a CPU intensive task. It involves processing on large arrays. Hence when you are implementing your Image Processing algorithm, you algorithm needs to be highly efficient.
The type of operation that can be applied on an Image can be classified into three categories.
Operation Type | Characteristics | Complexity per pixel |
---|---|---|
Point Operations | The output pixel value at specific location is dependent only at the input value of pixel at that location. | |
Local Operations | The output pixel value at specific location is dependent on the input values in the neighborhood of that coordinate. ( P is the size of neighborhood Kernel ) | |
Global Operations | The output value at specific location is dependent on all the pixels of input Image. |
The complexity for all type of operations is shown in the above table. To apply any type of operation , it is required to iterate over all the pixels and access the pixel value.
Suppose we are applying Point Operation on a gray (single channel) image of resolution 640 x 480. Then total number of 307200 calculations needs to be done.
If you ever tried to loop over an image using python, you know that it is painfully slow operation.
Why accessing individual pixel in python is slow?
In Python, OpenCV images are stored as NumPy arrays.Numpy operations are implemented in C. This allows us to avoid the expensive overhead of python loops and provide performance gain by multiple orders of magnitude as compare to Python List.But this performance gain can only be achieved if we can frame our problem as a vector operations using numpy arrays.When we are implementing something from scratch,we use python for loop and loose much of performance gain.
Fast Pixel Processing in Python
Let's try to implement basic Gamma Correction to an Image.Gamma Correction is known as Power Law Transform where
Iout = Iin ^ ( 1 / G )
Where Iin is our input image , G is our gamma value and Iout is our output Image.Gamma value < 1 will make the image darker.
Gamma value > 1 will make the image appear lighter.
Gamma value = 1 will have no effect on input Image.
![]() |
Original image |
![]() |
Gamma Corrected Image |
Let's quickly dive into code.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import cv2
import numpy as np
import time
img = cv2.imread("scene1.jpg") #load image
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #convert to grayscale
We import our necessary packages , we import time module to measure and compare the performance of different optimization methods.
Next we load an image and convert it to gray scale.
Now we will apply different technique and measure th time taken and create functions that takes gray image and value of Gamma as input and return Gamma Corrected Image.
The time taken will linearly vary with input image size.
Method 1 : Direct element access.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def gammaCorrect1(im , gamma ):
# create a blank output Image
outImg = np.zeros(im.shape,im.dtype)
rows,cols = im.shape
for i in range(rows):
for j in range(cols):
outImg[i][j] = ( (im[i][j]/255.0) ** (1/gamma) )*255
return outImg
Time : 2.0738 sec Method 2 : Using item() and itemset()
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def gammaCorrect2(im , gamma ):
# create a blank output Image
outImg = np.zeros(im.shape,im.dtype)
rows,cols = im.shape
for i in range(rows):
for j in range(cols):
gammaValue = ( (im.item(i,j)/255.0) ** (1/gamma) ) * 255
outImg.itemset(i,j,gammaValue)
return outImg
Time : 0.4229 secMethod 3 : Using Look Up Table
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#using Look UP table with numpy item and itemset
def gammaCorrect3(im , gamma ):
# create a blank output Image
outImg = np.zeros(im.shape,im.dtype)
rows,cols = im.shape
# create a lookup table
LUT = []
for i in range(256):
LUT.append( ( (i/255.0) ** (1/gamma) ) * 255 )
for i in range(rows):
for j in range(cols):
gammaValue = LUT[im.item(i,j)]
outImg.itemset(i,j,gammaValue)
return outImg
Time : 0.2109 sec
Method 4 : Using numpy Vector method.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#using numpy vectors
def gammaCorrect4(im, gamma):
# create a blank output Image
outImg = np.zeros(im.shape, im.dtype)
rows, cols = im.shape
# create a lookup table
LUT = []
for i in range(256):
LUT.append(((i / 255.0) ** (1 / gamma)) * 255)
LUT = np.array(LUT,dtype=np.uint8)
outImg = LUT[im]
return outImg
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#using numpy vectors | |
def gammaCorrect4(im, gamma): | |
# create a blank output Image | |
outImg = np.zeros(im.shape, im.dtype) | |
rows, cols = im.shape | |
# create a lookup table | |
LUT = [] | |
for i in range(256): | |
LUT.append(((i / 255.0) ** (1 / gamma)) * 255) | |
LUT = np.array(LUT,dtype=np.uint8) | |
outImg = LUT[im] | |
return outImg |
Time : 0.0004649 sec
So its great , a massive improvement of performance.Hence we can increase the speed of our pixel-by-pixel loop access to 4500 times than direct element access method. Hope you enjoyed it, feel free for any discussion and feedback in comments section.
Downloads :
To download the code: Click Here (Link to Github Repository)
Thanks.
So its great , a massive improvement of performance.Hence we can increase the speed of our pixel-by-pixel loop access to 4500 times than direct element access method. Hope you enjoyed it, feel free for any discussion and feedback in comments section.
Downloads :
To download the code: Click Here (Link to Github Repository)
Thanks.
Comments
Post a Comment