Skip to content

Commit ebc8a0f

Browse files
committed
Refactor image processing pipeline and enhance visualization consistency.
Updated image extensions filter to include `.jpg`. Refactored centroid calculations and visualization to improve naming consistency and clarity. Standardized axis labels to "pixels" and optimized 3D visualizations with centroid overlays. Parallelized dataset processing using multithreading for efficiency. Added new 3D visualization outputs for FBM and CCL methods.
1 parent dc1ac8f commit ebc8a0f

2 files changed

Lines changed: 121 additions & 60 deletions

File tree

centroid_calculator.py

Lines changed: 114 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def __init__(self, image_path, num_of_segments=75, a_compactness=10):
3535
:type image_path: str
3636
:param num_of_segments: The desired number of segments to divide the
3737
image into for segmentation. Default is 100.
38-
:type num_of_segments: int, optional
38+
:type num_of_segments: int, optionally
3939
:param a_compactness: Controls the compactness of Superpixels. Higher
4040
values result in more square-like segments. Default is 10.
4141
:type a_compactness: int, optional
@@ -60,15 +60,15 @@ def __init__(self, image_path, num_of_segments=75, a_compactness=10):
6060
self.superpixels_images = [gray_image, image_for_super_process]
6161

6262
@staticmethod
63-
def plot_wireframe(actual_algorithm, rotated_gray_image_meth, x_grid_meth, y_grid_meth, rotated_segments_meth, h_rot_meth, w_rot_meth, X_meth, Y_meth):
63+
def plot_wireframe(actual_algorithm, rotated_gray_image_meth, x_grid_meth, y_grid_meth, rotated_segments_meth, h_rot_meth, w_rot_meth, x_meth, y_meth, stride=10):
6464
# Plot the center using a wireframe
6565

6666
# Create 3D plot
6767
fig_3d = plt.figure(f"3D Visualization -- {actual_algorithm}", figsize=(15, 15))
6868
ax_3d = fig_3d.add_subplot(111, projection='3d')
6969

7070
# Plot the 3D surface
71-
stride = 1 # Use a smaller stride for more detail
71+
# Use a smaller stride for more detail
7272
# Scale the z-axis (intensity) to 256
7373
scaled_intensity = rotated_gray_image_meth[::stride, ::stride] * 256
7474
ax_3d.plot_wireframe(x_grid_meth[::stride, ::stride], y_grid_meth[::stride, ::stride],
@@ -86,12 +86,12 @@ def plot_wireframe(actual_algorithm, rotated_gray_image_meth, x_grid_meth, y_gri
8686
z_contour = rotated_gray_image_meth[y_contour, x_contour] * 256 + 2.5 # Raise slightly
8787
ax_3d.plot(x_contour, y_contour, z_contour, color='black', linewidth=1.5)
8888

89-
ax_3d.scatter(Y_meth, 1280 - X_meth, 256, c='red', s=250, marker='o', depthshade=True,
89+
ax_3d.scatter(y_meth, 1280 - x_meth, 256, c='red', s=250, marker='o', depthshade=True,
9090
label='Centroid')
9191

92-
ax_3d.set_xlabel('X')
93-
ax_3d.set_ylabel('Y')
94-
ax_3d.set_zlabel('Intensity')
92+
# ax_3d.set_xlabel('X')
93+
# ax_3d.set_ylabel('Y')
94+
# ax_3d.set_zlabel('Intensity')
9595
ax_3d.view_init(elev=50, azim=280)
9696
ax_3d.legend(fontsize=20)
9797
plt.savefig(f'{actual_algorithm}wireframe.png')
@@ -113,25 +113,25 @@ def calculate_superpixels_slic(self):
113113
gray_image_2d, image = self.superpixels_images
114114

115115
segments = slic(image, n_segments=self.n_segments, compactness=self.compactness, sigma=5)
116-
X, Y = self.center_of_spot(image, segments)
117-
print('SLIC centroid coordinates are in X = ' + str(X) + ' & Y = ' + str(Y))
116+
x, y = self.center_of_spot(image, segments)
117+
print('SLIC centroid coordinates are in X = ' + str(x) + ' & Y = ' + str(y))
118118

119119
# Show the output of SLIC
120120
plt.rcParams.update({'font.size': 30})
121-
fig = plt.figure("Superpixels -- SLIC (%d segments)" % (self.n_segments), figsize=(11, 12.8))
121+
fig = plt.figure("Superpixels -- SLIC (%d segments)" % self.n_segments, figsize=(11, 12.8))
122122
ax = fig.add_subplot(1, 1, 1)
123123
ax.imshow(np.rot90(mark_boundaries(image, segments)), origin='lower')
124-
plt.plot(Y, 1280-X, marker='o', markersize=15, color='red') # Swap X and Y for the rotated plot
124+
plt.plot(y, 1280-x, marker='o', markersize=15, color='red') # Swap X and Y for the rotated plot
125125
# plt.title("Superpixels -- SLIC (%d segments)" % (self.n_segments))
126-
plt.xlabel("pixeles")
127-
plt.ylabel("pixeles")
126+
plt.xlabel("pixels")
127+
plt.ylabel("pixels")
128128
plt.axis("on")
129129
plt.savefig('SLIC.png')
130130
plt.show()
131131

132132
# Show the output of slic as 3d graphic
133133

134-
h, w = gray_image_2d.shape
134+
# h, w = gray_image_2d.shape
135135

136136
# Rotate image and segments for consistency with 2D plot
137137
rotated_gray_image = np.rot90(gray_image_2d)
@@ -166,7 +166,7 @@ def calculate_superpixels_slic(self):
166166

167167
# Plot the center using a wireframe
168168
self.plot_wireframe("SLIC", rotated_gray_image, x_grid, y_grid, rotated_segments,
169-
h_rot, w_rot, X, Y)
169+
h_rot, w_rot, x, y)
170170

171171

172172
def calculate_superpixels_quickshift(self):
@@ -184,25 +184,25 @@ def calculate_superpixels_quickshift(self):
184184
gray_image_2d, image = self.superpixels_images
185185

186186
segments = quickshift(image, kernel_size=21, max_dist=50, ratio=5)
187-
X, Y = self.center_of_spot(image, segments)
188-
print('Quick-shift centroid coordinates are in X = ' + str(X) + ' & Y = ' + str(Y) )
187+
x, y = self.center_of_spot(image, segments)
188+
print('Quick-shift centroid coordinates are in X = ' + str(x) + ' & Y = ' + str(y) )
189189
# Show the output of Quickshift
190190
plt.rcParams.update({'font.size': 30})
191191
plt.rcParams['figure.figsize'] = 11, 12.8
192192
fig = plt.figure("Superpixels -- Quickshift")
193193
ax = fig.add_subplot(1, 1, 1)
194194
ax.imshow(np.rot90(mark_boundaries(image, segments)), origin='lower')
195-
plt.plot(Y, 1280-X, marker='o', markersize=15, color='red')
195+
plt.plot(y, 1280-x, marker='o', markersize=15, color='red')
196196
# plt.title("Superpixels -- Quickshift")
197-
plt.xlabel("pixeles")
198-
plt.ylabel("pixeles")
197+
plt.xlabel("pixels")
198+
plt.ylabel("pixels")
199199
plt.axis("on")
200200
plt.savefig('Quickshift.png')
201201
plt.show()
202202

203-
# Show the output of quickshift as 3d graphic
203+
# Show the output of quickshift as a 3d graphic
204204

205-
h, w = gray_image_2d.shape
205+
# h, w = gray_image_2d.shape
206206

207207
# Rotate image and segments for consistency with 2D plot
208208
rotated_gray_image = np.rot90(gray_image_2d)
@@ -237,7 +237,7 @@ def calculate_superpixels_quickshift(self):
237237

238238
# Plot the center using a wireframe
239239
self.plot_wireframe("Quickshift", rotated_gray_image, x_grid, y_grid, rotated_segments,
240-
h_rot, w_rot, X, Y)
240+
h_rot, w_rot, x, y)
241241

242242
def calculate_superpixels_felzenszwalb(self):
243243
"""
@@ -254,25 +254,25 @@ def calculate_superpixels_felzenszwalb(self):
254254
gray_image_2d, image = self.superpixels_images
255255

256256
segments = felzenszwalb(image, scale=300, sigma=0.5, min_size=200)
257-
X, Y = self.center_of_spot(image, segments)
258-
print('Felzenszwalb centroid coordinates are in X = ' + str(X) + ' & Y = ' + str(Y) )
257+
x, y = self.center_of_spot(image, segments)
258+
print('Felzenszwalb centroid coordinates are in X = ' + str(x) + ' & Y = ' + str(y) )
259259
# Show the output of Felzenszwalb
260260
plt.rcParams.update({'font.size': 30})
261261
plt.rcParams['figure.figsize'] = 11, 12.8
262262
fig = plt.figure("Superpixels -- Felzenszwalb")
263263
ax = fig.add_subplot(1, 1, 1)
264264
ax.imshow(np.rot90(mark_boundaries(image, segments)), origin='lower')
265-
plt.plot(Y, 1280-X, marker='o', markersize=15, color='red')
265+
plt.plot(y, 1280-x, marker='o', markersize=15, color='red')
266266
# plt.title("Superpixels -- Felzenszwalb")
267-
plt.xlabel("pixeles")
268-
plt.ylabel("pixeles")
267+
plt.xlabel("pixels")
268+
plt.ylabel("pixels")
269269
plt.axis("on")
270270
plt.savefig('Felzenszwalb.png')
271271
plt.show()
272272

273273
# Show the output of felzenszwalb as 3d graphic
274274

275-
h, w = gray_image_2d.shape
275+
# h, w = gray_image_2d.shape
276276

277277
# Rotate image and segments for consistency with 2D plot
278278
rotated_gray_image = np.rot90(gray_image_2d)
@@ -307,7 +307,7 @@ def calculate_superpixels_felzenszwalb(self):
307307
plt.show()
308308
# Plot the center using a wireframe
309309
self.plot_wireframe("Felzenszwalb", rotated_gray_image, x_grid, y_grid, rotated_segments,
310-
h_rot, w_rot, X, Y)
310+
h_rot, w_rot, x, y)
311311

312312
@staticmethod
313313
def center_of_spot(image, segments):
@@ -343,26 +343,26 @@ def center_of_spot(image, segments):
343343
meansum = []
344344
for j in range(arrsiz):
345345
# individualCoor = [coor[0][j],coor[1][j]]#coordenada individual de cda pixel del segemento
346-
coorVal = image[coor[0][j]][coor[1][j]][0] # valaor de cada pixel del segmento
347-
meansum.append(coorVal) # se agrega el valor a un vector
348-
segmentVal = np.mean(meansum) # promedio de valores para cada segmento
349-
values.append(segmentVal) # agrega ese valor a una variable (la media de cada segmento)
346+
coor_val = image[coor[0][j]][coor[1][j]][0] # valaor de cada pixel del segmento
347+
meansum.append(coor_val) # se agrega el valor a un vector
348+
segment_val = np.mean(meansum) # promedio de valores para cada segmento
349+
values.append(segment_val) # agrega ese valor a una variable (la media de cada segmento)
350350
maxsegment = np.where(values == np.amax(values)) # elige segmento con valor maximo
351-
maxS = maxsegment[0] + 1 # compensacion del 0 en el indice del array
352-
maxseg = maxS[0]
351+
max_s = maxsegment[0] + 1 # compensacion del 0 en el indice del array
352+
maxseg = max_s[0]
353353
# print(maxseg)
354-
maxVC = np.where(
354+
max_vc = np.where(
355355
segments == maxseg) # selecciona todas las coordenadas del segmento con valor maximo
356356
# calcular la distancia desde el segmento hasta el centro
357-
arraysz = maxVC[0].shape # dimencion del conjunto de coordenadas del segmento
357+
arraysz = max_vc[0].shape # dimencion del conjunto de coordenadas del segmento
358358
arsz = int(arraysz[0] / 2) # la mitad de ese conjunto
359-
XselectCoor = maxVC[1][arsz] # coordenada intermedia en x
360-
X = XselectCoor
361-
YselectCoor = maxVC[0][arsz] # coordenada intermedia en y
362-
Y = YselectCoor
363-
return X, Y
359+
x_select_coor = max_vc[1][arsz] # coordenada intermedia en x
360+
x = x_select_coor
361+
y_select_coor = max_vc[0][arsz] # coordenada intermedia en y
362+
y = y_select_coor
363+
return x, y
364364

365-
def calculate_centroid(image_path):
365+
def calculate_centroid(fbm_image_path):
366366
"""
367367
Calculates the centroid of the largest object in the provided image.
368368
@@ -372,14 +372,14 @@ def calculate_centroid(image_path):
372372
enhance object detection. Once the largest object is identified via contours,
373373
the centroid is computed using image moments.
374374
375-
:param image_path: Path to the image file.
376-
:type image_path: str
375+
:param fbm_image_path: Path to the image file.
376+
:type fbm_image_path: str
377377
:return: A tuple containing the x and y coordinates of the centroid of the
378378
largest object, or None if no objects are detected.
379379
:rtype: tuple[int, int] | None
380380
"""
381381
# Step 1: Read the image
382-
image = cv2.imread(image_path, cv2.IMREAD_COLOR)
382+
image = cv2.imread(fbm_image_path, cv2.IMREAD_COLOR)
383383

384384
# Step 2: Convert to grayscale
385385
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
@@ -411,7 +411,7 @@ def calculate_centroid(image_path):
411411
cx = int(moments['m10'] / moments['m00']) # X-coordinate of the centroid
412412
cy = int(moments['m01'] / moments['m00']) # Y-coordinate of the centroid
413413
else:
414-
# In case of a single point as an object (unlikely in this case)
414+
# In the case of a single point as an object (unlikely in this case)
415415
cx, cy = 0, 0
416416

417417
print(f"Centroid of the object is at: ({cx}, {cy})")
@@ -430,22 +430,46 @@ def calculate_centroid(image_path):
430430
plt.savefig('FBM.png')
431431
plt.show()
432432

433+
# 3D visualization for FBM
434+
# Prepare rotated grayscale image normalized to [0,1]
435+
rotated_gray_image = np.rot90(gray.astype(np.float32) / 255.0)
436+
h_rot, w_rot = rotated_gray_image.shape
437+
y_grid, x_grid = np.mgrid[0:h_rot, 0:w_rot]
438+
439+
# Build segmentation from morph mask for boundary plotting
440+
label_image = label(morph > 0)
441+
rotated_segments = np.rot90(label_image)
442+
443+
# Plot 3D surface
444+
fig_3d = plt.figure("3D Visualization -- FBM", figsize=(15, 15))
445+
ax_3d = fig_3d.add_subplot(111, projection='3d')
446+
stride = 1
447+
scaled_intensity = rotated_gray_image[::stride, ::stride] * 256
448+
ax_3d.plot_surface(x_grid[::stride, ::stride], y_grid[::stride, ::stride],
449+
scaled_intensity,
450+
cmap='viridis', alpha=0.7, linewidth=0)
451+
plt.savefig('FBM-surface.png')
452+
plt.show()
453+
454+
# Plot wireframe with centroid overlay
455+
Superpixels.plot_wireframe("FBM", rotated_gray_image, x_grid, y_grid, rotated_segments, h_rot, w_rot, cx, cy, 100)
456+
433457
return cx, cy
434458

435-
def calculate_centroid_scikit(image_path):
459+
def calculate_centroid_scikit(ccl_image_path):
436460
"""
437461
Calculates the centroid of the largest connected region in a given image using scikit-image.
438462
439463
This function performs preprocessing on the image, including Gaussian Blur and morphological
440464
operations, and then identifies the largest connected region. The centroid of this region
441465
is computed and displayed. If no connected regions are found, it returns None.
442466
443-
:param str image_path: The path to the image file to be processed.
467+
:param str ccl_image_path: The path to the image file to be processed.
444468
:return: A tuple containing the x and y coordinates of the centroid as integers.
445469
:rtype: tuple[int, int] | None
446470
"""
447471
# Read the image using scikit-image
448-
image = imread(image_path)
472+
image = imread(ccl_image_path)
449473

450474
if len(image.shape) == 2:
451475
gray = image
@@ -491,12 +515,47 @@ def calculate_centroid_scikit(image_path):
491515
# plt.title("Centrid calculated with CCL")
492516
plt.savefig('CCL.png')
493517
plt.show()
518+
519+
# 3D visualization for CCL
520+
# Prepare rotated grayscale image normalized to [0,1]
521+
rotated_gray_image = np.rot90(img_as_float(gray))
522+
h_rot, w_rot = rotated_gray_image.shape
523+
y_grid, x_grid = np.mgrid[0:h_rot, 0:w_rot]
524+
525+
# Use the label_image for boundaries
526+
rotated_segments = np.rot90(label_image)
527+
528+
# Plot the 3D surface with boundaries overlay
529+
fig_3d = plt.figure("3D Visualization -- CCL", figsize=(15, 15))
530+
ax_3d = fig_3d.add_subplot(111, projection='3d')
531+
stride = 1
532+
scaled_intensity = rotated_gray_image[::stride, ::stride] * 256
533+
ax_3d.plot_surface(x_grid[::stride, ::stride], y_grid[::stride, ::stride],
534+
scaled_intensity,
535+
cmap='viridis', alpha=0.7, linewidth=0)
536+
537+
for label_val in np.unique(rotated_segments):
538+
contours = find_contours(rotated_segments, level=label_val)
539+
for contour in contours:
540+
y_contour, x_contour = contour[:, 0].astype(int), contour[:, 1].astype(int)
541+
y_contour = np.clip(y_contour, 0, h_rot - 1)
542+
x_contour = np.clip(x_contour, 0, w_rot - 1)
543+
z_contour = rotated_gray_image[y_contour, x_contour] * 256 + 2.5
544+
ax_3d.plot(x_contour, y_contour, z_contour, color='black', linewidth=1.5)
545+
546+
plt.savefig('CCL-surface.png')
547+
plt.show()
548+
549+
# Plot wireframe with centroid overlay
550+
Superpixels.plot_wireframe("CCL", rotated_gray_image, x_grid, y_grid, rotated_segments, h_rot, w_rot, int(cx),
551+
int(cy), 1)
552+
494553
return int(cx), int(cy)
495554

496555

497556
if __name__ == '__main__':
498-
image_path = "images/l0/image100.png"
499-
superpixels_centroid = Superpixels(image_path, 50, 10)
557+
path_to_image = "images/l0/image100.png"
558+
superpixels_centroid = Superpixels(path_to_image, 50, 10)
500559

501560
# Superpixels
502561
# SLIC
@@ -523,14 +582,14 @@ def calculate_centroid_scikit(image_path):
523582
# cv2 centroid
524583

525584
reset = time.time()
526-
calculate_centroid(image_path)
585+
calculate_centroid(path_to_image)
527586
end = time.time()
528587
cv2_centroid_time = end - reset
529588

530589
# Scikit centroid
531590

532591
reset = time.time()
533-
calculate_centroid_scikit(image_path)
592+
calculate_centroid_scikit(path_to_image)
534593
end = time.time()
535594
scikit_centroid_time = end - reset
536595

main.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def process_local_dataset(path):
1515
image_files = []
1616
for root, dirs, files in os.walk(path):
1717
for file in files:
18-
if file.endswith(".png"):
18+
if file.endswith(".png") or file.endswith(".jpg"):
1919
# Skip result images (those that are named after algorithms)
2020
if not any(file.startswith(alg_result) for alg_result in ["CCL", "FBM", "Felzenszwalb", "Quickshift", "SLIC"]):
2121
# Use forward slashes for compatibility with the existing code
@@ -122,8 +122,10 @@ def process_local_dataset(path):
122122
actual_path = []
123123
threads = [] # List to store threads
124124
for actual_path in image_directory_paths:
125-
process_local_dataset(actual_path)
126-
# thread = Thread(target=process_local_dataset, args=(actual_path,))
127-
# threads.append(thread)
128-
# thread.start()
125+
# process_local_dataset(actual_path)
126+
thread = Thread(target=process_local_dataset, args=(actual_path,))
127+
threads.append(thread)
128+
thread.start()
129129

130+
for i_thread in threads:
131+
i_thread.join()

0 commit comments

Comments
 (0)