image_processing
This commit is contained in:
parent
0e9f1202cd
commit
833d8801c9
4
app.py
4
app.py
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -25,6 +50,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(
|
||||||
|
|
@ -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 =====
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue