Files
backend/xiaci.py
2025-07-01 18:06:39 +08:00

229 lines
8.7 KiB
Python

import cv2
import numpy as np
from tkinter import Tk, Button, filedialog, Label, messagebox, Label, messagebox, StringVar, OptionMenu
from PIL import Image, ImageTk
import os
import cv2
import numpy as np
from sklearn.mixture import GaussianMixture
import matplotlib.pyplot as plt
def preprocess_image(image_path):
image = cv2.imread(image_path)
if image is None:
raise FileNotFoundError(f"Image not found or unable to read: {image_path}")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
return blurred, image
def segment_crystal(blurred_image):
_, binary = cv2.threshold(blurred_image, 30, 255, cv2.THRESH_BINARY)
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if len(contours) == 0:
raise ValueError("No crystal detected in the image.")
crystal_contour = max(contours, key=cv2.contourArea)
mask = np.zeros_like(blurred_image)
cv2.drawContours(mask, [crystal_contour], -1, 255, thickness=cv2.FILLED)
segmented_crystal = cv2.bitwise_and(blurred_image, blurred_image, mask=mask)
return segmented_crystal, crystal_contour, binary
def detect_defects_GMM(segmented_crystal, crystal_contour):
# Extract the region of interest (ROI) using the crystal contour
mask = np.zeros_like(segmented_crystal)
cv2.drawContours(mask, [crystal_contour], -1, 255, thickness=cv2.FILLED)
roi = cv2.bitwise_and(segmented_crystal, segmented_crystal, mask=mask)
# Flatten the ROI to a 1D array of pixel intensities
pixel_intensities = roi.flatten()
pixel_intensities = pixel_intensities[pixel_intensities > 0] # Remove background pixels
if len(pixel_intensities) < 10:
raise ValueError("Not enough data points to fit Gaussian Mixture Model.")
# Reshape for GMM
X = pixel_intensities.reshape(-1, 1)
# Fit a Gaussian Mixture Model with two components
gmm = GaussianMixture(n_components=2, random_state=0).fit(X)
# Get the means and covariances of the fitted Gaussians
means = gmm.means_.flatten()
covars = gmm.covariances_.flatten()
# Determine which component corresponds to high brightness
high_brightness_mean_index = np.argmax(means)
high_brightness_mean = means[high_brightness_mean_index]
high_brightness_covar = covars[high_brightness_mean_index]
# Calculate the probability density function (PDF) values for each pixel intensity
pdf_values = gmm.score_samples(X)
# Set a threshold to identify high brightness regions
threshold = np.percentile(pdf_values, 98) # Adjust this threshold as needed
# Identify high brightness pixels
high_brightness_pixels = X[pdf_values >= threshold].flatten()
# Find contours corresponding to high brightness regions
_, binary_high_brightness = cv2.threshold(roi, int(high_brightness_mean), 255, cv2.THRESH_BINARY)
contours, _ = cv2.findContours(binary_high_brightness, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
defects = []
for contour in contours:
perimeter = cv2.arcLength(contour, True)
if perimeter > 5:
defects.append(contour)
# Create a black image with the same shape as the original image
binary_defects = np.zeros_like(segmented_crystal, dtype=np.uint8)
# Draw high brightness regions on the binary defects image
for y in range(segmented_crystal.shape[0]):
for x in range(segmented_crystal.shape[1]):
if mask[y, x] != 0 and segmented_crystal[y, x] >= high_brightness_mean:
binary_defects[y, x] = 255
# Generate a GMM plot image
fig, ax = plt.subplots(figsize=(4.8, 3.6))
ax.hist(pixel_intensities, bins=50, density=True, alpha=0.5, color='gray', edgecolor='black')
x_vals = np.linspace(0, 255, 1000).reshape(-1, 1)
log_prob = gmm.score_samples(x_vals)
responsibilities = gmm.predict_proba(x_vals)
# Convert the plot to an image
fig.canvas.draw()
gmm_plot_img = np.frombuffer(fig.canvas.tostring_rgb(), dtype=np.uint8)
gmm_plot_img = gmm_plot_img.reshape(fig.canvas.get_width_height()[::-1] + (3,))
plt.close(fig)
cv2.imshow("gmm",gmm_plot_img)
cv2.waitKey(10)
return defects, binary_defects
def detect_defects(segmented_crystal, crystal_contour):
edges = cv2.Canny(segmented_crystal, 30, 90)
mask = np.zeros_like(edges)
cv2.drawContours(mask, [crystal_contour], -1, 255, thickness=cv2.FILLED)
inverted_mask = mask#cv2.bitwise_not(mask)
defects_edges = cv2.bitwise_and(edges, inverted_mask)
contours, _ = cv2.findContours(defects_edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
defects = []
for contour in contours:
perimeter = cv2.arcLength(contour, True)
if perimeter > 5:
defects.append(contour)
return defects,defects_edges
def calculate_total_area(crystal_contour):
total_area = cv2.contourArea(crystal_contour)
return total_area
def score_defects(defects, total_area):
defect_area = sum(cv2.contourArea(defect) for defect in defects)
score = (defect_area / total_area) * 100
return score
def resize_image_for_display(image, width=480, height=360):
return cv2.resize(image, (width, height))
def process_image():
global original_image, binary_image, image_with_defects, segmented_crystal, crystal_contour, defects, total_area, score
# Get the directory of the current script
script_dir = os.path.dirname(os.path.abspath(__file__))
# Open file dialog starting from the script's directory
image_path = filedialog.askopenfilename(initialdir=script_dir, title="Select an Image",
filetypes=(("JPEG files", "*.jpg *.jpeg"), ("PNG files", "*.png"), ("All files", "*.*")))
if not image_path:
return
try:
blurred_image, original_image = preprocess_image(image_path)
segmented_crystal, crystal_contour, binary_image = segment_crystal(blurred_image)
selected_algorithm = algorithm_var.get()
if selected_algorithm == '1':
defects,binary_image = detect_defects(segmented_crystal, crystal_contour)
elif selected_algorithm == '2':
defects,binary_image = detect_defects_GMM(segmented_crystal, crystal_contour)
total_area = calculate_total_area(crystal_contour)
score = score_defects(defects, total_area)
result_text = (
f"Detected {len(defects)} defects.\n"
f"Total Defect Area: {sum(cv2.contourArea(defect) for defect in defects):.2f} pixels\n"
f"Total Crystal Area: {total_area} pixels\n"
f"Defect Score (% of Total Area): {score:.2f}%"
)
result_label.config(text=result_text)
print(result_text)
image_with_defects = cv2.drawContours(original_image.copy(), defects, -1, (0, 255, 0), 2)
update_images()
except Exception as e:
messagebox.showerror("Error", str(e))
def update_images():
images = [
("Original Image", resize_image_for_display(original_image)),
("Binary Defect Image", resize_image_for_display(binary_image)),
("Image with Defects", resize_image_for_display(image_with_defects)),
("Segmented Crystal", resize_image_for_display(segmented_crystal))
]
for i, (label_text, img) in enumerate(images):
label = labels[i]
label.config(text=label_text)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img_pil = Image.fromarray(img_rgb)
img_tk = ImageTk.PhotoImage(img_pil)
image_labels[i].config(image=img_tk)
image_labels[i].image = img_tk
root = Tk()
root.title("Crystal Defect Detection")
original_image = None
binary_image = None
image_with_defects = None
segmented_crystal = None
crystal_contour = None
defects = []
total_area = 0
score = 0
labels = [Label(root, text=f"Image {i+1}") for i in range(4)]
image_labels = [Label(root) for i in range(4)]
# Add a label to display the detection results
result_label = Label(root, text="", justify='left')
button_open = Button(root, text="Open Image", command=process_image)
# Algorithm selection dropdown menu
algorithm_var = StringVar(value='1') # Default to algorithm 1
algorithm_menu = OptionMenu(root, algorithm_var, '1', '2')
algorithm_menu.config(width=10)
# Arrange labels and images in a 2x2 grid
for i in range(4):
row = i // 2
col = i % 2
labels[i].grid(row=row*2, column=col*2, padx=10, pady=5)
image_labels[i].grid(row=row*2+1, column=col*2, padx=10, pady=5)
# Place the result label below the images
result_label.grid(row=4, column=0, columnspan=2, padx=10, pady=10)
button_open.grid(row=5, column=0, columnspan=2, padx=10, pady=10)
algorithm_menu.grid(row=5, column=1, columnspan=2, padx=30, pady=10)
root.mainloop()