Home > Mobile >  calculate the pixel ratio of two halves of an image
calculate the pixel ratio of two halves of an image

Time:06-16

I'm trying to calculate the pixel ratio of two halves of an image split vertically and horizontally along its centroid. The goal is to see how symmetrical/asymmetrical the image is. Below is the code, the original image, and an image showing what I'm trying to do.

So far, I've thresholded the image, created a contour around its perimeter, filled that contour, and calculated and labeled the centroid.

I'm stuck on how to (a) split the contour into two, and (b) calculate the pixel ratios between the two halves of the contour image (just the black parts. Thanks for any advice and/or help.

# import packages
import argparse
import imutils
import cv2

# construct argument parser
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True,
    help="path to the input image")
args = vars(ap.parse_args())

# load the image
image = cv2.imread(args["image"])

# convert it to grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# threshold the image
(T, threshInv) = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY_INV)

# find outer contour of thresholded image
cnts = cv2.findContours(threshInv.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)

# loop over the contour/s for image moments
for c in cnts:
    #  compute the center of the contour
    M = cv2.moments(c)
    #  calculate the centroid
    cX = int(M["m10"] / M["m00"])
    cY = int(M["m01"] / M["m00"])

# draw and fill the contour on the image
cv2.drawContours(image, [c], -1, (0, 0, 0), thickness=cv2.FILLED)

# draw the centroid on the filled contour
cv2.circle(image, (cX, cY), 7, (255, 0, 0), -1)

# show the image
cv2.imshow("Image", image)
cv2.waitKey(0)

Original image:

enter image description here

Goal:

enter image description here

CodePudding user response:

Part 1:

Images can be cropped respectively as shown below

top_half = image[0:cY, :]
bottom_half = image[cY:, :]

left_half = image[:, 0:cX]
right_half = image[:, cX:]

Part 2:

To calculate the ratio, let us just take any one channel of the above 4 cropped images. The channel would be a binary image consisting of white (255) pixels and black (0) pixels only. We will count the number of black pixels in each half and divide:

top_half = top_half[:,:,1]
bottom_half = bottom_half[:,:,1]
left_half = left_half[:,:,1]
right_half = right_half[:,:,1]

All the above are single channel images

top_bottom_ratio = int(np.size(top_half) - np.count_nonzero(top_half) / np.size(bottom_half) - np.count_nonzero(bottom_half)

np.size() gives total number of pixels in the image

np.count_nonzero() gives number of white pixels

You can do the same to find ratio between left and right halves

CodePudding user response:

Thanks to Jeru Luke for help. Using his suggestions, this code solves the issue.

# import packages
import argparse
import imutils
import numpy as np
import cv2

# construct argument parser
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True, help="path to the input image")
args = vars(ap.parse_args())

# load the image
image = cv2.imread(args["image"])

# convert it to grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# threshold the image
(T, threshInv) = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY_INV)

# find outer contour of thresholded image
conts = cv2.findContours(threshInv.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
conts = imutils.grab_contours(conts)

# loop over the contour/s for image moments
for c in conts:
    #  compute the center of the contour
    M = cv2.moments(c)
    #  calculate the centroid
    cX = int(M["m10"] / M["m00"])
    cY = int(M["m01"] / M["m00"])

# draw and fill the contour on the image
cv2.drawContours(image, [c], -1, (0, 0, 0), thickness=cv2.FILLED)

# draw the centroid on the filled contour
cv2.circle(image, (cX, cY), 7, (255, 0, 0), -1)

# portions of the image (includes white and black pixels)
top_half = image[0:cY, :]
bottom_half = image[cY:, :]
left_half = image[:, 0:cX]
right_half = image[:, cX:]

# halves of images containing black pixels
top_half = top_half[:, :, 1]
bottom_half = bottom_half[:, :, 1]
left_half = left_half[:, :, 1]
right_half = right_half[:, :, 1]

#  np.size() gives total number of pixels in the image
#  np.count_nonzero() gives number of white pixels
top_bot_ratio = (int(np.size(top_half) - np.count_nonzero(top_half)) / (np.size(bottom_half) - np.count_nonzero(bottom_half)))
left_right_ratio = (int(np.size(left_half) - np.count_nonzero(left_half)) / (np.size(right_half) - np.count_nonzero(right_half)))

# area of the entire contour

# show the image
cv2.imshow("Original Image (grayscale)", gray)
cv2.imshow("Centroid", image)
cv2.imshow("Top Half (at the centroid)", top_half)
cv2.imshow("Bottom Half (at the centroid)", bottom_half)
cv2.imshow("Left Half (at the centroid)", left_half)
cv2.imshow("Right Half (at the centroid)", right_half)

print(f'The ratio of top to bottom halves is: {round(top_bot_ratio, 3)}')
print(f'The ratio of left to right halves is: {round(left_right_ratio, 3)}')
cv2.waitKey(0)
  • Related