Agentic-Defect-Synthesis / ArtiAgent - DefectFill /src /align_image_to_reference - Copy.py
cck-0702's picture
Clean commit without binary (image) files
c8c00f0
Raw
History Blame Contribute Delete
5.87 kB
import os
import cv2
import numpy as np
from flask import Flask, request, jsonify, send_file
from werkzeug.utils import secure_filename
app = Flask(__name__)
# ==============================================================================
# [CONFIG: STORAGE DIRECTORIES]
# ------------------------------------------------------------------------------
# Modify these paths to change where incoming raw uploads and processed output
# images are stored on your server's disk.
# ==============================================================================
UPLOAD_FOLDER = os.path.join(os.getcwd(), "storage", "inputs") # <--- INPUT DIRECTORY
OUTPUT_FOLDER = os.path.join(os.getcwd(), "storage", "outputs") # <--- OUTPUT DIRECTORY
app.config["UPLOAD_FOLDER"] = UPLOAD_FOLDER
app.config["OUTPUT_FOLDER"] = OUTPUT_FOLDER
# Ensure local directories exist on startup
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
os.makedirs(OUTPUT_FOLDER, exist_ok=True)
def align_image_to_reference(ref_path: str, target_path: str, output_path: str):
"""Aligns target_path image to match ref_path image geometry and saves to output_path."""
ref_img = cv2.imread(ref_path)
targ_img = cv2.imread(target_path)
if ref_img is None or targ_img is None:
raise ValueError("Could not read input images from storage.")
# Convert to grayscale for SIFT feature extraction
gray_ref = cv2.cvtColor(ref_img, cv2.COLOR_BGR2GRAY)
gray_targ = cv2.cvtColor(targ_img, cv2.COLOR_BGR2GRAY)
# 1. Detect SIFT features
sift = cv2.SIFT_create()
kp_ref, des_ref = sift.detectAndCompute(gray_ref, None)
kp_targ, des_targ = sift.detectAndCompute(gray_targ, None)
if des_ref is None or des_targ is None:
raise ValueError("Failed to extract keypoints from one or both images.")
# 2. Match keypoints using FLANN
INDEX_KDTREE = 1
flann = cv2.FlannBasedMatcher(
dict(algorithm=INDEX_KDTREE, trees=5), dict(checks=50)
)
matches = flann.knnMatch(des_ref, des_targ, k=2)
# 3. Apply Lowe's ratio test to filter matches
good_matches = [m for m, n in matches if m.distance < 0.7 * n.distance]
if len(good_matches) < 10:
raise ValueError("Insufficient matching features found between images.")
# 4. Extract keypoint coordinates
src_pts = np.float32([kp_ref[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2)
dst_pts = np.float32([kp_targ[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2)
# 5. Compute Affine Transformation Matrix (rigid: rotation, scale, translation)
matrix, _ = cv2.estimateAffinePartial2D(dst_pts, src_pts, method=cv2.RANSAC)
if matrix is None:
raise ValueError("Failed to compute valid alignment transformation matrix.")
# 6. Warp target image to match reference frame dimensions
h, w = ref_img.shape[:2]
aligned_img = cv2.warpAffine(
targ_img,
matrix,
(w, h),
flags=cv2.INTER_LANCZOS4,
borderMode=cv2.BORDER_CONSTANT,
borderValue=(0, 0, 0)
)
# ==============================================================================
# [OUTPUT IMAGE STORAGE LOCATION - WRITE TO DISK]
# ------------------------------------------------------------------------------
# The rotated/aligned image is saved here to output_path
# ==============================================================================
cv2.imwrite(output_path, aligned_img)
@app.route("/align", methods=["POST"])
def align_endpoint():
# Validate request payload
if "reference" not in request.files or "target" not in request.files:
return jsonify({"error": "Missing 'reference' or 'target' file in request form-data."}), 400
ref_file = request.files["reference"]
target_file = request.files["target"]
if ref_file.filename == "" or target_file.filename == "":
return jsonify({"error": "No file selected."}), 400
# Sanitize filenames
ref_name = secure_filename(ref_file.filename)
target_name = secure_filename(target_file.filename)
# ==============================================================================
# [INPUT IMAGE STORAGE LOCATION - SAVE RECEIVED FILES]
# ------------------------------------------------------------------------------
# Input files are saved into 'app.config["UPLOAD_FOLDER"]'
# ==============================================================================
input_ref_path = os.path.join(app.config["UPLOAD_FOLDER"], f"ref_{ref_name}")
input_target_path = os.path.join(app.config["UPLOAD_FOLDER"], f"target_{target_name}")
ref_file.save(input_ref_path) # <-- Input Reference saved here
target_file.save(input_target_path) # <-- Input Target saved here
# ==============================================================================
# ==============================================================================
# [OUTPUT IMAGE STORAGE LOCATION - DEFINE TARGET PATH]
# ------------------------------------------------------------------------------
# Rotated image path in 'app.config["OUTPUT_FOLDER"]'
# ==============================================================================
output_aligned_path = os.path.join(app.config["OUTPUT_FOLDER"], f"aligned_{target_name}")
# ==============================================================================
try:
# Run alignment pipeline
align_image_to_reference(input_ref_path, input_target_path, output_aligned_path)
# Return the processed image directly in the response
return send_file(output_aligned_path, mimetype="image/png")
except Exception as e:
return jsonify({"status": "error", "message": str(e)}), 500
if __name__ == "__main__":
# Run API server on http://0.0.0.0:5000
app.run(host="0.0.0.0", port=5000, debug=True)