Final Code

The aim of the project was to measure the height in pixels of a person from a given image, as I mentioned in a previous post that the challenge we faced was on Dr Mick’s image and any image that has a noise in the background, Safiah and Aaron tried various ways to get rid of the background on Dr Mick’s image, in the end Aaron succeeded, and was able to develop a code that worked well on Dr Mick’s image, and would be very useful to use on images with background noises.

To finalise our project, we decided to put the two methods together into one code, and allow the user to choose what method suits him, in order to do that I added a user menu, so he can choose if the picture that he wishes to measure the height from has a white background or has a noise in the background, and an if else statement will run the method that the user chooses [1], the final code is provided below:


'''

Image Processing Group Project
"Determine height of a person in pixels from an image"

Group members:
    Aaron Costello
    Ruba Bait Bin Saleem
    Safiah Sadeq

Objective:
    - To calculate the Height of a person in Pixals using various image processing methods in python to achieve that goal
	- Allowing the user to choose between two options listed using an If else statment 

**************************************** Option 1 ****************************************
Sample image used: 1man.JPG - 2man.JPG - 3man.JPG

Script used to identify image height in pixels, using Contours to get the highest and the lowest points to calculate the height

Steps:
	- Importing the necessary packages
	- Accessing a pixels value to get the Blue Green and Red colour values
	- Setting the upper and lower range values wanted for each colour
	- Setting the mask in function B using the ranges set
	- Deducting the White from the Mask B
	- Using the contours function to outline the man in the white background image
	- Getting the max contour area
	- From it using tuples the extreme top (extTop) and extrem bottom (extBot) point are obtained
	- A Blue dot is drawn at the extreme top point and a Green dot is drawn at the extreme bottom point
	- From the coordination the x and y values are extracted
	- The y values from the bottom and top coordinations are subtracted from each other to get the height
	- The values obtained are then printed on screen and the image is shown on screen

**************************************** Option 2 ****************************************
Sample image provided: DrMick.jpg

Script used to identify image height in pixels, utilising HOG feature detector + Canny edge detection

Concept:
	- Read image and convert to grayscale
	- Initialize HOG descriptor and feed in image classification SVM
	- Pass in accurate parameters
	- Adjust sliding window values / scale to optimise detection
	- draw_detections function draws border surrounding the findings of the HOG descriptor
	- Inside of border taken and canny is applied to detect edges of person
	- Iterate through edges, find lowest point to retrieve Y-value of feet and the height is found

The HOG Descriptor: 

The Histogram of Gradients is utilised to find not only the x and y gradiants but the magnitude. This is useful in finding more
information regarding our edges and corners in the image in order to learn about the shapes contained in it. This is fed into
an SVM (getDefaultPeopleDetector) which contains algorithms used to detect features.

Parameters:
	 winStride - defines the step size in x and y values of the sliding window
     padding - defines the amount of pixels our region is padded with PRIOR to the HOG descriptor actually being applied
     scale - the image is downsized and smoothed through multiple layers,  the greater the scale the less accurate detections may be yet
     performance is faster due to a smaller amount of layers. Ultimately, the sliding window as defined in winStride will iterate over
     the image at different layers and how many layers there are is defined here. 

draw_border():

	This function contains the processing done for drawing the border around out detections, Canny edge detection, and finding the
	height in pixels.

	Input:
		img - our source image
		rects - this contains our x and y coordinates, along with the width and height that was returned from HOG
		thickness - the thickness of our border we will draw

	We iterate over each detection, and adjust the frame size of our border (pad_w, pad_h) due to the HOG descriptor
	tending to over exaggerate.

	cv2.rectangle takes in:
		- our image to draw on (passed into the function)
		- our start and end X & Y values
		- BGR value of rectangle (Green in this case)
		- thickness of border, set to 1 as defined at the start of the function

	This draws the border around the detected person. 

	Accessing the pixels defined in the border, this image is cropped leaving only our ROI using the following format
		- cropped_region = original[bounding_y_vals:bounding_x_vals]
	A small addition to the top, and subtraction from the bottom is made (+2 and -3), to reduce the gap between the head of the person and
	the feet from the edge of the image

	A threshold of min and max val of 100 and 200 is applied, before utilising Canny edge detection to disern the human shape
	without ay remaining noise.

	non_zero_points holds all the points in the image that are an edge, effectively where the value is not 0

	Due to non_zero_points being an array of tuples (x and y coords) we access the i part of the array, holding the Y values. By
	using np.max, with find the top of the head. Due to the feet being the starting point, this will give us the height of our person
	in pixels

'''
#######################################################################

# import the necessary packages:
import numpy as np
import cv2
import imutils
from matplotlib import pyplot as plt
from matplotlib import image as image
import easygui

# Opening the images

f = easygui.fileopenbox()
img = cv2.imread(f)
Original = img.copy() 

#######################################################################

print("---------------- Height Measurment Tool ----------------")
print("	The Type of image:")
print("1. White background image.")
print("2. Image with a noise in the background.")
print("Note: Press q to exit option 2.")
option = int(input("option choosed: "))

#######################################################################

if option == 1:

# Accessing a pixel's value:
    b,g,r = img[1,1]

    lowerRange = (b-25, g-25, r-25)
    upperRange = (b+50, g+50, r+50)

    B = cv2.inRange(img, lowerRange, upperRange)

    B=255-B

    B, contours,_ = cv2.findContours(B, mode = cv2.RETR_EXTERNAL, method = cv2.CHAIN_APPROX_NONE)

    img = cv2.drawContours(img, contours, contourIdx = -1, color = (0,0,255), thickness = 5)

    c = max(contours, key=cv2.contourArea)

    extTop = tuple(c[c[:, :, 1].argmin()][0])
    cv2.circle(img = img, center = extTop, radius = 5, color = (255,0,0), thickness = -1)
    extBot = tuple(c[c[:, :, 1].argmax()][0])
    cv2.circle(img = img, center = extBot, radius = 5, color = (0,255,0), thickness = -1)

    x_Top, y_Top = extTop
    x_Bot, y_Bot = extBot

    height = (y_Bot-y_Top)

    print("---------------- Printing Results ----------------")
    print(" The top point: ", extTop)
    print(" The bottom point: ", extBot)
    print(" The height of the person in pixels: ", height)

# show the output image
    cv2.imshow("Result", img)
    cv2.waitKey(0)

#######################################################################

elif option == 2:
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    def draw_border(img, rects, thickness = 1):
        for x, y, w, h in rects:
            # Make the rectangle smaller due to inflated HOG results
            pad_w, pad_h = int(0.15*w), int(0.02*h)
            cv2.rectangle(img, (x+pad_w, y+pad_h), (x+w-pad_w, y+h-pad_h), (0, 255, 0), thickness)
            #crop out contents contained within our rectangle with adjusted height values
            drmick = img[y+pad_h+2:y+h-pad_h-3,x+pad_w+2:x+w-pad_w-3]
            #Apply Canny Edge detection
            edges = cv2.Canny(drmick,100,200)
            cv2.imshow("Canny Edges",edges)

            non_zero_points = np.where(edges != [0])
            maxval = np.max(non_zero_points[0])

            print("---------------- Printing Results ----------------")
            print "Height in pixels of image = " + str(maxval)

    hog = cv2.HOGDescriptor()
    hog.setSVMDetector( cv2.HOGDescriptor_getDefaultPeopleDetector() )
    found,w=hog.detectMultiScale(img, winStride=(4,4), padding=(8,8), scale=1.10)
    draw_border(img,found)

    while True:
	    cv2.imshow("HOG Results", img)
	    key = cv2.waitKey(0)

	    if key == ord("r"):
	    	I = Original.copy()
	    elif key == ord("q"):
		    break
######################################################################

else:
    print("Good bye!")

 

Reference:

[1]”Python if, if…else, if…elif…else and Nested if Statement”, Programiz.com, 2018. [Online]. Available: https://www.programiz.com/python-programming/if-elif-else. [Accessed: 25- Nov- 2018].

Height Measurement

The final step in the project will be to get the height in pixels, after obtaining the extreme top and extreme bottom coordinates, the height could be measured by subtracting the extreme top y (row) value from the extreme bottom y (row) value, to do that the coordinates must be separated into two:

 

x_Top, y_Top = extTop
x_Bot, y_Bot = extBot

height = (y_Bot-y_Top)

print("---------------- Printing Results ----------------")
print(" The top point: ", extTop)
print(" The bottom point: ", extBot)
print(" The height of the person in pixels: ", height)

 

The results will be printed as below:

 

resultsV4print.JPG

 

With that, the aim of the project was achieved.

tenor.gif

Extreme Points on Contour

After getting the contour from the image of the person, the second step  would be getting the highest and lowest points on the contour, using the area of the contour, then getting the maximum and minimum points as tuples from the area that we have, that was the approach that we used to get the extreme points that are later on used to get the exact height of the person in pixels [1]:

c = max(contours, key=cv2.contourArea)
extTop = tuple(c[c[:, :, 1].argmin()][0])
extBot = tuple(c[c[:, :, 1].argmax()][0])

The code above will give us the coordinates of the extreme top and extreme bottom pixels. To show the extreme points on the picture I added a code to draw a circle on the points:

extTop = tuple(c[c[:, :, 1].argmin()][0])
cv2.circle(img = img, center = extTop, radius = 5, color = (255,0,0), thickness = -1)
extBot = tuple(c[c[:, :, 1].argmax()][0])
cv2.circle(img = img, center = extBot, radius = 5, color = (0,255,0), thickness = -1)

The result of modifying the previous code will be that a blue circle will be drawn at the extreme top value of the contour, and a green circle will be drawn at the extreme bottom value, as shown below:

resultsV4.JPG

Reference:
[1]A. Rosebrock, “Finding extreme points in contours with OpenCV – PyImageSearch”, PyImageSearch, 2018. [Online]. Available: https://www.pyimagesearch.com/2016/04/11/finding-extreme-points-in-contours-with-opencv/. [Accessed: 25- Nov- 2018].

Contours and Challenges

I thought of using contours to identify a person in a picture but the challenge with Dr Mick’s picture is the background noises using the code below:

#Opening the images
I = cv2.imread("DrMick.jpg")
Original = I.copy()
# Accessing pixel's b(blue) g(green) r(red) values:
b,g,r = I[1,1]
# Setting the lower and upper range values
lowerR = (b-25, g-25, r-25)
upperR = (b+50, g+50, r+50)
# Getting the values within this range
B = cv2.inRange(I, lowerR, upperR)
# subtract them from 255 so we would not go out of range
B=255-B
# Finding Contour
B, contours,_ = cv2.findContours(B, mode = cv2.RETR_EXTERNAL, method = cv2.CHAIN_APPROX_NONE)
# Draw contour
I = cv2.drawContours(I, contours, contourIdx = -1, color = (0,0,255), thickness = 5)
# Show the Original Image
cv2.imshow("Original", Original)
# Show the Result Image
cv2.imshow("Result", I)
cv2.waitKey(0)

The code will work in the following steps:

  1. Open Dr.Micks image, make a copy of it.
  2. Accessing pixel’s b(blue) g(green) r(red) values.
  3. Setting the lower and upper range values.
  4. Getting the values within this range, subtract them from 255 so we would not go out of range.
  5. Find the contour, draw a contour around the person.
  6. Show the original and the result.

The Original image and the Result image side by side looked like this:

DrMickResults

The problem with this method is that it is not effective with images that have background noise in them, but if we passed an image with a white background the result is far more satisfying:

Man2Results

Therefore, if we said that our design requires that the image passed into it to have a solid white background, that will solve the background noise problem.

Project Approach

Our project objective is to design a code that could measure a height of a person when passing an image of a person into it, the challenge was in finding the best approach to do this project, the following are some approaches that we thought of using:

  • Identifying the person manually using mouse click and measuring the distance from the first click to the second click as the height of the person.
  • Extract a person from the picture and measuring the distance between the highest point which is his head/hair and the lowest point to be his legs.
  • Using a reference: by taking a picture of a person we know his height and compare between any given picture of a person, calculate the difference compared to the reference we have to be that person height. [1]

The first approach is a manual approach and the code must be able to detect a person and measure the height automatically, the last option would be suitable to calculate the real person’s height in meters or centimetres or other measurement scales. However, in the project, we are only asked to calculate the height in pixels so the second approach would be the most suitable one.

References

[1]A. Rosebrock, “Measuring size of objects in an image with OpenCV – PyImageSearch”, PyImageSearch, 2018. [Online]. Available: https://www.pyimagesearch.com/2016/03/28/measuring-size-of-objects-in-an-image-with-opencv/. [Accessed: 22- Nov- 2018].

Sharing Information

In order to share whatever we find useful, I shared a google drive folder with all the information that I found useful for the project with my team mates:

drive

I thought that having a shared google drive document would make sharing information a lot easier, my team has two final year engineering and one final year computer science students, and due to different time tables, and semester workload, it would be more handy to have a drive folder set up that all team members could access when they have time.

Image Processing Project

Welcome to my Image Processing Project blog, my name is Ruba Bait Bin Saleem, and my group members are:

  • Aaron Costello
  • Safiah Sadeq

The objective of our project was to design a python code that would measure the height of the person (in pixels), and the picture that was given to us as a reference was a picture of Dr Mick standing straight as shown below:

DrMick

The code should measure the height of Dr Mick from the lowest point the tip of his shoes, to the highest point which is his head/hair.