Bên dưới là giao diện mình cần xây dựng
Công cụ filter giống với flask Để thay đổi input thành dạng range (thanh trượt) và sử dụng kỹ thuật “debounce” để giảm thiểu số lần gọi API khi người dùng tương tác với các thanh trượt, chúng ta có thể làm như sau:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Image Transformations</title> <style> .container { max-width: 800px; margin: 0 auto; text-align: center; } img { margin-top: 20px; border: 2px solid #ddd; } .params { margin: 20px 0; } label { margin-right: 10px; } input[type="range"] { width: 200px; } button { margin-top: 10px; padding: 10px 20px; cursor: pointer; } .output { font-weight: bold; margin-left: 10px; } </style> <script> // Hàm debounce để hạn chế gọi API liên tục function debounce(func, wait) { let timeout; return function(...args) { const context = this; clearTimeout(timeout); timeout = setTimeout(() => func.apply(context, args), wait); }; } // Hàm để upload ảnh function uploadImage() { const fileInput = document.getElementById('fileInput'); const formData = new FormData(); formData.append('file', fileInput.files[0]); fetch('/api/upload', { method: 'POST', body: formData }) .then(response => response.json()) .then(data => { if (data.image_path) { // Hiển thị ảnh sau khi upload thành công document.getElementById('image').src = data.image_path; document.getElementById('imagePath').value = data.image_path; } else { console.error(data.error); } }) .catch(error => console.error('Error:', error)); } // Hàm để áp dụng transform ảnh với debounce const applyTransform = debounce(function() { const imagePath = document.getElementById('imagePath').value; const p = parseFloat(document.getElementById('probability').value); const limit = parseInt(document.getElementById('limit').value); const transformType = document.querySelector('input[name="transform"]:checked').value; if (!imagePath) { console.error('Image path is not set'); return; } fetch('/api/transform', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ transform: transformType, image_path: imagePath, params: { "p": p, "limit": limit } }) }) .then(response => response.json()) .then(data => { if (data.processed_image_path) { // Thêm chuỗi ngẫu nhiên vào URL để tránh cache const randomParam = `?t=${new Date().getTime()}`; // Hiển thị ảnh sau khi biến đổi document.getElementById('image').src = data.processed_image_path + randomParam; } else { console.error(data.error); } }) .catch(error => console.error('Error:', error)); }, 500); // Debounce time set to 500ms // Cập nhật giá trị hiển thị khi thay đổi range function updateValue(id, value) { document.getElementById(id).innerText = value; applyTransform(); // Gọi lại hàm debounce khi giá trị thay đổi } </script> </head> <body> <div class="container"> <h1>Upload and Transform Image</h1> <!-- Form để tải lên ảnh --> <input type="file" id="fileInput" accept="image/*"> <button onclick="uploadImage()">Upload</button> <!-- Ẩn đường dẫn ảnh --> <input type="hidden" id="imagePath" value=""> <!-- Các tham số cho biến đổi --> <div class="params"> <label for="probability">Probability (p):</label> <input type="range" id="probability" min="0" max="1" step="0.1" value="1.0" oninput="updateValue('probabilityValue', this.value)"> <span id="probabilityValue" class="output">1.0</span> </div> <div class="params"> <label for="limit">Limit (Degrees):</label> <input type="range" id="limit" min="0" max="360" step="1" value="90" oninput="updateValue('limitValue', this.value)"> <span id="limitValue" class="output">90</span> </div> <!-- Các nút transform với radio buttons để chọn --> <div class="params"> <label for="transform">Transformation:</label> <label><input type="radio" name="transform" value="horizontal_flip" checked> Horizontal Flip</label> <label><input type="radio" name="transform" value="vertical_flip"> Vertical Flip</label> <label><input type="radio" name="transform" value="rotate_90"> Rotate</label> </div> <!-- Hiển thị ảnh --> <div> <img id="image" src="https://placehold.co/600x400" alt="Image will appear here" style="max-width: 100%; height: auto;"> </div> </div> </body> </html>
Thanh trượt (range inputs):
Debounce:
Hiển thị giá trị thanh trượt:
Các tùy chọn biến đổi:
Với giao diện mới này, bạn có thể thay đổi các tham số như xác suất (p) và giới hạn góc quay (limit) một cách dễ dàng và trực quan bằng cách sử dụng thanh trượt. Nhờ kỹ thuật debounce, API sẽ chỉ được gọi khi người dùng đã dừng thao tác trên thanh trượt trong một khoảng thời gian ngắn, tránh việc gọi API quá nhiều lần.
Chúng ta có thể thêm nhiều phép biến đổi (transformation) và bộ lọc (filter) để tăng khả năng xử lý hình ảnh. Albumentations hỗ trợ nhiều loại phép biến đổi và bộ lọc như Blur, Sharpen, BrightnessContrast, và GaussianNoise. Chúng ta sẽ thêm các tùy chọn mới cho phép người dùng áp dụng nhiều biến đổi cùng lúc.
Dưới đây là các bước để cập nhật:
Thêm các phép biến đổi và bộ lọc như:
Cập nhật giao diện để hiển thị thêm các tùy chọn này và điều chỉnh các tham số liên quan.
Sẽ gồm 2 api chính là upload và transform có tác dụng render lại ảnh khi thay đổi tham số
import os import cv2 from flask import Blueprint, jsonify, render_template, url_for,request, redirect,session from matplotlib import pyplot as plt from modules import image_processor from modules.image_processor import ImageProcessor from modules.main.main import show_visualize from werkzeug.utils import secure_filename from modules.image_processor import ImageProcessor import albumentations as A # Tạo Blueprint main = Blueprint('main', __name__) # Đường dẫn tới folder static để lưu ảnh STATIC_FOLDER = 'static/processed_images' # Đường dẫn để lưu ảnh upload PROCESSED_FOLDER = 'static/processed_images' UPLOAD_FOLDER = 'static/uploads' ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg'} # Đảm bảo thư mục tồn tại os.makedirs(UPLOAD_FOLDER, exist_ok=True) os.makedirs(PROCESSED_FOLDER, exist_ok=True) # Khởi tạo đối tượng xử lý hình ảnh image_processor = ImageProcessor() # Kiểm tra định dạng file ảnh hợp lệ def allowed_file(filename): return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS @main.route('/', methods=['GET']) def homepage(): return render_template('main/index.html') # Route để upload ảnh @main.route('/api/upload', methods=['POST']) def upload_image(): if 'file' not in request.files: return jsonify({"error": "No file part"}), 400 file = request.files['file'] if file.filename == '': return jsonify({"error": "No selected file"}), 400 # Lưu ảnh vào thư mục uploads if file: filename = secure_filename(file.filename) image_path = os.path.join(UPLOAD_FOLDER, filename) file.save(image_path) return jsonify({"message": "File uploaded successfully", "image_path": image_path}), 200 @main.route('/api/transform', methods=['POST']) def transform_image(): # Lấy toàn bộ payload từ yêu cầu data = request.json # Xử lý các thông tin cơ bản transform_type = data.get('transform') image_path = data.get('image_path') params = data.get('params', {}) if not image_path or not os.path.exists(image_path): return jsonify({"error": "Invalid image path"}), 400 # Lấy giá trị p và limit từ params (hoặc giá trị mặc định nếu không tồn tại) p = params.get('p', 1.0) limit = params.get('limit', 90) # Đọc và xử lý ảnh image = cv2.imread(image_path) image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # Áp dụng biến đổi dựa trên loại được chọn bằng Albumentations với params transform = None if transform_type == 'horizontal_flip': transform = A.HorizontalFlip(p=p) elif transform_type == 'vertical_flip': transform = A.VerticalFlip(p=p) elif transform_type == 'rotate_90': transform = A.Rotate(limit=(limit, limit), p=p) if transform: augmented = transform(image=image) image = augmented['image'] # Lưu ảnh đã xử lý vào thư mục processed_images processed_image_path = os.path.join(PROCESSED_FOLDER, 'processed_image.jpg') plt.imsave(processed_image_path, image) return jsonify({"message": "Image transformed successfully", "processed_image_path": processed_image_path}), 200
Kiểm tra log
Leave A Comment