Spaces:
Sleeping
Sleeping
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import cv2
|
2 |
+
import numpy as np
|
3 |
+
import matplotlib.pyplot as plt
|
4 |
+
import pywt
|
5 |
+
from skimage import exposure
|
6 |
+
import gradio as gr
|
7 |
+
from io import BytesIO
|
8 |
+
from PIL import Image
|
9 |
+
|
10 |
+
def musica_enhancement(image):
|
11 |
+
"""
|
12 |
+
Enhances a 16-bit TIFF image using wavelet decomposition, CLAHE, gamma correction,
|
13 |
+
and edge-preserving sharpening.
|
14 |
+
|
15 |
+
Args:
|
16 |
+
image (PIL.Image): Uploaded image.
|
17 |
+
|
18 |
+
Returns:
|
19 |
+
enhanced_image (PIL.Image): Enhanced image.
|
20 |
+
histogram (PIL.Image): Histogram of the enhanced image.
|
21 |
+
"""
|
22 |
+
# Convert PIL Image to numpy array
|
23 |
+
img = np.array(image)
|
24 |
+
|
25 |
+
# Handle different image modes
|
26 |
+
if image.mode == 'I;16':
|
27 |
+
# 16-bit image
|
28 |
+
img = img.astype(np.uint16)
|
29 |
+
img_norm = img.astype(np.float32) / 65535.0
|
30 |
+
elif image.mode == 'I':
|
31 |
+
# 32-bit integer image
|
32 |
+
img = img.astype(np.int32)
|
33 |
+
img_norm = img.astype(np.float32) / (2**32 - 1)
|
34 |
+
elif image.mode in ['RGB', 'RGBA']:
|
35 |
+
# Convert to grayscale if it's a color image
|
36 |
+
img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
|
37 |
+
img_norm = img.astype(np.float32) / 255.0
|
38 |
+
elif image.mode == 'L':
|
39 |
+
# 8-bit grayscale
|
40 |
+
img_norm = img.astype(np.float32) / 255.0
|
41 |
+
else:
|
42 |
+
raise ValueError(f"Unsupported image mode: {image.mode}")
|
43 |
+
|
44 |
+
# 1. Multi-Scale Decomposition (3-level wavelet transform)
|
45 |
+
coeffs = pywt.wavedec2(img_norm, 'bior1.3', level=3)
|
46 |
+
cA3, (cH3, cV3, cD3), (cH2, cV2, cD2), (cH1, cV1, cD1) = coeffs
|
47 |
+
|
48 |
+
# 2. Adaptive Processing per Sub-band
|
49 |
+
cD1 = pywt.threshold(cD1, 0.05 * np.max(cD1), mode='soft')
|
50 |
+
cD2 = pywt.threshold(cD2, 0.07 * np.max(cD2), mode='soft')
|
51 |
+
cH1 = cH1 * 1.2
|
52 |
+
cV1 = cV1 * 1.2
|
53 |
+
|
54 |
+
# 3. Reconstruct Enhanced Image with clipping
|
55 |
+
coeffs_enhanced = [cA3, (cH3, cV3, cD3), (cH2, cV2, cD2), (cH1, cV1, cD1)]
|
56 |
+
img_recon = pywt.waverec2(coeffs_enhanced, 'bior1.3')
|
57 |
+
img_recon = np.clip(img_recon, 0, 1) # Critical fix
|
58 |
+
|
59 |
+
# 4. Adaptive CLAHE
|
60 |
+
entropy = -np.sum(img_recon * np.log2(img_recon + 1e-7)) # Now safe
|
61 |
+
clip_limit = 0.02 if entropy > 7 else 0.05
|
62 |
+
img_clahe = exposure.equalize_adapthist(img_recon, clip_limit=clip_limit, kernel_size=64)
|
63 |
+
|
64 |
+
# 5. Gamma correction
|
65 |
+
p5, p95 = np.percentile(img_clahe, (5, 95))
|
66 |
+
gamma = 0.7 if (p95 - p5) < 0.3 else 0.9
|
67 |
+
img_gamma = exposure.adjust_gamma(img_clahe, gamma=gamma)
|
68 |
+
|
69 |
+
# 6. Edge-Preserving Sharpening (convert to BGR first)
|
70 |
+
img_gamma_8bit = (img_gamma * 255).astype(np.uint8)
|
71 |
+
img_bgr = cv2.cvtColor(img_gamma_8bit, cv2.COLOR_GRAY2BGR) # Convert to 3-channel
|
72 |
+
img_sharp = cv2.detailEnhance(img_bgr, sigma_s=12, sigma_r=0.15)
|
73 |
+
img_sharp = cv2.cvtColor(img_sharp, cv2.COLOR_BGR2GRAY) # Convert back to grayscale
|
74 |
+
|
75 |
+
# Convert enhanced image to PIL Image
|
76 |
+
enhanced_image = Image.fromarray(img_sharp)
|
77 |
+
|
78 |
+
# Create histogram plot
|
79 |
+
plt.figure(figsize=(6, 4))
|
80 |
+
plt.hist(img_sharp.ravel(), bins=256, range=(0, 255), color='gray')
|
81 |
+
plt.title('Enhanced Histogram')
|
82 |
+
plt.xlabel('Pixel Intensity')
|
83 |
+
plt.ylabel('Frequency')
|
84 |
+
plt.tight_layout()
|
85 |
+
buf = BytesIO()
|
86 |
+
plt.savefig(buf, format='png')
|
87 |
+
plt.close()
|
88 |
+
buf.seek(0)
|
89 |
+
histogram = Image.open(buf)
|
90 |
+
|
91 |
+
return enhanced_image, histogram
|
92 |
+
|
93 |
+
# Define Gradio interface
|
94 |
+
with gr.Blocks() as demo:
|
95 |
+
gr.Markdown("# Musica Image Enhancement")
|
96 |
+
gr.Markdown(
|
97 |
+
"""
|
98 |
+
Upload a 16-bit TIFF image to enhance it using wavelet decomposition, CLAHE,
|
99 |
+
gamma correction, and edge-preserving sharpening.
|
100 |
+
"""
|
101 |
+
)
|
102 |
+
with gr.Row():
|
103 |
+
with gr.Column():
|
104 |
+
input_image = gr.Image(
|
105 |
+
type="pil",
|
106 |
+
label="Upload 16-bit TIFF Image",
|
107 |
+
source="upload",
|
108 |
+
tool="editor",
|
109 |
+
)
|
110 |
+
run_button = gr.Button("Enhance Image")
|
111 |
+
with gr.Column():
|
112 |
+
output_image = gr.Image(type="pil", label="Enhanced Image")
|
113 |
+
output_hist = gr.Image(type="pil", label="Enhanced Histogram")
|
114 |
+
|
115 |
+
run_button.click(
|
116 |
+
fn=musica_enhancement,
|
117 |
+
inputs=input_image,
|
118 |
+
outputs=[output_image, output_hist],
|
119 |
+
)
|
120 |
+
|
121 |
+
if __name__ == "__main__":
|
122 |
+
demo.launch()
|