Spaces:
Runtime error
Runtime error
| import cv2 as cv | |
| import numpy as np | |
| from PIL import Image | |
| DCT_SIZE = 8 | |
| TABLE_SIZE = DCT_SIZE ** 2 | |
| ZIG_ZAG = [ | |
| [0, 0], | |
| [0, 1], | |
| [1, 0], | |
| [2, 0], | |
| [1, 1], | |
| [0, 2], | |
| [0, 3], | |
| [1, 2], | |
| [2, 1], | |
| [3, 0], | |
| [4, 0], | |
| [3, 1], | |
| [2, 2], | |
| [1, 3], | |
| [0, 4], | |
| [0, 5], | |
| [1, 4], | |
| [2, 3], | |
| [3, 2], | |
| [4, 1], | |
| [5, 0], | |
| [6, 0], | |
| [5, 1], | |
| [4, 2], | |
| [3, 3], | |
| [2, 4], | |
| [1, 5], | |
| [0, 6], | |
| [0, 7], | |
| [1, 6], | |
| [2, 5], | |
| [3, 4], | |
| [4, 4], | |
| [5, 3], | |
| [6, 2], | |
| [7, 1], | |
| [7, 2], | |
| [6, 3], | |
| [5, 4], | |
| [4, 5], | |
| [3, 5], | |
| [2, 6], | |
| [1, 7], | |
| [2, 7], | |
| [3, 6], | |
| [4, 5], | |
| [5, 4], | |
| [6, 3], | |
| [7, 2], | |
| [7, 3], | |
| [6, 4], | |
| [5, 5], | |
| [4, 6], | |
| [3, 7], | |
| [4, 7], | |
| [5, 6], | |
| [6, 5], | |
| [7, 4], | |
| [7, 5], | |
| [6, 6], | |
| [5, 7], | |
| [6, 7], | |
| [7, 6], | |
| [7, 7], | |
| ] | |
| def compress_jpg(image: Image.Image, quality, color=True): | |
| """Compress a PIL image to JPEG format with specified quality. | |
| Args: | |
| image: Input PIL image (RGB format) | |
| quality: JPEG compression quality (1-100) | |
| color: Whether to preserve color (BGR format) | |
| Returns: | |
| np.ndarray: Decompressed image in BGR or grayscale format | |
| """ | |
| # Convert PIL image to OpenCV BGR format | |
| img_np = np.array(image) | |
| if color: | |
| img_np = cv.cvtColor(img_np, cv.COLOR_RGB2BGR) | |
| _, buffer = cv.imencode(".jpg", img_np, [cv.IMWRITE_JPEG_QUALITY, quality]) | |
| return cv.imdecode(buffer, cv.IMREAD_COLOR if color else cv.IMREAD_GRAYSCALE) | |
| def loss_curve(image: Image.Image, qualities=tuple(range(1, 101)), normalize=True): | |
| """Calculate JPEG compression loss curve for quality estimation. | |
| Args: | |
| image: Input PIL image (RGB format) | |
| qualities: Quality values to test (1-100) | |
| normalize: Whether to normalize the output curve | |
| Returns: | |
| np.ndarray: Mean absolute difference values across quality levels | |
| """ | |
| # Convert input image to grayscale BGR for compression testing | |
| img_np = np.array(image) | |
| if len(img_np.shape) == 3: | |
| x = cv.cvtColor(img_np, cv.COLOR_RGB2GRAY) | |
| else: | |
| x = img_np | |
| c = np.array( | |
| [cv.mean(cv.absdiff(compress_jpg(x, q, False), x))[0] for q in qualities] | |
| ) | |
| if normalize: | |
| c = cv.normalize(c, None, 0, 1, cv.NORM_MINMAX).flatten() | |
| return c | |
| def estimate_qf(image): | |
| return np.argmin(loss_curve(image)) | |
| def get_tables(quality): | |
| luma = np.array( | |
| [ | |
| [16, 11, 10, 16, 24, 40, 51, 61], | |
| [12, 12, 14, 19, 26, 58, 60, 55], | |
| [14, 13, 16, 24, 40, 57, 69, 56], | |
| [14, 17, 22, 29, 51, 87, 80, 62], | |
| [18, 22, 37, 56, 68, 109, 103, 77], | |
| [24, 35, 55, 64, 81, 104, 113, 92], | |
| [49, 64, 78, 87, 103, 121, 120, 101], | |
| [72, 92, 95, 98, 112, 100, 103, 99], | |
| ] | |
| ) | |
| chroma = np.array( | |
| [ | |
| [17, 18, 24, 47, 99, 99, 99, 99], | |
| [18, 21, 26, 66, 99, 99, 99, 99], | |
| [24, 26, 56, 99, 99, 99, 99, 99], | |
| [47, 66, 99, 99, 99, 99, 99, 99], | |
| [99, 99, 99, 99, 99, 99, 99, 99], | |
| [99, 99, 99, 99, 99, 99, 99, 99], | |
| [99, 99, 99, 99, 99, 99, 99, 99], | |
| [99, 99, 99, 99, 99, 99, 99, 99], | |
| ] | |
| ) | |
| quality = np.clip(quality, 1, 100) | |
| if quality < 50: | |
| quality = 5000 / quality | |
| else: | |
| quality = 200 - quality * 2 | |
| tables = np.concatenate((luma[:, :, np.newaxis], chroma[:, :, np.newaxis]), axis=2) | |
| tables = (tables * quality + 50) / 100 | |
| return np.clip(tables, 1, 255).astype(int) | |