File size: 2,803 Bytes
78aefa8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
"""
Ekstraksi fitur lokal SIFT & SURF, serta momen HSV, dibatasi pada area ROI daging (mask).

Membutuhkan opencv-contrib-python versi yang dikompilasi dengan
OPENCV_ENABLE_NONFREE=ON (mis. 3.4.2.16) agar cv2.xfeatures2d.SIFT_create()
dan cv2.xfeatures2d.SURF_create() tersedia.
"""
import time
import cv2
import numpy as np
from scipy.stats import skew

import config


def _get_detector(method):
    method = method.lower()
    if method == "sift":
        return cv2.xfeatures2d.SIFT_create(nfeatures=config.SIFT_N_FEATURES)
    elif method == "surf":
        return cv2.xfeatures2d.SURF_create(
            hessianThreshold=config.SURF_HESSIAN_THRESHOLD
        )
    raise ValueError("method harus 'sift' atau 'surf', diterima: {}".format(method))


def calculate_hsv_moments(hsv_image, mask):
    """
    Mengekstrak momen statistik (Mean, Std, Skewness) dari citra HSV.
    Hanya menghitung piksel yang berada di dalam mask (daging).
    """
    h, s, v = cv2.split(hsv_image)
    moments = []
    
    for channel in (h, s, v):
        # Terapkan mask untuk mengisolasi area daging secara eksklusif
        channel_data = channel[mask == 255]
        
        if len(channel_data) == 0:
            moments.extend([0.0, 0.0, 0.0])
            continue
            
        moments.extend([
            float(np.mean(channel_data)), 
            float(np.std(channel_data)), 
            float(skew(channel_data))
        ])
    return np.array(moments, dtype=np.float32)


def extract_features(gray_image, hsv_image, mask, method):
    """
    Mendeteksi keypoint & menghitung descriptor pada area mask saja,
    serta mengekstrak momen warna global HSV.

    Returns
    -------
    descriptors : np.ndarray (N, D) float32
    hsv_moments : np.ndarray (9,) float32
    n_keypoints : int
    elapsed_sec : float
    """
    detector = _get_detector(method)

    start = time.perf_counter()
    keypoints, descriptors = detector.detectAndCompute(gray_image, mask)
    
    # Ekstraksi HSV Moments sesuai parameter konfigurasi
    hsv_moments = calculate_hsv_moments(hsv_image, mask) if config.USE_COLOR_FUSION else None
    
    elapsed_sec = time.perf_counter() - start

    n_keypoints = len(keypoints) if keypoints is not None else 0

    if descriptors is None:
        descriptors = np.empty((0, 128 if method.lower() == "sift" else 64),
                                dtype=np.float32)
    else:
        descriptors = descriptors.astype(np.float32)
        cap = config.MAX_DESCRIPTORS_PER_IMAGE
        if cap is not None and len(descriptors) > cap:
            rng = np.random.RandomState(config.RANDOM_STATE)
            idx = rng.choice(len(descriptors), size=cap, replace=False)
            descriptors = descriptors[idx]

    return descriptors, hsv_moments, n_keypoints, elapsed_sec