shape-recognition / preprocess_quality.py
Laufey's picture
Update preprocess_quality.py
fa18cc6 verified
Raw
History Blame Contribute Delete
3.91 kB
import cv2
import numpy as np
from PIL import Image
class GeometricFeatureExtractor:
def __init__(self, image_size=128):
self.image_size = image_size
self.last_feature_dict = None
def extract_features(self, pil_image: Image.Image):
img = np.array(pil_image.convert("L"))
img = cv2.resize(img, (self.image_size, self.image_size))
blur = cv2.GaussianBlur(img, (5,5),0)
_, thresh = cv2.threshold(
blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU
)
contours, _ = cv2.findContours(
thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
)
if not contours:
return None
contour = max(contours, key=cv2.contourArea)
area = cv2.contourArea(contour)
perimeter = cv2.arcLength(contour, True)
if area == 0 or perimeter == 0:
return None
x, y, w, h = cv2.boundingRect(contour)
aspect_ratio = w / h if h != 0 else 0
if len(contour) >= 5:
ellipse = cv2.fitEllipse(contour)
major_axis = max(ellipse[1])
minor_axis = min(ellipse[1])
eccentricity = (
np.sqrt(1 - (minor_axis / major_axis)**2)
if major_axis > 0 else 0
)
else:
eccentricity = 0
compactness = (perimeter**2) / (4*np.pi*area)
circularity = (4*np.pi*area) / (perimeter**2)
epsilon = 0.02 * perimeter
approx = cv2.approxPolyDP(contour, epsilon, True)
num_vertices = len(approx)
angle_error = 0.0
if num_vertices >= 3:
angles = []
for i in range(num_vertices):
p1 = approx[i % num_vertices][0]
p2 = approx[(i+1) % num_vertices][0]
p3 = approx[(i+2) % num_vertices][0]
v1 = p1 - p2
v2 = p3 - p2
cosang = np.dot(v1, v2) / (
np.linalg.norm(v1)*np.linalg.norm(v2) + 1e-6
)
angle = np.degrees(np.arccos(np.clip(cosang, -1, 1)))
angles.append(angle)
ideal = 60 if num_vertices == 3 else 90
angle_error = float(np.mean(np.abs(np.array(angles) - ideal)))
side_lengths = []
for i in range(num_vertices):
p1 = approx[i][0]
p2 = approx[(i+1) % num_vertices][0]
side_lengths.append(np.linalg.norm(p1 - p2))
side_length_variance = (
np.std(side_lengths) / (np.mean(side_lengths) + 1e-6)
if len(side_lengths) >= 2 else 0
)
moments = cv2.moments(contour)
hu_moments = cv2.HuMoments(moments).flatten()
edges = cv2.Canny(thresh, 50, 150)
edge_density = np.sum(edges > 0) / (self.image_size**2)
hull = cv2.convexHull(contour)
hull_area = cv2.contourArea(hull)
solidity = area / hull_area if hull_area > 0 else 0
self.last_feature_dict = {
"area_norm": area / (self.image_size**2),
"perimeter_norm": perimeter / (self.image_size*4),
"aspect_ratio": aspect_ratio,
"eccentricity": eccentricity,
"compactness": compactness,
"circularity": circularity,
"num_vertices": num_vertices,
"edge_density": edge_density,
"solidity": solidity,
"angle_error": angle_error,
"side_length_variance": side_length_variance
}
features = np.array([
self.last_feature_dict["area_norm"],
self.last_feature_dict["perimeter_norm"],
aspect_ratio,
eccentricity,
compactness,
circularity,
num_vertices,
edge_density,
solidity,
*hu_moments
], dtype=np.float32)
return features