基于机器学习的Blob OK/NOK分类技术方案咨询
Hey there! Let's walk through how to get your Blob classification project up and running smoothly—since you've got a small, manageable dataset and can handle manual labeling, we can stick to practical, effective approaches without overcomplicating things.
1. First: Complete Manual Labeling
Since your 200 images are unlabeled, this is the critical first step. You want consistent, accurate labels to train a reliable model. Here's how to do it efficiently:
- Simple folder-based labeling: Create two folders,
OKandNOK, then manually sort each image into the correct folder. This is straightforward and works great for small datasets. - Optional: Label with a tool: If you want to track metadata (like notes on why a Blob is NOK), use a lightweight tool like LabelImg or even a CSV file where you list each image filename and its corresponding label (
1for OK,0for NOK). - Pro tip: Label in batches to stay focused—do 20-30 images at a time, and double-check a random sample of your labels to avoid mistakes.
2. Extract Target Features (Shape + "Volume")
You mentioned focusing on contour shape and "volume" (which for 2D images is just the pixel area of the Blob). Let's break down how to extract these using OpenCV (Python) since it's the go-to for image processing tasks:
Step 2.1: Preprocess Images
First, convert your grayscale images to binary to isolate the Blob:
import cv2 import numpy as np def preprocess_image(img_path): # Read 8-bit grayscale image img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) # Otsu's thresholding to auto-separate Blob from white background _, binary_img = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) return binary_img
Otsu's thresholding is perfect here because it automatically finds the best threshold to separate your dark Blob from the white background.
Step 2.2: Extract Contour & Features
Once you have the binary image, extract the Blob's contour and calculate your target features:
def extract_features(binary_img): # Find contours (only the largest one, since each image has one Blob) contours, _ = cv2.findContours(binary_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) blob_contour = max(contours, key=cv2.contourArea) # Volume (Area) feature area = cv2.contourArea(blob_contour) # Shape features perimeter = cv2.arcLength(blob_contour, closed=True) # Circularity: 4π*Area/Perimeter² (1 = perfect circle) circularity = (4 * np.pi * area) / (perimeter ** 2) if perimeter > 0 else 0 # Bounding box aspect ratio (width/height) x, y, w, h = cv2.boundingRect(blob_contour) aspect_ratio = w / h if h > 0 else 0 # Hu Moments (shape-invariant to rotation/scale) moments = cv2.moments(blob_contour) hu_moments = cv2.HuMoments(moments).flatten() # Combine all features into a single array return np.array([area, circularity, aspect_ratio] + hu_moments.tolist())
- Area: Directly tells you the size of the Blob (your "volume" feature)
- Circularity: Great for detecting irregular shapes (NOK Blobs might have low circularity)
- Aspect Ratio: Useful if OK Blobs are supposed to be a specific width/height ratio
- Hu Moments: Invariant to rotation, scaling, and translation—perfect for capturing subtle shape differences
3. Train a Classification Model
With labeled data and extracted features, you can use traditional machine learning models (no need for deep learning here—your dataset is too small for that). Scikit-learn has all the tools you need:
Step 3.1: Prepare Your Dataset
Load your labeled images, extract features, and split into training/test sets:
import os from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler # Define paths ok_dir = "path/to/OK" nok_dir = "path/to/NOK" # Load data X = [] y = [] # OK samples for img_file in os.listdir(ok_dir): img_path = os.path.join(ok_dir, img_file) binary = preprocess_image(img_path) features = extract_features(binary) X.append(features) y.append(1) # Label OK as 1 # NOK samples for img_file in os.listdir(nok_dir): img_path = os.path.join(nok_dir, img_file) binary = preprocess_image(img_path) features = extract_features(binary) X.append(features) y.append(0) # Label NOK as 0 # Convert to numpy arrays X = np.array(X) y = np.array(y) # Standardize features (important for models like SVM, Logistic Regression) scaler = StandardScaler() X_scaled = scaler.fit_transform(X) # Split into train/test sets (80/20 split works for small data) X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.2, random_state=42)
Step 3.2: Train & Evaluate Models
Try a few models and pick the one with the best performance. Here are top choices for small datasets:
from sklearn.svm import SVC from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import accuracy_score, confusion_matrix # Train SVM (great for small, high-dimensional data) svm_model = SVC(kernel='rbf', random_state=42) svm_model.fit(X_train, y_train) y_pred_svm = svm_model.predict(X_test) print(f"SVM Accuracy: {accuracy_score(y_test, y_pred_svm):.2f}") print("SVM Confusion Matrix:\n", confusion_matrix(y_test, y_pred_svm)) # Train Random Forest (robust to overfitting) rf_model = RandomForestClassifier(n_estimators=100, random_state=42) rf_model.fit(X_train, y_train) y_pred_rf = rf_model.predict(X_test) print(f"Random Forest Accuracy: {accuracy_score(y_test, y_pred_rf):.2f}") print("Random Forest Confusion Matrix:\n", confusion_matrix(y_test, y_pred_rf))
- Confusion Matrix: Tells you how many OK/NOK samples were correctly classified—look for low false positives/negatives, since misclassifying NOK as OK might be a critical error.
- Cross-Validation: For small datasets, use 5-fold cross-validation to get a more reliable estimate of model performance:
from sklearn.model_selection import cross_val_score cv_scores = cross_val_score(svm_model, X_scaled, y, cv=5) print(f"SVM Cross-Validation Accuracy: {cv_scores.mean():.2f} ± {cv_scores.std():.2f}")
4. Optimize & Deploy
- Feature Selection: If some features don't help (e.g., Hu moments with low variance), use
SelectKBestfrom scikit-learn to keep only the most impactful features. - Hyperparameter Tuning: Use
GridSearchCVto tweak model parameters (e.g., SVM'sCandgammavalues) for better performance. - Deploy: Once you're happy with the model, save it and the scaler using
joblib, then write a simple inference script:import joblib # Save model and scaler joblib.dump(svm_model, "blob_classifier.pkl") joblib.dump(scaler, "scaler.pkl") # Inference function def classify_blob(img_path): model = joblib.load("blob_classifier.pkl") scaler = joblib.load("scaler.pkl") binary = preprocess_image(img_path) features = extract_features(binary) features_scaled = scaler.transform([features]) prediction = model.predict(features_scaled)[0] return "OK" if prediction == 1 else "NOK"
Quick Notes to Avoid Pitfalls
- Noise Handling: If some images have small noise spots, add a morphological operation (like
cv2.morphologyEx(binary_img, cv2.MORPH_OPEN, kernel)) to clean up the binary image before contour extraction. - Label Consistency: Make sure your definition of OK/NOK is clear (e.g., "OK = circular Blob with area between X and Y pixels")—this will make labeling and model training more reliable.
内容的提问来源于stack exchange,提问作者ToKra

