Follical / app.py
Afeezee's picture
Update app.py
2d31980 verified
import cv2
import numpy as np
import gradio as gr
def process_image(image, calibration_factor):
"""
Processes the input image to measure the hair diameter.
Args:
image (numpy array): Uploaded image in RGB format.
calibration_factor (float): Conversion factor from pixels to micrometers.
Returns:
tuple: A result text and the annotated image.
"""
if image is None:
return "No image provided.", None
# Convert image from RGB (Gradio default) to BGR (OpenCV default)
image_bgr = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
# Convert to grayscale
gray = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2GRAY)
# Apply Gaussian blur to reduce noise
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
# Apply thresholding (using Otsu's method) to create a binary image
ret, thresh = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# Find contours in the thresholded image
contours, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if not contours:
return "No hair contour found.", image
# Assume the hair strand is the largest contour
hair_contour = max(contours, key=cv2.contourArea)
# Fit a minimum area rectangle to the contour
rect = cv2.minAreaRect(hair_contour)
(center, (width, height), angle) = rect
# The smaller dimension is considered the hair's diameter in pixels
diameter_pixels = min(width, height)
diameter_real = diameter_pixels * calibration_factor # Convert to micrometers
# Draw the fitted rectangle on the image for visualization
box = cv2.boxPoints(rect)
box = np.int0(box)
annotated = image_bgr.copy()
cv2.drawContours(annotated, [box], 0, (0, 255, 0), 2)
# Convert annotated image back to RGB for Gradio display
annotated_rgb = cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB)
result_text = f"Estimated Hair Diameter: {diameter_real:.2f} micrometers"
return result_text, annotated_rgb
# Build the Gradio interface
iface = gr.Interface(
fn=process_image,
inputs=[
gr.Image(type="numpy", label="Upload Hair Image"),
gr.Number(value=0.05, label="Calibration Factor (micrometers per pixel)")
],
outputs=[
gr.Textbox(label="Result"),
gr.Image(label="Annotated Image")
],
title="Hair Diameter Measurement Tool",
description=(
"Upload a high-resolution image of a hair sample (captured with a dermascope) "
"and provide a calibration factor based on a reference object included in the image. "
"This tool processes the image to estimate the hair diameter using image processing techniques."
)
)
if __name__ == '__main__':
iface.launch()