-
Notifications
You must be signed in to change notification settings - Fork 0
/
least_squares_robust.py
99 lines (82 loc) · 3.11 KB
/
least_squares_robust.py
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
87
88
89
90
91
92
93
94
95
96
97
98
99
import cv2
import numpy as np
import matplotlib.pyplot as plt
def cauchy_weight(residual, c):
return 1.0 / (1.0 + (residual / c)**2)
def fit_quadratic_surface_robust(image, c, iterations=20):
rows, cols = image.shape
X, Y = np.meshgrid(np.arange(cols), np.arange(rows))
Z = image
# Prepare the data for least squares fitting
X_flat = X.flatten()
Y_flat = Y.flatten()
Z_flat = Z.flatten()
A = np.vstack([X_flat**2, Y_flat**2, X_flat*Y_flat, X_flat, Y_flat, np.ones_like(X_flat)]).T
B = Z_flat
# Initial guess for the coefficients (least squares)
coeffs, _, _, _ = np.linalg.lstsq(A, B, rcond=None)
# Iterative reweighted least squares
for itr in range(iterations):
residuals = A @ coeffs - B
weights = cauchy_weight(residuals, c)
W = np.diag(weights)
Aw = A.T @ W @ A
Bw = A.T @ W @ Z_flat
coeffs = np.linalg.solve(Aw, Bw)
# Create the background model
background = (coeffs[0] * X**2 + coeffs[1] * Y**2 +
coeffs[2] * X * Y + coeffs[3] * X +
coeffs[4] * Y + coeffs[5])
residual = background.astype(np.float32) - image.astype(np.float32)
residual = np.clip(residual, 0, 255).astype(np.uint8)
_, binarized = cv2.threshold(residual, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
# Plot the intermediate result
plt.subplot(2, 2, 1)
plt.title(f'Iteration: #{itr + 1}')
plt.imshow(image, cmap='gray')
plt.subplot(2, 2, 2)
plt.title('Background')
plt.imshow(background, cmap='gray')
plt.subplot(2, 2, 3)
plt.title('Residual Image')
plt.imshow(residual, cmap='gray')
plt.subplot(2, 2, 4)
plt.title('Binary Image')
plt.imshow(binarized, cmap='gray')
plt.pause(0.5)
plt.clf()
return background
def main(image_path):
# Load the input image
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
if image is None:
print("Error: Could not open or find the image!")
return
# Initialize display
plt.figure(figsize=(6, 6))
# Fit and remove the background
c = 2.3849 # Cauchy function parameter
background = fit_quadratic_surface_robust(image, c)
residual = background.astype(np.float32) - image.astype(np.float32)
residual = np.clip(residual, 0, 255).astype(np.uint8)
_, binarized = cv2.threshold(residual, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
# Display final results
plt.subplot(2, 2, 1)
plt.title('Original Image')
plt.imshow(image, cmap='gray')
plt.subplot(2, 2, 2)
plt.title('Background')
plt.imshow(background, cmap='gray')
plt.subplot(2, 2, 3)
plt.title('Residual Image')
plt.imshow(residual, cmap='gray')
plt.subplot(2, 2, 4)
plt.title('Binary Image')
plt.imshow(binarized, cmap='gray')
plt.show()
if __name__ == "__main__":
import sys
if len(sys.argv) != 2:
print(f"Usage: {sys.argv[0]} <image_path>")
else:
main(sys.argv[1])