| import cv2
|
| import numpy as np
|
| import gradio as gr
|
| from sklearn.cluster import KMeans
|
| from scipy.fftpack import dct, idct
|
| from skimage.morphology import skeletonize
|
|
|
|
|
| def to_greyscale(image):
|
| return cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
|
|
|
|
| def to_negative(image):
|
| return 255 - image
|
|
|
|
|
| def adjust_color(image, r_factor=1.0, g_factor=1.0, b_factor=1.0):
|
| b, g, r = cv2.split(image)
|
| b = np.clip(b * b_factor, 0, 255).astype(np.uint8)
|
| g = np.clip(g * g_factor, 0, 255).astype(np.uint8)
|
| r = np.clip(r * r_factor, 0, 255).astype(np.uint8)
|
| return cv2.merge([b, g, r])
|
|
|
|
|
| def flip_image(image, direction="horizontal"):
|
| flip_code = {'horizontal': 1, 'vertical': 0, 'diagonal': -1}
|
| return cv2.flip(image, flip_code.get(direction, 1))
|
|
|
|
|
| def translate(image, x_offset, y_offset):
|
| rows, cols = image.shape[:2]
|
| M = np.float32([[1, 0, x_offset],
|
| [0, 1, y_offset]])
|
| return cv2.warpAffine(image, M, (cols, rows))
|
|
|
|
|
| def scale_image(image, width=None, height=None, keep_aspect_ratio=True):
|
| h, w = image.shape[:2]
|
| if keep_aspect_ratio:
|
| if width is not None:
|
| ratio = width / float(w)
|
| height = int(h * ratio)
|
| elif height is not None:
|
| ratio = height / float(h)
|
| width = int(w * ratio)
|
| if width is None:
|
| width = w
|
| if height is None:
|
| height = h
|
| return cv2.resize(image, (width, height), interpolation=cv2.INTER_AREA)
|
|
|
|
|
| def rotate(image, angle, clockwise=True):
|
| rows, cols = image.shape[:2]
|
| center = (cols / 2, rows / 2)
|
| if clockwise:
|
| angle = -angle
|
| M = cv2.getRotationMatrix2D(center, angle, 1.0)
|
| rotated = cv2.warpAffine(image, M, (cols, rows))
|
| return rotated
|
|
|
|
|
| def crop(image, x, y, w, h):
|
| return image[y:y+h, x:x+w]
|
|
|
|
|
| def blend(image1, image2, alpha=0.5):
|
| if image2 is None:
|
| return image1
|
| if image1.shape != image2.shape:
|
| image2 = cv2.resize(image2, (image1.shape[1], image1.shape[0]))
|
| return cv2.addWeighted(image1, alpha, image2, 1 - alpha, 0)
|
|
|
|
|
| def adjust_brightness_contrast(image, brightness=50, contrast=1.2):
|
| hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
|
| h, s, v = cv2.split(hsv)
|
| v = np.clip(v + brightness, 0, 255).astype(np.uint8)
|
| final_hsv = cv2.merge((h, s, v))
|
| image_brightness = cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR)
|
|
|
| image_float = image_brightness.astype(np.float32)
|
| image_contrast = np.clip(contrast * (image_float - 128) + 128, 0, 255)
|
| return image_contrast.astype(np.uint8)
|
|
|
|
|
| def color_filter(image, lower_bound, upper_bound):
|
| hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
|
| mask = cv2.inRange(hsv, lower_bound, upper_bound)
|
| return cv2.bitwise_and(image, image, mask=mask)
|
|
|
| def apply_sepia(image):
|
| image_float = image.astype(np.float32)
|
| sepia_kernel = np.array([
|
| [0.272, 0.534, 0.131],
|
| [0.349, 0.686, 0.168],
|
| [0.393, 0.769, 0.189]
|
| ])
|
| sepia_image = image_float @ sepia_kernel.T
|
| sepia_image = np.clip(sepia_image, 0, 255)
|
| return sepia_image.astype(np.uint8)
|
|
|
| def apply_cyanotype(image):
|
| gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
| cyan_image = np.zeros_like(image)
|
| cyan_image[:, :, 0] = np.clip(gray / 2, 0, 255)
|
| cyan_image[:, :, 1] = np.clip(gray, 0, 255)
|
| cyan_image[:, :, 2] = 255
|
| return cyan_image
|
|
|
|
|
| def add_border(image, top, bottom, left, right, color_str="(0,0,0)"):
|
| color = tuple(map(int, color_str.strip("()").split(",")))
|
| return cv2.copyMakeBorder(image, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color)
|
|
|
|
|
| def overlay(image1, image2, x, y, alpha=0.5):
|
| if image2 is None:
|
| return image1
|
| h1, w1 = image1.shape[:2]
|
| h2, w2 = image2.shape[:2]
|
| if y + h2 > h1 or x + w2 > w1:
|
|
|
| new_h = min(h2, h1 - y)
|
| new_w = min(w2, w1 - x)
|
| image2 = cv2.resize(image2, (new_w, new_h))
|
| h2, w2 = new_h, new_w
|
| roi = image1[y:y+h2, x:x+w2]
|
| blended = cv2.addWeighted(roi, 1 - alpha, image2, alpha, 0)
|
| image1[y:y+h2, x:x+w2] = blended
|
| return image1
|
|
|
| def pixelwise_operation(image1, image2, operation, brightness_factor=1.2):
|
| if operation == 'Bitwise (NOT)':
|
|
|
| return cv2.bitwise_not(image1)
|
|
|
| if image2 is None:
|
| return image1
|
|
|
|
|
| if image1.shape != image2.shape:
|
| image2 = cv2.resize(image2, (image1.shape[1], image1.shape[0]))
|
|
|
| if operation == 'Add':
|
| return cv2.add(image1, image2)
|
| elif operation == 'Subtract':
|
| return cv2.subtract(image1, image2)
|
| elif operation == 'Multiply':
|
| return cv2.multiply(image1, image2)
|
| elif operation == 'Divide':
|
| epsilon = 1e-5
|
| image2 = image2.astype(np.float32) + epsilon
|
| image1 = image1.astype(np.float32)
|
| divided = cv2.divide(image1, image2)
|
| divided_normalized = cv2.normalize(divided, None, 0, 255, cv2.NORM_MINMAX)
|
| return cv2.convertScaleAbs(divided_normalized, alpha=brightness_factor, beta=0)
|
| elif operation == 'Bitwise (AND)':
|
| return cv2.bitwise_and(image1, image2)
|
| elif operation == 'Bitwise (OR)':
|
| return cv2.bitwise_or(image1, image2)
|
| elif operation == 'Bitwise (XOR)':
|
| return cv2.bitwise_xor(image1, image2)
|
| else:
|
| return image1
|
|
|
|
|
| def fft_image(image: np.ndarray):
|
| if len(image.shape) == 3:
|
| image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
| f = np.fft.fft2(image)
|
| fshift = np.fft.fftshift(f)
|
| magnitude = 20 * np.log(np.abs(fshift) + 1e-8)
|
| return cv2.normalize(magnitude, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)
|
|
|
|
|
| def mean_blur(image: np.ndarray, ksize: int = 3):
|
| return cv2.blur(image, (ksize, ksize))
|
|
|
|
|
| def gaussian_blur(image: np.ndarray, ksize: int = 3, sigma: float = 1.0):
|
| if ksize % 2 == 0:
|
| ksize += 1
|
| return cv2.GaussianBlur(image, (ksize, ksize), sigma)
|
|
|
|
|
| def median_blur(image: np.ndarray, ksize: int = 3):
|
| if ksize % 2 == 0:
|
| ksize += 1
|
| return cv2.medianBlur(image, ksize)
|
|
|
|
|
| def sobel_edge(image: np.ndarray, ksize: int = 3):
|
| if ksize % 2 == 0:
|
| ksize += 1
|
| if len(image.shape) == 3:
|
| image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
| sobelx = cv2.Sobel(image, cv2.CV_64F, 1, 0, ksize=ksize)
|
| sobely = cv2.Sobel(image, cv2.CV_64F, 0, 1, ksize=ksize)
|
| magnitude = cv2.magnitude(sobelx, sobely)
|
| return np.clip(magnitude, 0, 255).astype(np.uint8)
|
|
|
|
|
| def canny_edge(image: np.ndarray, t1: float, t2: float):
|
| if len(image.shape) == 3:
|
| image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
| return cv2.Canny(image, t1, t2)
|
|
|
|
|
| def laplacian_edge(image: np.ndarray, ksize: int = 3):
|
| if ksize % 2 == 0:
|
| ksize += 1
|
| if len(image.shape) == 3:
|
| image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
| lap = cv2.Laplacian(image, cv2.CV_64F, ksize=ksize)
|
| return np.clip(np.abs(lap), 0, 255).astype(np.uint8)
|
|
|
|
|
| def histogram_equalization(image: np.ndarray):
|
| if len(image.shape) == 2:
|
| return cv2.equalizeHist(image)
|
| else:
|
| b, g, r = cv2.split(image)
|
| b_eq = cv2.equalizeHist(b)
|
| g_eq = cv2.equalizeHist(g)
|
| r_eq = cv2.equalizeHist(r)
|
| return cv2.merge([b_eq, g_eq, r_eq])
|
|
|
|
|
| def contrast_stretch(image: np.ndarray, in_low: int, in_high: int):
|
| img_float = image.astype(np.float32)
|
| img_stretched = (img_float - in_low) * (255.0 / max(in_high - in_low, 1e-6))
|
| return np.clip(img_stretched, 0, 255).astype(np.uint8)
|
|
|
|
|
| def gamma_correction(image: np.ndarray, gamma: float = 1.0):
|
| inv_gamma = 1.0 / gamma
|
| table = np.array([(i / 255.0) ** inv_gamma * 255 for i in range(256)]).astype("uint8")
|
| return cv2.LUT(image, table)
|
|
|
|
|
| def rle_encode(image: np.ndarray):
|
| if len(image.shape) > 2:
|
| image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
| flat = image.flatten()
|
| encoding = []
|
| prev = flat[0]
|
| count = 1
|
| for pixel in flat[1:]:
|
| if pixel == prev:
|
| count += 1
|
| else:
|
| encoding.append((int(prev), count))
|
| prev = pixel
|
| count = 1
|
| encoding.append((int(prev), count))
|
| return encoding
|
|
|
| def rle_decode(encoding, shape):
|
| flat_image = []
|
| for pixel_value, count in encoding:
|
| flat_image.extend([pixel_value] * count)
|
| return np.array(flat_image, dtype=np.uint8).reshape(shape)
|
|
|
|
|
| def apply_dct(image, threshold_ratio=0.01):
|
| dct_transformed = dct(dct(image.T, norm='ortho').T, norm='ortho')
|
| dct_thresh = dct_transformed * (np.abs(dct_transformed) > threshold_ratio * np.max(dct_transformed))
|
| img_reconstructed = idct(idct(dct_thresh.T, norm='ortho').T, norm='ortho')
|
|
|
| return np.clip(img_reconstructed, 0, 255).astype(np.uint8)
|
|
|
|
|
| def global_threshold(image: np.ndarray, thresh: int = 128):
|
| if len(image.shape) == 3:
|
| image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
| _, binary = cv2.threshold(image, thresh, 255, cv2.THRESH_BINARY)
|
| return binary
|
|
|
|
|
| def adaptive_threshold(image: np.ndarray, block_size: int = 11, C: int = 2):
|
| if len(image.shape) == 3:
|
| image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
| return cv2.adaptiveThreshold(image, 255, cv2.ADAPTIVE_THRESH_MEAN_C,
|
| cv2.THRESH_BINARY, block_size, C)
|
|
|
| def kmeans_segmentation(image: np.ndarray, k: int = 2):
|
| if len(image.shape) == 2:
|
| data = image.reshape((-1, 1))
|
| else:
|
| data = image.reshape((-1, 3))
|
| data = np.float32(data)
|
| criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
|
| _, labels, centers = cv2.kmeans(data, k, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
|
| centers = np.uint8(centers)
|
| segmented = centers[labels.flatten()]
|
| if len(image.shape) == 2:
|
| segmented = segmented.reshape((image.shape[0], image.shape[1]))
|
| else:
|
| segmented = segmented.reshape((image.shape[0], image.shape[1], 3))
|
| return segmented
|
|
|
|
|
| def binarize_image(image: np.ndarray, thresh: int = 127):
|
| if len(image.shape) == 3:
|
| image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
| _, binary = cv2.threshold(image, thresh, 255, cv2.THRESH_BINARY)
|
| return binary
|
|
|
|
|
| def morphological_op(binary_image: np.ndarray, op_type: str, ksize: int, iterations: int):
|
| kernel = np.ones((ksize, ksize), np.uint8)
|
| if op_type == 'erosion':
|
| return cv2.erode(binary_image, kernel, iterations=iterations)
|
| elif op_type == 'dilation':
|
| return cv2.dilate(binary_image, kernel, iterations=iterations)
|
| elif op_type == 'opening':
|
| return cv2.morphologyEx(binary_image, cv2.MORPH_OPEN, kernel, iterations=iterations)
|
| elif op_type == 'closing':
|
| return cv2.morphologyEx(binary_image, cv2.MORPH_CLOSE, kernel, iterations=iterations)
|
| else:
|
| return binary_image
|
|
|
|
|
| def extract_boundary(binary_image: np.ndarray, ksize: int = 3):
|
| kernel = np.ones((ksize, ksize), np.uint8)
|
| eroded = cv2.erode(binary_image, kernel, iterations=1)
|
| return cv2.subtract(binary_image, eroded)
|
|
|
|
|
| def skeletonize_image(binary_image: np.ndarray):
|
| from skimage.morphology import skeletonize
|
| if len(binary_image.shape) == 3:
|
| binary_image = cv2.cvtColor(binary_image, cv2.COLOR_BGR2GRAY)
|
| bin_bool = (binary_image > 0)
|
| skeleton = skeletonize(bin_bool)
|
| return (skeleton * 255).astype(np.uint8)
|
|
|
|
|
| def add_noise(image: np.ndarray, mean=0, std=25):
|
| gauss = np.random.normal(mean, std, image.shape)
|
| noisy = image.astype(np.float32) + gauss
|
| return np.clip(noisy, 0, 255).astype(np.uint8)
|
|
|
|
|
| def wiener_filter(image: np.ndarray, ksize=5, noise_var=0.01):
|
| if len(image.shape) == 3:
|
| image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
| kernel = np.ones((ksize, ksize), np.float32) / (ksize * ksize)
|
| local_mean = cv2.filter2D(image, -1, kernel)
|
| local_var = cv2.filter2D(image ** 2, -1, kernel) - (local_mean ** 2)
|
| noise_var = max(noise_var, 0.0001)
|
| result = local_mean + ((local_var - noise_var) / np.maximum(local_var, noise_var)) * (image - local_mean)
|
| return np.clip(result, 0, 255).astype(np.uint8)
|
|
|
|
|
| def gaussian_filter_restoration(image: np.ndarray, ksize=5, sigma=1.0):
|
| if len(image.shape) == 3:
|
| image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
| return cv2.GaussianBlur(image, (ksize, ksize), sigma)
|
|
|
|
|
| def inpaint_image(image: np.ndarray, top_left, bottom_right, inpaint_radius=3):
|
| mask = np.zeros(image.shape[:2], dtype=np.uint8)
|
| r1, c1 = top_left
|
| r2, c2 = bottom_right
|
| mask[r1:r2, c1:c2] = 255
|
| return cv2.inpaint(image, mask, inpaint_radius, cv2.INPAINT_TELEA)
|
|
|
|
|
| def detect_and_compute(image: np.ndarray, method='ORB'):
|
| if len(image.shape) == 3:
|
| gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
| else:
|
| gray = image
|
| if method == 'ORB':
|
| detector = cv2.ORB_create()
|
| else:
|
| try:
|
| detector = cv2.SIFT_create()
|
| except:
|
| detector = cv2.ORB_create()
|
| kp, des = detector.detectAndCompute(gray, None)
|
| return kp, des
|
|
|
|
|
| def match_features(img1: np.ndarray, img2: np.ndarray, method='ORB', match_method='BF'):
|
| kp1, des1 = detect_and_compute(img1, method)
|
| kp2, des2 = detect_and_compute(img2, method)
|
| if des1 is None or des2 is None or len(des1) < 2 or len(des2) < 2:
|
| return img1
|
| if match_method == 'BF':
|
| norm_type = cv2.NORM_HAMMING if method=='ORB' else cv2.NORM_L2
|
| bf = cv2.BFMatcher(norm_type, crossCheck=True)
|
| matches = bf.match(des1, des2)
|
| else:
|
| if method == 'ORB':
|
| index_params = dict(algorithm=6, table_number=6, key_size=12, multi_probe_level=1)
|
| search_params = dict(checks=50)
|
| flann = cv2.FlannBasedMatcher(index_params, search_params)
|
| matches = flann.match(des1, des2)
|
| else:
|
| FLANN_INDEX_KDTREE = 1
|
| index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
|
| search_params = dict(checks=50)
|
| flann = cv2.FlannBasedMatcher(index_params, search_params)
|
| matches = flann.match(des1, des2)
|
| matches = sorted(matches, key=lambda x: x.distance)
|
| matched_img = cv2.drawMatches(img1, kp1, img2, kp2, matches[:20], None,
|
| flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
|
| return matched_img
|
|
|
|
|
| def template_match(main_img: np.ndarray, templ: np.ndarray, threshold=0.8):
|
| if len(main_img.shape) == 3:
|
| main_gray = cv2.cvtColor(main_img, cv2.COLOR_BGR2GRAY)
|
| else:
|
| main_gray = main_img
|
| if len(templ.shape) == 3:
|
| templ_gray = cv2.cvtColor(templ, cv2.COLOR_BGR2GRAY)
|
| else:
|
| templ_gray = templ
|
| result = cv2.matchTemplate(main_gray, templ_gray, cv2.TM_CCOEFF_NORMED)
|
| loc = np.where(result >= threshold)
|
| display = main_img.copy()
|
| h, w = templ_gray.shape
|
| for pt in zip(*loc[::-1]):
|
| cv2.rectangle(display, pt, (pt[0] + w, pt[1] + h), (0, 0, 255), 2)
|
| return display
|
|
|
|
|
| def tab_basic_ops(img1, img2, operation, r_factor, g_factor, b_factor, flip_dir,
|
| trans_x, trans_y, scale_w, scale_h, scale_keep_aspect, rot_angle, rot_clockwise,
|
| crop_x, crop_y, crop_w, crop_h, blend_alpha, bright_val, contrast_val, filter_lower_H,
|
| filter_lower_S, filter_lower_V, filter_upper_H, filter_upper_S, filter_upper_V,
|
| border_top, border_bottom, border_left, border_right, border_color_str, overlay_x,
|
| overlay_y, overlay_alpha):
|
| if img1 is None:
|
| return None
|
|
|
| try:
|
| color_tuple = tuple(map(int, border_color_str.split(',')))
|
| if len(color_tuple) != 3:
|
| color_tuple = (0, 0, 0)
|
| except:
|
| color_tuple = (0, 0, 0)
|
|
|
| if operation == "Greyscale":
|
| return to_greyscale(img1)
|
| elif operation == "Negative":
|
| return to_negative(img1)
|
| elif operation == "Adjust Color":
|
| return adjust_color(img1, r_factor, g_factor, b_factor)
|
| elif operation == "Flip":
|
| return flip_image(img1, flip_dir)
|
| elif operation == "Translate":
|
| return translate(img1, trans_x, trans_y)
|
| elif operation == "Scale":
|
| return scale_image(img1, width=scale_w, height=scale_h, keep_aspect_ratio=scale_keep_aspect)
|
| elif operation == "Rotate":
|
| return rotate(img1, rot_angle, clockwise=rot_clockwise)
|
| elif operation == "Crop":
|
| return crop(img1, crop_x, crop_y, crop_w, crop_h)
|
| elif operation == "Blend":
|
| return blend(img1, img2, alpha=blend_alpha)
|
| elif operation == "Brightness & Contrast":
|
| return adjust_brightness_contrast(img1, bright_val, contrast_val)
|
| elif operation == "Color Filter":
|
| lowerb = (filter_lower_H, filter_lower_S, filter_lower_V)
|
| upperb = (filter_upper_H, filter_upper_S, filter_upper_V)
|
|
|
| return color_filter(img1, lowerb, upperb)
|
| elif operation == "Sepia":
|
| return apply_sepia(img1)
|
| elif operation == "Cyanotype":
|
| return apply_cyanotype(img1)
|
| elif operation == "Add Border":
|
| return add_border(img1, border_top, border_bottom, border_left, border_right, color=color_tuple)
|
| elif operation == "Overlay":
|
| return overlay(img1, img2, overlay_x, overlay_y, alpha=overlay_alpha)
|
| else:
|
| return img1
|
|
|
|
|
| def tab_pixelwise_op(image1, image2, operation):
|
| if image1 is None:
|
| return None
|
| return pixelwise_operation(image1, image2, operation)
|
|
|
|
|
| def tab_transform_filter(image, operation, ksize=3, sigma=1.0, t1=50, t2=150):
|
| if image is None:
|
| return None
|
| if operation == "FFT":
|
| return fft_image(image)
|
| elif operation == "Mean Blur":
|
| return mean_blur(image, ksize)
|
| elif operation == "Gaussian Blur":
|
| return gaussian_blur(image, ksize, sigma)
|
| elif operation == "Median Blur":
|
| return median_blur(image, ksize)
|
| elif operation == "Sobel Edge":
|
| return sobel_edge(image, ksize)
|
| elif operation == "Canny Edge":
|
| return canny_edge(image, t1, t2)
|
| elif operation == "Laplacian Edge":
|
| return laplacian_edge(image, ksize)
|
| else:
|
| return image
|
|
|
|
|
| def tab_enhancement(image, operation, in_low=0, in_high=255, gamma_val=1.0):
|
| if image is None:
|
| return None
|
| if operation == "Histogram Equalization":
|
| return histogram_equalization(image)
|
| elif operation == "Contrast Stretching":
|
| return contrast_stretch(image, in_low, in_high)
|
| elif operation == "Gamma Correction":
|
| return gamma_correction(image, gamma_val)
|
| else:
|
| return image
|
|
|
|
|
| def tab_compression(image, operation, threshold_ratio=0.005):
|
| if image is None:
|
| return None
|
| if operation == "RLE":
|
| encoded = rle_encode(image)
|
| decoded_image = rle_decode(encoded, image.shape[:2])
|
| return decoded_image
|
| elif operation == "DCT":
|
| compressed_image = apply_dct(image, threshold_ratio=threshold_ratio)
|
| return compressed_image
|
| else:
|
| return image
|
|
|
|
|
| def tab_segmentation(image, operation, threshold=128, block_size=11, C=2, k=3):
|
| if image is None:
|
| return None
|
| if operation == "Global Thresholding":
|
| return global_threshold(image, threshold)
|
| elif operation == "Adaptive Thresholding":
|
| return adaptive_threshold(image, block_size, C)
|
| elif operation == "K-Means":
|
| return kmeans_segmentation(image, k)
|
| else:
|
| return image
|
|
|
|
|
| def tab_binary(image, operation, thresh=127, morph_op_='erosion', ksize=3, iterations=1):
|
| if image is None:
|
| return None
|
| if operation == "Binarize":
|
| return binarize_image(image, thresh)
|
| elif operation == "Morphological":
|
| bin_img = binarize_image(image, thresh=127) if len(image.shape) == 3 else image
|
| return morphological_op(bin_img, morph_op_, ksize, iterations)
|
| elif operation == "Extract Boundary":
|
| bin_img = binarize_image(image, thresh=127) if len(image.shape) == 3 else image
|
| return extract_boundary(bin_img, ksize=ksize)
|
| elif operation == "Skeletonize":
|
| bin_img = binarize_image(image, thresh=127) if len(image.shape) == 3 else image
|
| return skeletonize_image(bin_img)
|
| else:
|
| return image
|
|
|
|
|
| def tab_restoration(image, operation, ksize=5, sigma=1.0, noise_var=0.01, top_left_r=0, top_left_c=0, bot_right_r=10, bot_right_c=10, inpaint_radius=3):
|
| if image is None:
|
| return None
|
| if operation == "Add Noise + Wiener Filter":
|
| noisy = add_noise(image, 0, 25)
|
| return wiener_filter(noisy, ksize, noise_var)
|
| elif operation == "Add Noise + Gaussian Filter":
|
| noisy = add_noise(image, 0, 25)
|
| return gaussian_filter_restoration(noisy, ksize, sigma)
|
| elif operation == "Inpainting":
|
| return inpaint_image(image, (top_left_r, top_left_c), (bot_right_r, bot_right_c), inpaint_radius)
|
| else:
|
| return image
|
|
|
|
|
| def tab_matching(image1, image2, operation, feature_method='ORB', match_method='BF', templ_thresh=0.8):
|
| if image1 is None or image2 is None:
|
| return None
|
| if operation == "Feature Detection & Matching":
|
| return match_features(image1, image2, method=feature_method, match_method=match_method)
|
| elif operation == "Template Matching":
|
| return template_match(image1, image2, threshold=templ_thresh)
|
| else:
|
| return None
|
|
|
| def build_demo():
|
| with gr.Blocks() as demo:
|
| gr.Markdown("## Image Processing And Recognition")
|
| with gr.Tabs():
|
|
|
| with gr.TabItem("Basic Image Operations"):
|
| gr.Markdown("### Basic Image Operations")
|
|
|
|
|
| with gr.TabItem("Greyscale"):
|
| with gr.Row():
|
| input_image_basic1 = gr.Image(label="Input Image 1")
|
| output_image_basic = gr.Image(label="Output Image")
|
| run_basic = gr.Button("Apply")
|
| run_basic.click(
|
| fn=to_greyscale,
|
| inputs=[input_image_basic1],
|
| outputs=[output_image_basic]
|
| )
|
|
|
|
|
| with gr.TabItem("Negative"):
|
| with gr.Row():
|
| input_image_basic1 = gr.Image(label="Input Image 1")
|
| output_image_basic = gr.Image(label="Output Image")
|
| run_basic = gr.Button("Apply")
|
| run_basic.click(
|
| fn=to_negative,
|
| inputs=[input_image_basic1],
|
| outputs=[output_image_basic]
|
| )
|
|
|
|
|
| with gr.TabItem("Adjust Color"):
|
| with gr.Row():
|
| input_image_basic1 = gr.Image(label="Input Image 1")
|
| output_image_basic = gr.Image(label="Output Image")
|
|
|
| r_factor = gr.Slider(0.0, 2.0, value=1.0, step=0.1, label="Red Factor")
|
| g_factor = gr.Slider(0.0, 2.0, value=1.0, step=0.1, label="Green Factor")
|
| b_factor = gr.Slider(0.0, 2.0, value=1.0, step=0.1, label="Blue Factor")
|
|
|
| run_basic = gr.Button("Apply")
|
| run_basic.click(
|
| fn=adjust_color,
|
| inputs=[input_image_basic1, r_factor, g_factor, b_factor],
|
| outputs=[output_image_basic]
|
| )
|
|
|
|
|
| with gr.TabItem("Flip"):
|
| with gr.Row():
|
| input_image_basic1 = gr.Image(label="Input Image 1")
|
| output_image_basic = gr.Image(label="Output Image")
|
|
|
| flip_dir = gr.Dropdown(["horizontal", "vertical", "diagonal"], value="horizontal", label="Flip Direction")
|
|
|
| run_basic = gr.Button("Apply")
|
| run_basic.click(
|
| fn=flip_image,
|
| inputs=[input_image_basic1, flip_dir],
|
| outputs=[output_image_basic]
|
| )
|
|
|
|
|
| with gr.TabItem("Translate"):
|
| with gr.Row():
|
| input_image_basic1 = gr.Image(label="Input Image 1")
|
| output_image_basic = gr.Image(label="Output Image")
|
|
|
| trans_x = gr.Slider(-200, 200, value=0, step=1, label="Translate X")
|
| trans_y = gr.Slider(-200, 200, value=0, step=1, label="Translate Y")
|
|
|
| run_basic = gr.Button("Apply")
|
| run_basic.click(
|
| fn=translate,
|
| inputs=[input_image_basic1, trans_x, trans_y],
|
| outputs=[output_image_basic]
|
| )
|
|
|
|
|
| with gr.TabItem("Scale"):
|
| with gr.Row():
|
| input_image_basic1 = gr.Image(label="Input Image 1")
|
| output_image_basic = gr.Image(label="Output Image")
|
|
|
| scale_w = gr.Number(value=None, label="Scale Width (None=auto)")
|
| scale_h = gr.Number(value=None, label="Scale Height (None=auto)")
|
| scale_keep_aspect = gr.Checkbox(value=True, label="Keep Aspect Ratio")
|
|
|
| run_basic = gr.Button("Apply")
|
| run_basic.click(
|
| fn=scale_image,
|
| inputs=[input_image_basic1, scale_w, scale_h, scale_keep_aspect],
|
| outputs=[output_image_basic]
|
| )
|
|
|
|
|
| with gr.TabItem("Rotate"):
|
| with gr.Row():
|
| input_image_basic1 = gr.Image(label="Input Image 1")
|
| output_image_basic = gr.Image(label="Output Image")
|
|
|
| rot_angle = gr.Slider(-180, 180, value=0, step=1, label="Rotate Angle")
|
| rot_clockwise = gr.Checkbox(value=True, label="Rotate Clockwise")
|
|
|
| run_basic = gr.Button("Apply")
|
| run_basic.click(
|
| fn=rotate,
|
| inputs=[input_image_basic1, rot_angle, rot_clockwise],
|
| outputs=[output_image_basic]
|
| )
|
|
|
|
|
| with gr.TabItem("Crop"):
|
| with gr.Row():
|
| input_image_basic1 = gr.Image(label="Input Image 1")
|
| output_image_basic = gr.Image(label="Output Image")
|
|
|
| crop_x = gr.Slider(0, 300, value=0, step=1, label="Crop X")
|
| crop_y = gr.Slider(0, 300, value=0, step=1, label="Crop Y")
|
| crop_w = gr.Slider(0, 300, value=100, step=1, label="Crop Width")
|
| crop_h = gr.Slider(0, 300, value=100, step=1, label="Crop Height")
|
|
|
| run_basic = gr.Button("Apply")
|
| run_basic.click(
|
| fn=crop,
|
| inputs=[input_image_basic1, crop_x, crop_y, crop_w, crop_h],
|
| outputs=[output_image_basic]
|
| )
|
|
|
|
|
| with gr.TabItem("Blend"):
|
| with gr.Row():
|
| input_image_basic1 = gr.Image(label="Input Image 1")
|
| input_image_basic2 = gr.Image(label="Input Image 2 (for blend)")
|
| output_image_basic = gr.Image(label="Output Image")
|
|
|
| blend_alpha = gr.Slider(0.0, 1.0, value=0.5, step=0.05, label="Blend Alpha")
|
|
|
| run_basic = gr.Button("Apply")
|
| run_basic.click(
|
| fn=blend,
|
| inputs=[input_image_basic1, input_image_basic2, blend_alpha],
|
| outputs=[output_image_basic]
|
| )
|
|
|
|
|
| with gr.TabItem("Brightness & Contrast"):
|
| with gr.Row():
|
| input_image_basic1 = gr.Image(label="Input Image 1")
|
| output_image_basic = gr.Image(label="Output Image")
|
|
|
| bright_val = gr.Slider(-100, 100, value=50, step=1, label="Brightness")
|
| contrast_val = gr.Slider(0.1, 3.0, value=1.2, step=0.1, label="Contrast")
|
|
|
| run_basic = gr.Button("Apply")
|
| run_basic.click(
|
| fn=adjust_brightness_contrast,
|
| inputs=[input_image_basic1, bright_val, contrast_val],
|
| outputs=[output_image_basic]
|
| )
|
|
|
|
|
| with gr.TabItem("Color Filter"):
|
| with gr.Row():
|
| input_image_basic1 = gr.Image(label="Input Image 1")
|
| output_image_basic = gr.Image(label="Output Image")
|
|
|
| filter_type = gr.Radio(
|
| ["Custom", "Sepia", "Cyanotype"],
|
| label="Select Filter Type",
|
| value="Custom",
|
| )
|
|
|
| filter_lower_H = gr.Slider(0, 179, value=0, step=1, label="Filter Lower H")
|
| filter_lower_S = gr.Slider(0, 255, value=0, step=1, label="Filter Lower S")
|
| filter_lower_V = gr.Slider(0, 255, value=0, step=1, label="Filter Lower V")
|
|
|
| filter_upper_H = gr.Slider(0, 179, value=179, step=1, label="Filter Upper H")
|
| filter_upper_S = gr.Slider(0, 255, value=255, step=1, label="Filter Upper S")
|
| filter_upper_V = gr.Slider(0, 255, value=255, step=1, label="Filter Upper V")
|
|
|
| run_basic = gr.Button("Apply")
|
|
|
| def apply_selected_filter(image, filter_type, lower_H, lower_S, lower_V, upper_H, upper_S, upper_V):
|
| if filter_type == "Custom":
|
| lowerb = (lower_H, lower_S, lower_V)
|
| upperb = (upper_H, upper_S, upper_V)
|
| return color_filter(image, lowerb, upperb)
|
| elif filter_type == "Sepia":
|
| return apply_sepia(image)
|
| elif filter_type == "Cyanotype":
|
| return apply_cyanotype(image)
|
|
|
| run_basic.click(
|
| fn=apply_selected_filter,
|
| inputs=[input_image_basic1, filter_type, filter_lower_H, filter_lower_S, filter_lower_V, filter_upper_H, filter_upper_S, filter_upper_V],
|
| outputs=[output_image_basic]
|
| )
|
|
|
|
|
| with gr.TabItem("Add Border"):
|
| with gr.Row():
|
| input_image_basic1 = gr.Image(label="Input Image 1")
|
| output_image_basic = gr.Image(label="Output Image")
|
|
|
| border_top = gr.Slider(0, 100, value=0, step=1, label="Border Top")
|
| border_bottom = gr.Slider(0, 100, value=0, step=1, label="Border Bottom")
|
| border_left = gr.Slider(0, 100, value=0, step=1, label="Border Left")
|
| border_right = gr.Slider(0, 100, value=0, step=1, label="Border Right")
|
| border_color_str = gr.Textbox(value="0,0,0", label="Border Color (R,G,B)")
|
|
|
| run_basic = gr.Button("Apply")
|
| run_basic.click(
|
| fn=add_border,
|
| inputs=[input_image_basic1, border_top, border_bottom, border_left, border_right, border_color_str],
|
| outputs=[output_image_basic]
|
| )
|
|
|
|
|
| with gr.TabItem("Overlay"):
|
| with gr.Row():
|
| input_image_basic1 = gr.Image(label="Input Image 1")
|
| input_image_basic2 = gr.Image(label="Input Image 2 (for overlay)")
|
| output_image_basic = gr.Image(label="Output Image")
|
|
|
| overlay_x = gr.Slider(0, 300, value=0, step=1, label="Overlay X")
|
| overlay_y = gr.Slider(0, 300, value=0, step=1, label="Overlay Y")
|
| overlay_alpha = gr.Slider(0.0, 1.0, value=0.5, step=0.05, label="Overlay Alpha")
|
|
|
| run_basic = gr.Button("Apply")
|
| run_basic.click(
|
| fn=overlay,
|
| inputs=[input_image_basic1, input_image_basic2, overlay_x, overlay_y, overlay_alpha],
|
| outputs=[output_image_basic]
|
| )
|
|
|
|
|
| with gr.TabItem("Mathematical Operations"):
|
| gr.Markdown("### Mathematical Operations on Images")
|
| with gr.Row():
|
| input_image_math1 = gr.Image(label="Input Image 1")
|
| input_image_math2 = gr.Image(label="Input Image 2 (ignored if NOT)")
|
| output_image_math = gr.Image(label="Output Image")
|
|
|
| operation_math = gr.Radio(
|
| choices=["Add", "Subtract", "Multiply", "Divide",
|
| "Bitwise (AND)", "Bitwise (OR)", "Bitwise (XOR)", "Bitwise (NOT)"],
|
| value="add",
|
| label="Pixelwise Operation"
|
| )
|
| run_math = gr.Button("Apply Operation")
|
| run_math.click(
|
| tab_pixelwise_op,
|
| inputs=[input_image_math1, input_image_math2, operation_math],
|
| outputs=[output_image_math]
|
| )
|
|
|
|
|
| with gr.TabItem("Transforms & Filtering"):
|
| gr.Markdown("### Transforms & Filtering")
|
|
|
| with gr.Row():
|
| input_image_tf = gr.Image(type="numpy", label="Input Image")
|
| output_image_tf = gr.Image(type="numpy", label="Output Image")
|
|
|
|
|
| operation_tf = gr.Radio(
|
| choices=["FFT", "Mean Blur", "Gaussian Blur", "Median Blur",
|
| "Sobel Edge", "Canny Edge", "Laplacian Edge"],
|
| value="FFT",
|
| label="Select Operation"
|
| )
|
|
|
|
|
| ksize_tf = gr.Slider(1, 15, step=1, value=3, label="Kernel Size (blur/edge)")
|
| sigma_tf = gr.Slider(0.1, 5.0, step=0.1, value=1.0, label="Sigma (Gaussian)")
|
| t1_tf = gr.Slider(0, 255, value=50, step=1, label="Canny Threshold1")
|
| t2_tf = gr.Slider(0, 255, value=150, step=1, label="Canny Threshold2")
|
|
|
| run_tf = gr.Button("Process")
|
| def _process_tf(img, op, k, s, c1, c2):
|
| return tab_transform_filter(img, op, k, s, c1, c2)
|
|
|
|
|
| run_tf.click(_process_tf,
|
| inputs=[input_image_tf, operation_tf, ksize_tf, sigma_tf, t1_tf, t2_tf],
|
| outputs=[output_image_tf])
|
|
|
|
|
| with gr.TabItem("Enhancement"):
|
| gr.Markdown("### Image Enhancement")
|
| with gr.Row():
|
| input_image_en = gr.Image(label="Input Image")
|
| output_image_en = gr.Image(label="Output Image")
|
|
|
| operation_en = gr.Radio(
|
| choices=["Histogram Equalization", "Contrast Stretching", "Gamma Correction"],
|
| value="Histogram Equalization",
|
| label="Select Enhancement"
|
| )
|
| in_low_en = gr.Slider(0, 255, value=0, step=1, label="In Low (Contrast Stretch)")
|
| in_high_en = gr.Slider(0, 255, value=255, step=1, label="In High (Contrast Stretch)")
|
| gamma_en = gr.Slider(0.1, 5.0, value=1.0, step=0.1, label="Gamma")
|
|
|
| run_en = gr.Button("Enhance")
|
| def _process_en(img, op, low, high, gm):
|
| return tab_enhancement(img, op, low, high, gm)
|
| run_en.click(_process_en,
|
| inputs=[input_image_en, operation_en, in_low_en, in_high_en, gamma_en],
|
| outputs=[output_image_en])
|
|
|
|
|
| with gr.TabItem("Compression"):
|
| gr.Markdown("### Image Compression")
|
| with gr.Row():
|
| input_image_comp = gr.Image(label="Input Image")
|
| output_comp_image = gr.Image(label="Output Image", interactive=False)
|
|
|
| operation_comp = gr.Radio(
|
| choices=["RLE", "DCT"],
|
| value="RLE",
|
| label="Select Compression"
|
| )
|
|
|
| run_comp = gr.Button("Compress")
|
|
|
| def _process_comp(img, op):
|
| result = tab_compression(img, op)
|
| return result
|
|
|
| run_comp.click(
|
| _process_comp,
|
| inputs=[input_image_comp, operation_comp],
|
| outputs=[output_comp_image]
|
| )
|
|
|
|
|
| with gr.TabItem("Segmentation"):
|
| gr.Markdown("### Image Segmentation")
|
| with gr.Row():
|
| input_image_seg = gr.Image(label="Input Image")
|
| output_image_seg = gr.Image(label="Output Image")
|
|
|
| operation_seg = gr.Radio(
|
| choices=["Global Thresholding", "Adaptive Thresholding", "K-Means"],
|
| value="Global Thresholding",
|
| label="Select Segmentation"
|
| )
|
| thresh_seg = gr.Slider(0, 255, value=128, step=1, label="Threshold (Global)")
|
| block_seg = gr.Slider(3, 31, step=2, value=11, label="Block Size (Adaptive)")
|
| c_seg = gr.Slider(0, 10, value=2, step=1, label="C (Adaptive)")
|
| k_seg = gr.Slider(2, 10, value=3, step=1, label="K (K-Means)")
|
|
|
| run_seg = gr.Button("Segment")
|
| def _process_seg(img, op, th, bs, c_, k_):
|
| return tab_segmentation(img, op, th, bs, c_, k_)
|
| run_seg.click(_process_seg,
|
| inputs=[input_image_seg, operation_seg, thresh_seg, block_seg, c_seg, k_seg],
|
| outputs=[output_image_seg])
|
|
|
|
|
| with gr.TabItem("Binary Processing"):
|
| gr.Markdown("### Binary Image Processing")
|
| with gr.Row():
|
| input_image_bin = gr.Image(label="Input Image")
|
| output_image_bin = gr.Image(label="Output Image")
|
|
|
| operation_bin = gr.Radio(
|
| choices=["Binarize", "Morphological", "Extract Boundary", "Skeletonize"],
|
| value="Binarize",
|
| label="Select Operation"
|
| )
|
| thresh_bin = gr.Slider(0, 255, value=127, step=1, label="Threshold")
|
| morph_op_bin = gr.Dropdown(
|
| choices=["erosion", "dilation", "opening", "closing"],
|
| value="erosion",
|
| label="Morphological Operation"
|
| )
|
| ksize_bin = gr.Slider(1, 15, value=3, step=1, label="Kernel Size")
|
| iter_bin = gr.Slider(1, 10, value=1, step=1, label="Iterations")
|
|
|
| run_bin = gr.Button("Process Binary")
|
| def _process_bin(img, op, th, mop, ks, iters):
|
| return tab_binary(img, op, th, mop, ks, iters)
|
| run_bin.click(_process_bin,
|
| inputs=[input_image_bin, operation_bin, thresh_bin, morph_op_bin, ksize_bin, iter_bin],
|
| outputs=[output_image_bin])
|
|
|
|
|
| with gr.TabItem("Restoration"):
|
| gr.Markdown("### Image Restoration")
|
| with gr.Row():
|
| input_image_rest = gr.Image(label="Input Image")
|
| output_image_rest = gr.Image(label="Output Image")
|
|
|
| operation_rest = gr.Radio(
|
| choices=["Add Noise + Wiener Filter", "Add Noise + Gaussian Filter", "Inpainting"],
|
| value="Add Noise + Wiener Filter",
|
| label="Select Restoration"
|
| )
|
| ksize_rest = gr.Slider(1, 15, value=5, step=1, label="Kernel Size (Wiener/Gaussian)")
|
| sigma_rest = gr.Slider(0.1, 5.0, value=1.0, step=0.1, label="Sigma (Gaussian)")
|
| noise_var_rest = gr.Slider(0.001, 1.0, value=0.01, step=0.01, label="Noise Var (Wiener)")
|
| top_left_r = gr.Number(value=0, label="Inpaint Top Row")
|
| top_left_c = gr.Number(value=0, label="Inpaint Left Col")
|
| bot_right_r = gr.Number(value=50, label="Inpaint Bottom Row")
|
| bot_right_c = gr.Number(value=50, label="Inpaint Right Col")
|
| inpaint_rad = gr.Slider(1, 10, value=3, step=1, label="Inpaint Radius")
|
|
|
| run_rest = gr.Button("Restore")
|
| def _process_rest(img, op, ks, sg, nv, r1, c1, r2, c2, ipr):
|
| return tab_restoration(img, op, ks, sg, nv, int(r1), int(c1), int(r2), int(c2), ipr)
|
| run_rest.click(_process_rest,
|
| inputs=[input_image_rest, operation_rest, ksize_rest, sigma_rest, noise_var_rest,
|
| top_left_r, top_left_c, bot_right_r, bot_right_c, inpaint_rad],
|
| outputs=[output_image_rest])
|
|
|
|
|
| with gr.TabItem("Matching"):
|
| gr.Markdown("### Image Matching")
|
| with gr.Row():
|
| input_image_match1 = gr.Image(label="Main Image / Image1")
|
| input_image_match2 = gr.Image(label="Template / Image2")
|
| output_image_match = gr.Image(label="Output Image")
|
|
|
| operation_match = gr.Radio(
|
| choices=["Feature Detection & Matching", "Template Matching"],
|
| value="Feature Detection & Matching",
|
| label="Select Matching Operation"
|
| )
|
| feature_method = gr.Dropdown(choices=["ORB", "SIFT"], value="ORB", label="Feature Method")
|
| match_method = gr.Dropdown(choices=["BF", "FLANN"], value="BF", label="Match Method")
|
| templ_thresh = gr.Slider(0.0, 1.0, value=0.8, step=0.01, label="Template Threshold")
|
|
|
| run_match = gr.Button("Match")
|
| def _process_match(img1, img2, op, fm, mm, thr):
|
| return tab_matching(img1, img2, op, fm, mm, thr)
|
| run_match.click(_process_match,
|
| inputs=[input_image_match1, input_image_match2, operation_match,
|
| feature_method, match_method, templ_thresh],
|
| outputs=[output_image_match])
|
| return demo
|
|
|
|
|
| if __name__ == "__main__":
|
| demo = build_demo()
|
| demo.launch(share=True)
|
| |