5
votes

I would like to calculate the area of a shape in an image produced from infra red cameras.

I have a large set of matrices which i produce from an infra- red camera. In each matrix/image i have mostly background with an image of a spot, which is a source of infra red radiation. The way i proceeded with it is to use Python OpenCV in order to isolate the image of the source by nullifying the background and calculating the number of pixels in the shape. The problem is that in each image, parts of the image is also turned into background and so i can't get the full image, as i would like.

import cv2
import numpy as np
from matplotlib import pyplot as plt

PPmm = 81/55 #Pixel per mm


img = np.genfromtxt('Image 5 Z_plane = 141.0_contour_plot.csv', delimiter= ',')

img_cv = cv2.resize(img,(81,81))
np.savetxt('testing.csv', img_cv, delimiter= ',')

img = (img_cv*255).astype(np.uint8)









edges = cv2.Canny(img,150,250)

se = np.ones((7,7), dtype='uint8')





# Perform morphology

image_close = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, se)

# Your code now applied to the closed image
cnt = cv2.findContours(image_close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
mask = np.zeros(img.shape[:2], np.uint8)
cv2.drawContours(mask, cnt, -1, 255, -1)





non_zero_pixel_count = (np.count_nonzero(mask))

Area_in_mm = non_zero_pixel_count*(1/PPmm)**2
print("Area of shape = {0:1f}mm^2".format(Area_in_mm))






plt.subplot(121)
plt.imshow(img,cmap = 'gray')
plt.title('Original Image')
plt.xticks([])
plt.yticks([])
plt.subplot(122)
plt.imshow(mask,cmap = 'gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])
[enter image description here][1]
plt.show()

the shape area is: 58.093278mm^2. If i calculate manually, approximatly i will get 68mm^2. In circular images this is even worse and I get an area twice as small

Circular image

Square image

edit: Using cv2.THRESH_BINARY

1
Why don't you just binarize your original image with a very low threshold since your background seems to be only black ?Clément
That's the thing, the background isn't really black. There's background noise which under just a simple binary threshold i can see very clearly (attached to the original post)tHeReaver
How do you define "exact" shape when there is no clear separation of the foreground objects and background, if the background is not perfectly constant and the foreground objects have varying gray levels, i.e. blurry.fmw42
Maybe provide some more information about the image generation. It is unclear to me how the blob in the image relates to the size of the radiation source. A simple threshold as in the answer below is unlikely to be meaningful. One good way of understanding this relationship is to image sources of different sizes and shapes.Cris Luengo
As Cris Luengo mentioned, simple thresholding is unlikely to yield meaningful data. However, it might help you identify the center of the shape, following which you can search for edges radially outwards, and the use edge points to define the outer boundary of the shape. Could you post the original, raw images before you removed the background? The background removal may have eliminated data of interest.Rethunk

1 Answers

3
votes

To get the exact edges of the shape you can do this

  • Threshold
  • Find contours
  • Count all non zero pixels

After finding the contour shape, you can use cv2.countNonZero() to find all white pixels and then calculate the area using your calibrated pixel metric (my area is different since I don't the exact original image)


import cv2

image = cv2.imread('2.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 20, 255, cv2.THRESH_BINARY)[1]

cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]

for c in cnts:
    cv2.drawContours(image,[c], 0, (36,255,12), 2)

area = cv2.countNonZero(thresh)
cv2.putText(image, "Area: {}".format(area), (40, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (156, 188, 24), 1)

cv2.imshow('thresh', thresh)
cv2.imshow('image', image)
cv2.imwrite('thresh.png', thresh)
cv2.imwrite('image.png', image)
cv2.waitKey()