Experiments with images #3 – Pixel frequency plotter, greyscale conversion & thresholding

Image : Output of a poor thresholding algorithm for color to binary image conversion

Date (s) : Multiple days [Unaccounted]

Time : Unaccounted

In my previous post, I had written about my experiments with pseudo-random images with integers & prime integers b/w 0-255. As I had mentioned in my previous post, I would be covering pixel frequency generator in this post. Along with it, I would also be reviewing other normal topics, I have covered such as color to greyscale image conversion, greyscale to binary conversion & pixel to integer composition & integer to pixel decomposition. I will also be posting the output of above programs :

  1. Pixel frequency generator : It generates frequency plots for the image using a library called as matplotlib. It’s quite simple to make, though it takes lots of time for processing large images (e.g. An image w/ a resolution of 480*640 would contain 307,200 pixels. Bigger the resolution, more time it will take.). Here is it’s program :
#Plotting frequency of pixel color in a given image
#Created by asxyzp (Aashish Panigrahi)
import matplotlib.pyplot as plt
import cv2
import numpy
RGBArr = []                     #Will Store RGB pixel list for conversion into hex value
RGBHex = []                     #Will Store RGB hex values for plotting
PixFrq =[]                      #Will store frequency for each pixel
tempArr = []                    #Temporary array for storing pixel values in a 1-D list 
'''
    Name : RGBToHex(RGBList)
    Utility : Conversion of list [R,G,B] into a hexadecimal value
    Parameter(s) : RGBList - A list storing pixel values [R,G,B]
    Return value : Hex - Hexadecimal value corresponding to [R,G,B] pixel
'''
def RGBToHex(RGBList):
    R = hex(RGBList[0])
    G = hex(RGBList[1])
    B = hex(RGBList[2])
    return R+G[2:4]+B[2:4]
'''
    Name : RGBArrToRGBHex()
    Utility : Convets & stores values of RGBArr to RGBHex
    Parameter(s) : None
    Return value : None
'''
def RGBArrToRGBHex():
    for i in range(len(RGBArr)):
        RGBHex.append(RGBToHex(RGBArr[i]))
'''
    Name : FreqPlot()
    Utility : Generates frquency plot for each pixel
    Parameters(s) : None
    Return value : None
'''
def FreqPlot():
    countArr = []
    for i in range(len(RGBHex)):
        countArr.append(i)
    '''
    #Plotting points
    plt.plot(countArr,PixFrq,'k.')
    plt.xlabel("Pixel values")
    plt.ylabel("Frequency")
    plt.grid()
    plt.show()
    '''
    #Plotting bar graph
    plt.bar(countArr,PixFrq)
    plt.xlabel("Pixel values")
    plt.ylabel("Frequency")
    plt.grid()
    plt.show()
'''
    Name : PixFreq(ImgFile)
    Utility : Computes the frequency of [R,G,B] lists from an image & stores it in PixFreq
    Parameter(s) : ImgFile - Name of the image file for which stats have to be formed
    Return value : None
'''
def PixFreq(ImgFile):
    ImgFrame = cv2.imread(ImgFile,cv2.IMREAD_COLOR)     #Reading Image
    ImgRowLen = len(ImgFrame)                           #Stores the length of row of pixels
    ImgColLen = len(ImgFrame[0])                        #Stores the length of col of pixels
    
    #For storing pixel values in tempArr
    #2D list to 1D list conversion of pixels
    for i in range(ImgRowLen):                          
        for j in range(ImgColLen):
            tempArr.append(numpy.ndarray.tolist(ImgFrame[i][j]))
            #numpy.ndarray.tolist() converts numpy array to list
    
    for i in range(len(tempArr)):
        if tempArr[i] not in RGBArr:                    #Stores the value of unique value in the list  
            RGBArr.append(tempArr[i])                   
    
    #Stores freq. of each pixel
    for i in range(len(RGBArr)):
        count = 0
        for j in range(len(tempArr)):
            if RGBArr[i] == tempArr[j]:
                count+=1
        PixFrq.append(count)
    
    RGBArrToRGBHex()                                   #Conversion of [R,G,B] to hex
    FreqPlot()                                         #Frequency plot
ImgName =  input("Name of image file/path (w/ file type. e.g. PNG/JPEG)?\t")
PixFreq(ImgName)

There are two sets of output for the above program : One from an image file asxyzp.jpeg & another from a pseudo-random image, as described in the previous post.

1. Output from pseudo-random image : As you can see below, the frequency represented in both the plots is a straight line, which means that the frequency of all pixels in the image is roughly the same.

2. Output from asxyzp.jpeg : Here, you can find, something really fascinating : While most pixels have a frequency in b/w 0-500, there are pixels which have a frequency above 2500.

2. Pixel to integer composition & integer to pixel decomposition : Being more curious, I wondered whether an image which is composed of thousands of pixel lists/arrays can be converted into a list/array of integers, because once we have an integer array/list, then we can do other kinds of experiments with it, such as matrix operations. And finally, today I made the program which does the composition of pixel to integers & vice versa, but initially I was a bit confused & I had to take help from mathematics stack exchange. The program for this is below :

#Program to compose & decompose pixel to integer & integer to pixel, respectively
#Created by asxyzp (Aashish Panigrahi)
import cv2
import numpy
PhotoFrame = []                 #Stores photo frame
PhotoNum   = []                 #Stores integer values after composition of pixel to an integer
NumDecomp  = []                 #Stores decomposed pixel lists             
'''
    Name : PixToInt(Pix)
    Utility : Conversion of a Pixel to an integer
    Parameter(s) : Pix - A numpy array/list which contains pixel [R,G,B] list
    Return value : An integer value after composition 
'''
def PixToInt(Pix):
    return Pix[0]*pow(2,16) + Pix[1]*pow(2,8) + Pix[2]*pow(2,0)     #Composition [REFER: MATH STACKEX]
'''
    Name : IntToPix(Int)
    Utility : Conversion of an integer
    Parameter(s) : Int - An integer for pixel decomposition
    Return value : A list/numpy array containing pixel images
'''
def IntToPix(Int):
    R = Int//pow(2,16)                                             #R Decomposition
    Int = Int - R*pow(2,16)
    G = Int//pow(2,8)                                              #G Decomposition
    B = Int - G*pow(2,8)                                           #B Decomposition
    return list([R,G,B])
'''
    Name : RowPixToInt(ImgRow)
    Utility : Converting a row of [R,G,B] pixels into a row of integers by composition
    Parameter : ImgRow : A Row of pixels of an image
    Return value : None
'''
def RowPixToInt(ImgRow):
    Row = []
    for pixel in ImgRow:
        Int = PixToInt(pixel)   #Converts Pixels into Integers
        Row.append(Int)
    PhotoNum.append(list(Row))
'''
    Name : RowIntToPix(NumRow)
    Utility : Converting a row of integers into a row of Pixels by decomposition
    Parameter : NumRow : A row of integers for decomposition
    Return value : None
'''
def RowIntToPix(NumRow):
    Row = []
    for num in NumRow:         #Converts Numbers into Pixels
        Pix = IntToPix(num)
        Row.append(list(Pix))
    NumDecomp.append(list(Row))
'''
    Name : ImgArr(FileName)
    Utility : Converting an image into a list
    Parameter : FileName : Name of file of the image
    Return value : None
'''
def ImgArr(FileName):
    PhotoFrame = cv2.imread(FileName,cv2.IMREAD_COLOR)
    for PixRow in PhotoFrame:
        RowPixToInt(PixRow)
'''
    Name : ArrImg(ArrName)
    Utility : Converting an array into an image
    Parameter : ArrName : Name of the list/numpy array storing the photo frame
    Return value : None
'''
def ArrImg(ArrName):
    for NumRow in ArrName:
        RowIntToPix(NumRow)
    cv2.imwrite("DEC"+FileName,numpy.asarray(NumDecomp))
FileName = input("Name of image file/path (w/ file type. e.g. PNG/JPEG)?\t")
ImgArr(FileName)
ArrImg(PhotoNum)

NOTE : I really wanted to move ahead with programs upon matrix operations, but then I am already a month late, for completing CS50.

Here there is one input & one output & they are exactly same.

3. Color to greyscale image conversion : Okay, this one is one of the most common digital image processing techniques. Here, I learnt about two new things :

a. Greyscale images. Earlier, I used to think that greyscale images = b/w images, but it’s not true. B/W images or Bitonal/Binary images only have two pixel values, either [0,0,0] or [255,255,255], unlike greyscale images which represents the intensity of a pixel, which varies from 0 to 255.

b. Human eyes perceive color intensity differently for different colors & this has led to development of Luma method :

Grey = Red * 0.3 + Green * 0.59 + Blue * 0.11

Here’s the program for color to greyscale conversion :

#Description: Converting RGB image to Greyscale/Grayscale image
#Author(s)  : asxyzp
import cv2
import sys
import os.path
'''
    Name : RGBToGrey(FileName)
    Utility : RGB to Greyscale converter program
    Parameter(s) : FileName is the name of the image file for which the conversion has to be done
    Return value : None
'''
def RGBToGrey(FileName):
    if os.path.isfile(FileName):                            #Checking whether the image exists or not
        Frame = cv2.imread(FileName,cv2.IMREAD_COLOR)       #Stores image into a photo frame
        RowSize = len(Frame)                                
        ColSize = len(Frame[0])
        for i in range(RowSize):
            for j in range(ColSize):
                Grey = 0.3*Frame[i][j][0] + 0.59*Frame[i][j][1] + 0.11*Frame[i][j][2] #Formula for RGB to Greyscale conversion
                Frame[i][j][0] = Grey
                Frame[i][j][1] = Grey
                Frame[i][j][2] = Grey
        FileName="Grey_"+FileName
        cv2.imwrite(FileName,Frame)
    else:
        print("Image file doesn't exists.\nEnter proper file name.")
        sys.exit(1)
FileName = input("Name of image file for greyscale conversion (w/ file type)?\t")
RGBToGrey(FileName)

Input & output for the above program :

4. Greyscale to Binary image conversion : Obviously, after color to greyscale conversion, the next step would be greyscale to binary conversion & I tried to do it using two algorithms & both failed :

a. Algorithm 1:

if grey <= 127:
   red = 0
   green = 0
   blue = 0
else:
   red = 255
   green = 255
   blue = 255

b. Algorithm 2: Similar to algorithm 1, but replacing 127 by an average value of all pixels.

Here’s the program for algorithm 1:

#Description: Converting greyscale image to binary image
#Author(s)  : asxyzp
import os.path
import sys
import cv2
'''
    Name : GreyToBinary(FileName)
    Utility : Converts GreyScale image to Binary image
    Parameter(s) : FileName, which is the name of the image file
    Return value : None 
'''
def GreyToBinary(FileName):
    if os.path.isfile(FileName):                            #Checking whether the image exists or not
        Frame = cv2.imread(FileName,cv2.IMREAD_COLOR)       #Stores image into a photo frame
        RowSize = len(Frame)                                
        ColSize = len(Frame[0])
        for i in range(RowSize):
            for j in range(ColSize):
                if (Frame[i][j][0]<=127):                    #Converting all pixels w/ intensity below 127 to Black
                    Frame[i][j][0] = 0
                    Frame[i][j][1] = 0
                    Frame[i][j][2] = 0
                elif (Frame[i][j][0]>127):                  #Converting all pixels w/ intensity above 127 to Black
                    Frame[i][j][0] = 255
                    Frame[i][j][1] = 255
                    Frame[i][j][2] = 255
        FileName="Bin_"+FileName
        cv2.imwrite(FileName,Frame)
    else:
        print("Image file doesn't exists.\nEnter proper file name.")
        sys.exit(1)
FileName = input("Name of image file for greyscale conversion (w/ file type)?\t")
GreyToBinary(FileName)

Output of the above mentioned algorithms :

1. For algorithm 1:

2. For algorithm 2:

After these two mistakes, I started searching for, two different ways to make this conversion happen & then I came across thresholding. I would be implementing thresholding later wards, but this is the last blog of this small series of blogs on playing around with images. Thank you. Bye.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s