image_processing

This commit is contained in:
livindra 2026-05-31 17:08:58 +07:00
parent 0e9f1202cd
commit 833d8801c9
2 changed files with 56 additions and 25 deletions

4
app.py
View File

@ -353,7 +353,7 @@ def api_validate_image():
file.save(temp_filepath) file.save(temp_filepath)
# Validasi gambar # Validasi gambar
is_cattle, confidence, reason = validate_cattle_image(temp_filepath, confidence_threshold=0.75) is_cattle, confidence, reason = validate_cattle_image(temp_filepath, confidence_threshold=0.65)
if is_cattle: if is_cattle:
result = { result = {
@ -410,7 +410,7 @@ def predict():
# Validasi ulang di backend supaya request langsung ke /predict tidak bisa bypass. # Validasi ulang di backend supaya request langsung ke /predict tidak bisa bypass.
is_cattle, validation_confidence, validation_reason = validate_cattle_image( is_cattle, validation_confidence, validation_reason = validate_cattle_image(
filepath, filepath,
confidence_threshold=0.75, confidence_threshold=0.65,
) )
if not is_cattle: if not is_cattle:
if os.path.exists(filepath): if os.path.exists(filepath):

View File

@ -3,7 +3,32 @@ import numpy as np
from PIL import Image from PIL import Image
def validate_cattle_image(image_path, confidence_threshold=0.75): def _detect_human_face(gray_image):
"""Return (detected, face_area_ratio) for human face detection."""
try:
cascade_path = cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
face_cascade = cv2.CascadeClassifier(cascade_path)
if face_cascade.empty():
return False, 0.0
faces = face_cascade.detectMultiScale(
gray_image,
scaleFactor=1.1,
minNeighbors=5,
minSize=(50, 50),
)
if len(faces) == 0:
return False, 0.0
image_area = float(gray_image.shape[0] * gray_image.shape[1])
largest_face_area = max((w * h for (_, _, w, h) in faces), default=0)
face_area_ratio = (largest_face_area / image_area) if image_area > 0 else 0.0
return face_area_ratio >= 0.03, face_area_ratio
except Exception:
return False, 0.0
def validate_cattle_image(image_path, confidence_threshold=0.65):
""" """
Validasi apakah gambar adalah sapi atau bukan. Validasi apakah gambar adalah sapi atau bukan.
Bisa deteksi wajah sapi (dengan mata) atau bagian tubuh sapi lainnya. Bisa deteksi wajah sapi (dengan mata) atau bagian tubuh sapi lainnya.
@ -26,6 +51,12 @@ def validate_cattle_image(image_path, confidence_threshold=0.75):
h, w = img.shape[:2] h, w = img.shape[:2]
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Tolak foto wajah manusia lebih awal karena sering lolos pada heuristik warna/tekstur.
human_face_detected, face_area_ratio = _detect_human_face(gray)
if human_face_detected:
print(f"[VALIDATE] Wajah manusia terdeteksi | area={face_area_ratio*100:.1f}%")
return False, 0.0, "Gambar tidak menunjukkan sapi"
# ===== LAYER 1: DETEKSI MATA (OPTIONAL - hanya bonus jika ada) ===== # ===== LAYER 1: DETEKSI MATA (OPTIONAL - hanya bonus jika ada) =====
circles = cv2.HoughCircles( circles = cv2.HoughCircles(
gray, cv2.HOUGH_GRADIENT, gray, cv2.HOUGH_GRADIENT,
@ -76,66 +107,66 @@ def validate_cattle_image(image_path, confidence_threshold=0.75):
cattle_color = brown_mask | red_mask | black_mask | white_mask cattle_color = brown_mask | red_mask | black_mask | white_mask
color_ratio = np.sum(cattle_color) / (h * w) color_ratio = np.sum(cattle_color) / (h * w)
# Minimum warna sapi harus 30% (ketat untuk menolak bukan sapi) # Minimum warna sapi dibuat lebih longgar supaya foto sapi yang crop/lighting-nya beragam tetap lolos
if color_ratio < 0.30: if color_ratio < 0.18:
return False, 0.0, "Gambar tidak menunjukkan sapi" return False, 0.0, "Gambar tidak menunjukkan sapi"
# Score color: ketat untuk matching sapi # Score color: ketat untuk matching sapi
if color_ratio >= 0.50: if color_ratio >= 0.45:
color_score = 1.0 color_score = 1.0
elif color_ratio >= 0.40:
color_score = 0.95
elif color_ratio >= 0.30: elif color_ratio >= 0.30:
color_score = 0.80 color_score = 0.90
elif color_ratio >= 0.18:
color_score = 0.75
else: else:
color_score = 0.60 color_score = 0.55
print(f"[VALIDATE] Warna sapi: {color_ratio*100:.0f}%") print(f"[VALIDATE] Warna sapi: {color_ratio*100:.0f}%")
# ===== LAYER 3: VALIDASI TEKSTUR KULIT ===== # ===== LAYER 3: VALIDASI TEKSTUR KULIT =====
laplacian = cv2.Laplacian(gray, cv2.CV_64F) laplacian = cv2.Laplacian(gray, cv2.CV_64F)
texture_variance = np.var(laplacian) texture_variance = np.var(laplacian)
# Texture sapi harus dalam range spesifik 80-2200 (ketat) # Range texture dibuat lebih fleksibel karena foto asli bisa sangat bervariasi
if texture_variance < 80 or texture_variance > 2200: if texture_variance < 45 or texture_variance > 3500:
return False, 0.0, "Gambar tidak menunjukkan tekstur sapi" return False, 0.0, "Gambar tidak menunjukkan tekstur sapi"
# Score texture: ketat untuk matching sapi texture # Score texture: ketat untuk matching sapi texture
if 120 <= texture_variance <= 1800: if 100 <= texture_variance <= 2200:
texture_score = 1.0 texture_score = 1.0
elif 90 <= texture_variance <= 2100: elif 70 <= texture_variance <= 3000:
texture_score = 0.90 texture_score = 0.85
else: else:
texture_score = 0.75 texture_score = 0.70
print(f"[VALIDATE] Tekstur variance: {texture_variance:.0f}") print(f"[VALIDATE] Tekstur variance: {texture_variance:.0f}")
# ===== LAYER 4: EDGE DETECTION (Struktur) ===== # ===== LAYER 4: EDGE DETECTION (Struktur) =====
edges = cv2.Canny(gray, 50, 150) edges = cv2.Canny(gray, 50, 150)
edge_density = np.sum(edges > 0) / (h * w) edge_density = np.sum(edges > 0) / (h * w)
# Edge density: 2-25% ketat untuk sapi # Edge density dibuat lebih longgar supaya gambar close-up atau background polos tidak langsung gagal
if edge_density < 0.02 or edge_density > 0.25: if edge_density < 0.01 or edge_density > 0.35:
return False, 0.0, "Gambar tidak memiliki struktur sapi" return False, 0.0, "Gambar tidak memiliki struktur sapi"
# Score edge: ketat untuk matching # Score edge: ketat untuk matching
if 0.05 <= edge_density <= 0.15: if 0.04 <= edge_density <= 0.18:
edge_score = 1.0 edge_score = 1.0
elif 0.03 <= edge_density <= 0.22: elif 0.02 <= edge_density <= 0.28:
edge_score = 0.90 edge_score = 0.85
else: else:
edge_score = 0.70 edge_score = 0.65
print(f"[VALIDATE] Edge density: {edge_density*100:.1f}%") print(f"[VALIDATE] Edge density: {edge_density*100:.1f}%")
# ===== HARD REQUIREMENTS ===== # ===== HARD REQUIREMENTS =====
# REQUIREMENT 1: Warna harus minimal decent (tidak boleh terlalu rendah) # REQUIREMENT 1: Warna harus minimal decent (tidak boleh terlalu rendah)
if color_score < 0.75: if color_score < 0.55:
return False, 0.0, "Warna gambar tidak sesuai sapi" return False, 0.0, "Warna gambar tidak sesuai sapi"
# REQUIREMENT 2: Texture harus dalam range sapi # REQUIREMENT 2: Texture harus dalam range sapi
if texture_score < 0.70: if texture_score < 0.60:
return False, 0.0, "Tekstur gambar tidak sesuai sapi" return False, 0.0, "Tekstur gambar tidak sesuai sapi"
# REQUIREMENT 3: Edge harus terdeteksi # REQUIREMENT 3: Edge harus terdeteksi
if edge_score < 0.65: if edge_score < 0.55:
return False, 0.0, "Struktur gambar tidak terlihat jelas" return False, 0.0, "Struktur gambar tidak terlihat jelas"
# ===== FINAL CALCULATION ===== # ===== FINAL CALCULATION =====