import cv2 import numpy as np import pandas as pd import streamlit as st def calculate_parameters(annotations, scale_factor): """Calculates parameters for each segmented object, including Feret diameter.""" df = pd.DataFrame(columns=['Object', 'Area', 'Perimeter', 'Roundness', 'Aspect Ratio (Elongation)', 'Longest Feret Diameter']) if len(annotations) > 0: for i, mask in enumerate(annotations): binary_mask = mask.cpu().numpy().astype(np.uint8) area_pixel = np.sum(binary_mask) area_micron = area_pixel * (scale_factor ** 2) # Find contours with all points (no approximation) contours, _ = cv2.findContours(binary_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) if contours: # Check if any contours were found perimeter_pixel = cv2.arcLength(contours[0], True) perimeter_micron = perimeter_pixel * scale_factor # Fit ellipse for roundness and aspect ratio (check for sufficient points) if len(contours[0]) >= 5: ellipse = cv2.fitEllipse(contours[0]) major_axis, minor_axis = ellipse[1] else: major_axis = minor_axis = 0 major_axis_micron = major_axis * scale_factor minor_axis_micron = minor_axis * scale_factor roundness = (4 * np.pi * area_micron) / (perimeter_micron ** 2) aspect_ratio = major_axis_micron / minor_axis_micron if minor_axis_micron != 0 else "Undefined" # Calculate Feret diameter and Elongation hull = cv2.convexHull(contours[0]) distances = np.linalg.norm(hull - hull[:, 0, :], axis=2) max_feret_diameter_micron = np.max(distances) * scale_factor new_row = pd.DataFrame({ 'Object': [f"Object {i}"], 'Area': [area_micron], 'Perimeter': [perimeter_micron], 'Roundness': [roundness], 'Aspect Ratio (Elongation)': [aspect_ratio], 'Longest Feret Diameter': [max_feret_diameter_micron], }) df = pd.concat([df, new_row], ignore_index=True) # Eliminate artifacts with undefined parameters df = df[(df['Longest Feret Diameter'] != 0) & (df['Roundness'] >= 0) & (df['Roundness'] <= 1)] return df