Quantify longest axis and width of irregular shapes within a single image

Question:

Original question

I have about 80-100 images such as (A). Each image is composed of shapes that were filled with black color after marking the outline in ImageJ. I need to extract each individual shape as in (B). I then need to find the widths of the longest axis as in (C).

enter image description here

And I then need to calculate all the possible widths at regular points within the black shape and return a .csv file containing these values.

I have been doing this manually and there must be a quicker way to do it. Any idea how I can go about doing this?

Partial solution as per Epsi’s answer

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

image = cv2.imread("Desktop/Analysis/NormalCol0.tif")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh_img = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

# Find contours, find rotated rectangle, obtain four verticies, and draw 
contours, hierarchy = cv2.findContours(thresh_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

lengths = []

for i in contours:

    x_y,width_height,angle_of_rotation = cv2.minAreaRect(i)
    Height = print(width_height[1])
    rect = cv2.minAreaRect(i)
    box = np.int0(cv2.boxPoints(rect))
    image_contours = np.zeros(image.shape)

    # Draw the contours on the empty image
    cv2.drawContours(image, [box], 0, (36,255,12), 3)
    # OR:
    # cv2.polylines(image, [box], True, (36,255,12), 3)

    plt.imshow(image)
    
cv2.imwrite(output, image)

Output:

4031.0
20.877727508544922
51.598445892333984
23.852108001708984
21.0
21.21320343017578
19.677398681640625
43.0

I am able to get the length of each shape. I next need to figure out how to get width of the shape at regular points along the length of the shape?

enter image description here

Asked By: Anurag N. Sharma

||

Answers:

As I’m not that good with python, I wont post code, but will post links to explanations and documentary.

How I would do it is as follows:

  1. Invert the image, so that all the white becomes black and black becomes white. https://www.delftstack.com/howto/python/opencv-invert-image/#invert-images-using-numpy.invert-method-in-python

  2. Use findContours to find all the (now) white contours. https://pythonexamples.org/python-opencv-cv2-find-contours-in-image/

  3. use minAreaRect to create bounding boxes around the contours that is positioned in a way that the area of the box is as small as possible. https://theailearner.com/tag/cv2-minarearect/

  4. from the created bounding boxes, take the longest side, this will represent the length of your contour.

  5. You can also get the angle of rotation from the bounding boxes

Answered By: Epsi

I have prepared a notebook with @Anyurag’s partial answer (as part of the question post), based on @Epsi’s idea:

https://colab.research.google.com/drive/1tBnU6Xdc58dLHzBZPwKz_Y_mCb1Pp40e?usp=sharing

The notebook can be helpful as a starting point for someone digging into the problem.

Of course my answer is also partial, because as @Anyurag noticed, there remains an issue of how to get width of the shape at regular points along the length of the shape.

Answered By: Mikolaj Buchwald