Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ __pycache__/

# Coverage
.coverage

# Pipeline output data (HDF5 results, large arrays)
examples/results/
132 changes: 1 addition & 131 deletions Untitled.ipynb

Large diffs are not rendered by default.

75 changes: 37 additions & 38 deletions XSpect/XSpect_Processor/XSpectDetectorProcessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
import matplotlib.pyplot as plt
from sklearn.cluster import DBSCAN


class XSpectDetectorProcessor:
"""
Processor for 2D detector data to perform image normalization, edge detection,
Processor for 2D detector data to perform image normalization, edge detection,
and alignment of signals.
"""

def __init__(self, image):
"""
Initialize the processor with the image.
Expand All @@ -23,7 +24,7 @@ def __init__(self, image):
self.image_8bit = self.normalize_to_8bit(image)
self.edges = None
self.aligned_image = None

def normalize_to_8bit(self, image):
"""
Normalize image data to 8-bit range [0, 255].
Expand All @@ -46,7 +47,7 @@ def normalize_to_8bit(self, image):

# Convert to 8-bit values
image_8bit = normalized_image.astype(np.uint8)

return image_8bit

def detect_edges(self, low_threshold=30, high_threshold=100):
Expand All @@ -68,7 +69,6 @@ def detect_edges(self, low_threshold=30, high_threshold=100):
self.edges = cv2.Canny(self.image_8bit, low_threshold, high_threshold)
return self.edges


def find_optimal_rotation_angle(self):
"""
Calculate the optimal rotation angle to align the signals.
Expand All @@ -77,56 +77,55 @@ def find_optimal_rotation_angle(self):
-------
float
Optimal rotation angle in degrees.

Raises
------
ValueError
If edges have not been detected.
"""
if self.edges is None:
raise ValueError("Edges have not been detected. Call `detect_edges` first.")

# Find all non-zero (signal) pixels
non_zero_coords = np.column_stack(np.nonzero(self.edges))
#Identifying multiple XES edges.

# Identifying multiple XES edges.
clustering = DBSCAN(eps=5, min_samples=10).fit(non_zero_coords)
labels = clustering.labels_

unique_labels = set(labels)
angles = []

cluster_sizes = []

for label in unique_labels:
if label == -1:
# -1 indicates noise
continue

cluster_coords = non_zero_coords[labels == label]

mean = np.mean(cluster_coords, axis=0)
centered_coords = cluster_coords - mean
covariance_matrix = np.cov(centered_coords, rowvar=False)
eigenvalues, eigenvectors = np.linalg.eigh(covariance_matrix)



principal_vector = eigenvectors[:, np.argmax(eigenvalues)]


angle = np.arctan2(principal_vector[1], principal_vector[0])



angle = np.arctan2(principal_vector[0], principal_vector[1])

angle_degrees = np.degrees(angle)
angles.append(angle_degrees)
cluster_sizes.append(len(cluster_coords))

# Weight by cluster size so small artifacts don't bias the result
optimal_angle = float(np.average(angles, weights=cluster_sizes))
self.angles = angles


optimal_angle = np.mean(angles)
self.angles=angles

if optimal_angle > 90:
optimal_angle -= 180
elif optimal_angle < -90:
optimal_angle += 180

return optimal_angle

def align_image(self):
Expand All @@ -141,7 +140,7 @@ def align_image(self):
optimal_angle = self.find_optimal_rotation_angle()
self.aligned_image = rotate(self.image, -optimal_angle, reshape=False)
return self.aligned_image

def plot_images(self):
"""
Plot the original image, edge-detected image, and aligned image.
Expand All @@ -155,22 +154,22 @@ def plot_images(self):
raise ValueError("Edges have not been detected. Call `detect_edges` first.")
if self.aligned_image is None:
raise ValueError("Image has not been aligned. Call `align_image` first.")
fig, axes = plt.subplots(1, 3, figsize=(9, 3),dpi=100)

fig, axes = plt.subplots(1, 3, figsize=(9, 3), dpi=100)

# Original image
axes[0].imshow(self.image, cmap='RdBu')
axes[0].imshow(self.image, cmap="RdBu")
axes[0].set_title("Original Image")
axes[0].axis('off')
axes[0].axis("off")

# Edge-detected image
axes[1].imshow(self.edges, cmap='gray')
axes[1].imshow(self.edges, cmap="gray")
axes[1].set_title("XES Detected Image")
axes[1].axis('off')
axes[1].axis("off")

# Aligned image
axes[2].imshow(self.aligned_image, cmap='RdBu')
axes[2].imshow(self.aligned_image, cmap="RdBu")
axes[2].set_title("Aligned Image")
axes[2].axis('off')
axes[2].axis("off")
plt.tight_layout()
plt.show()
plt.show()
1 change: 1 addition & 0 deletions XSpect/analysis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@
import XSpect.analysis.spectroscopy # noqa: F401
import XSpect.analysis.xes # noqa: F401
import XSpect.analysis.xas # noqa: F401
import XSpect.analysis.droplet # noqa: F401
Loading
Loading