File size: 24,133 Bytes
3b8bdca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d54d7a7
 
 
 
 
 
 
 
 
 
3b8bdca
 
 
 
 
 
d54d7a7
3b8bdca
 
 
 
d54d7a7
3b8bdca
 
 
 
 
 
d54d7a7
3b8bdca
 
 
 
 
 
 
 
 
d54d7a7
 
 
 
 
3b8bdca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d54d7a7
3b8bdca
 
 
 
d54d7a7
 
 
 
 
 
 
 
 
 
3b8bdca
 
 
 
 
 
d54d7a7
3b8bdca
 
 
 
 
 
 
 
 
 
 
 
d54d7a7
3b8bdca
 
d54d7a7
 
 
 
 
 
 
3b8bdca
 
 
 
d54d7a7
 
 
 
 
 
 
3b8bdca
 
d54d7a7
 
 
 
 
 
 
 
 
 
3b8bdca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d54d7a7
 
 
 
 
 
 
 
 
3b8bdca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d54d7a7
 
 
 
 
 
 
 
 
 
 
 
 
3b8bdca
 
 
 
 
 
 
 
f1d9f4b
 
3b8bdca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d54d7a7
 
 
 
 
 
 
 
3b8bdca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d54d7a7
 
 
 
 
 
 
 
3b8bdca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
import streamlit as st
import cv2
import numpy as np
from PIL import Image
import io
import matplotlib.pyplot as plt

def apply_threshold(image, threshold_type, thresh_value=127, max_value=255):
    if threshold_type == "Binary":
        _, thresh = cv2.threshold(image, thresh_value, max_value, cv2.THRESH_BINARY)
    elif threshold_type == "Binary Inverse":
        _, thresh = cv2.threshold(image, thresh_value, max_value, cv2.THRESH_BINARY_INV)
    elif threshold_type == "Truncate":
        _, thresh = cv2.threshold(image, thresh_value, max_value, cv2.THRESH_TRUNC)
    elif threshold_type == "To Zero":
        _, thresh = cv2.threshold(image, thresh_value, max_value, cv2.THRESH_TOZERO)
    elif threshold_type == "To Zero Inverse":
        _, thresh = cv2.threshold(image, thresh_value, max_value, cv2.THRESH_TOZERO_INV)
    elif threshold_type == "Adaptive Mean":
        thresh = cv2.adaptiveThreshold(image, max_value, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2)
    elif threshold_type == "Adaptive Gaussian":
        thresh = cv2.adaptiveThreshold(image, max_value, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)
    elif threshold_type == "Otsu":
        _, thresh = cv2.threshold(image, 0, max_value, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
    return thresh

def apply_histogram_equalization(image, method):
    if method == "Simple":
        if len(image.shape) == 3:
            img_yuv = cv2.cvtColor(image, cv2.COLOR_BGR2YUV)
            img_yuv[:,:,0] = cv2.equalizeHist(img_yuv[:,:,0])
            return cv2.cvtColor(img_yuv, cv2.COLOR_YUV2BGR)
        else:
            return cv2.equalizeHist(image)
    elif method == "CLAHE":
        clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
        if len(image.shape) == 3:
            img_yuv = cv2.cvtColor(image, cv2.COLOR_BGR2YUV)
            img_yuv[:,:,0] = clahe.apply(img_yuv[:,:,0])
            return cv2.cvtColor(img_yuv, cv2.COLOR_YUV2BGR)
        else:
            return clahe.apply(image)
    return image

def apply_color_quantization(image, k):
    data = np.float32(image).reshape((-1,3))
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 20, 1.0)
    _, label, center = cv2.kmeans(data, k, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
    center = np.uint8(center)
    result = center[label.flatten()]
    return result.reshape(image.shape)

def main():
    st.set_page_config(layout="wide")
    st.title("Advanced Image Processing Laboratory")
    st.markdown("""

    <style>

    .main {

        background-color: #f0f2f6;

    }

    .stButton>button {

        width: 100%;

    }

    </style>

    """, unsafe_allow_html=True)

    # Sidebar
    st.sidebar.title("Controls Panel")
    uploaded_file = st.sidebar.file_uploader("Upload Image", type=["jpg", "jpeg", "png"])

    if uploaded_file is not None:
        file_bytes = np.asarray(bytearray(uploaded_file.read()), dtype=np.uint8)
        original_img = cv2.imdecode(file_bytes, cv2.IMREAD_COLOR)
        
        # Create columns for better layout
        col1, col2 = st.columns(2)
        
        with col1:
            st.subheader("Original Image")
            st.image(cv2.cvtColor(original_img, cv2.COLOR_BGR2RGB))

        # Main processing options
        processing_option = st.sidebar.selectbox(
            "Select Processing Category",
            ["Basic Operations", "Filtering", "Color Spaces", "Thresholding", 
             "Morphological Operations", "Edge Detection", "Feature Detection",
             "Histogram Operations", "Advanced Effects"]
        )

        # Process and display result
        with col2:
            st.subheader("Processed Image")
            
            if processing_option == "Basic Operations":
                st.sidebar.markdown("""

                ### Basic Operations

                Transform your image with fundamental operations:

                - **Resize**: Scale the image up or down

                - **Rotate**: Rotate the image by any angle

                - **Flip**: Mirror the image horizontally or vertically

                - **Brightness/Contrast**: Adjust image lighting

                - **Color Quantization**: Reduce the number of colors

                """)
                
                operation = st.sidebar.selectbox(
                    "Select Operation",
                    ["Resize", "Rotate", "Flip", "Brightness/Contrast", "Color Quantization"]
                )
                
                if operation == "Resize":
                    st.sidebar.markdown("Adjust the scale factor to resize the image. Values > 1 enlarge, values < 1 shrink.")
                    scale = st.sidebar.slider("Scale Factor", 0.1, 2.0, 1.0)
                    processed_img = cv2.resize(original_img, None, fx=scale, fy=scale)
                
                elif operation == "Rotate":
                    st.sidebar.markdown("Rotate the image by specifying an angle in degrees. Positive values rotate counter-clockwise.")
                    angle = st.sidebar.slider("Angle", -180, 180, 0)
                    center = (original_img.shape[1] // 2, original_img.shape[0] // 2)
                    matrix = cv2.getRotationMatrix2D(center, angle, 1.0)
                    processed_img = cv2.warpAffine(original_img, matrix, (original_img.shape[1], original_img.shape[0]))
                
                elif operation == "Flip":
                    st.sidebar.markdown("Mirror the image in different directions.")
                    flip_option = st.sidebar.selectbox("Flip Direction", ["Horizontal", "Vertical", "Both"])
                    if flip_option == "Horizontal":
                        processed_img = cv2.flip(original_img, 1)
                    elif flip_option == "Vertical":
                        processed_img = cv2.flip(original_img, 0)
                    else:
                        processed_img = cv2.flip(original_img, -1)
                
                elif operation == "Brightness/Contrast":
                    st.sidebar.markdown("""

                    Adjust image brightness and contrast:

                    - **Brightness**: Negative values darken, positive values brighten

                    - **Contrast**: Negative values decrease contrast, positive values increase it

                    """)
                    brightness = st.sidebar.slider("Brightness", -100, 100, 0)
                    contrast = st.sidebar.slider("Contrast", -100, 100, 0)
                    
                    processed_img = original_img.copy()
                    if brightness != 0:
                        if brightness > 0:
                            shadow = brightness
                            highlight = 255
                        else:
                            shadow = 0
                            highlight = 255 + brightness
                        alpha_b = (highlight - shadow)/255
                        gamma_b = shadow
                        processed_img = cv2.addWeighted(processed_img, alpha_b, processed_img, 0, gamma_b)
                    
                    if contrast != 0:
                        f = 131*(contrast + 127)/(127*(131-contrast))
                        alpha_c = f
                        gamma_c = 127*(1-f)
                        processed_img = cv2.addWeighted(processed_img, alpha_c, processed_img, 0, gamma_c)

                elif operation == "Color Quantization":
                    st.sidebar.markdown("Reduce the number of colors in the image. Lower values create more poster-like effects.")
                    k = st.sidebar.slider("Number of Colors", 2, 16, 8)
                    processed_img = apply_color_quantization(original_img, k)

            elif processing_option == "Filtering":
                st.sidebar.markdown("""

                ### Image Filtering

                Apply different filters to smooth or enhance the image:

                - **Blur**: Simple averaging filter

                - **Gaussian**: Weighted gaussian smoothing

                - **Median**: Good for removing salt-and-pepper noise

                - **Bilateral**: Edge-preserving smoothing

                - **Custom Kernel**: Apply specific filter effects

                """)
                
                filter_type = st.sidebar.selectbox(
                    "Select Filter",
                    ["Blur", "Gaussian", "Median", "Bilateral", "Custom Kernel"]
                )
                
                if filter_type == "Custom Kernel":
                    st.sidebar.markdown("Apply predefined kernel effects. Larger kernel sizes create stronger effects.")
                    kernel_size = st.sidebar.slider("Kernel Size", 3, 7, 3, step=2)
                    kernel_type = st.sidebar.selectbox("Kernel Type", ["Sharpen", "Edge Detection", "Emboss"])
                    
                    if kernel_type == "Sharpen":
                        kernel = np.array([[-1,-1,-1], [-1,9,-1], [-1,-1,-1]])
                    elif kernel_type == "Edge Detection":
                        kernel = np.array([[-1,-1,-1], [-1,8,-1], [-1,-1,-1]])
                    elif kernel_type == "Emboss":
                        kernel = np.array([[-2,-1,0], [-1,1,1], [0,1,2]])
                    
                    processed_img = cv2.filter2D(original_img, -1, kernel)
                else:
                    st.sidebar.markdown("Adjust kernel size to control the strength of the filter effect.")
                    kernel_size = st.sidebar.slider("Kernel Size", 3, 15, 3, step=2)
                    
                    if filter_type == "Bilateral":
                        st.sidebar.markdown("""

                        Bilateral Filter Parameters:

                        - **d**: Diameter of pixel neighborhood

                        - **Sigma Color**: Filter sigma in color space

                        - **Sigma Space**: Filter sigma in coordinate space

                        """)
                        d = st.sidebar.slider("d", 1, 15, 9)
                        sigma_color = st.sidebar.slider("Sigma Color", 1, 255, 75)
                        sigma_space = st.sidebar.slider("Sigma Space", 1, 255, 75)
                        processed_img = cv2.bilateralFilter(original_img, d, sigma_color, sigma_space)
                    else:
                        if filter_type == "Blur":
                            processed_img = cv2.blur(original_img, (kernel_size, kernel_size))
                        elif filter_type == "Gaussian":
                            processed_img = cv2.GaussianBlur(original_img, (kernel_size, kernel_size), 0)
                        elif filter_type == "Median":
                            processed_img = cv2.medianBlur(original_img, kernel_size)

            elif processing_option == "Color Spaces":
                st.sidebar.markdown("""

                ### Color Spaces

                Convert the image between different color representations:

                - **RGB**: Standard Red-Green-Blue color space

                - **HSV**: Hue-Saturation-Value, useful for color segmentation

                - **LAB**: Perceptually uniform color space

                - **YCrCb**: Used in video encoding

                - **Individual Channels**: View color components separately

                """)
                
                color_space = st.sidebar.selectbox(
                    "Select Color Space",
                    ["RGB", "HSV", "LAB", "YCrCb", "Individual Channels"]
                )
                
                if color_space == "RGB":
                    processed_img = cv2.cvtColor(original_img, cv2.COLOR_BGR2RGB)
                elif color_space == "HSV":
                    processed_img = cv2.cvtColor(original_img, cv2.COLOR_BGR2HSV)
                elif color_space == "LAB":
                    processed_img = cv2.cvtColor(original_img, cv2.COLOR_BGR2LAB)
                elif color_space == "YCrCb":
                    processed_img = cv2.cvtColor(original_img, cv2.COLOR_BGR2YCrCb)
                elif color_space == "Individual Channels":
                    channel = st.sidebar.selectbox("Select Channel", ["Blue", "Green", "Red"])
                    if channel == "Blue":
                        processed_img = original_img[:,:,0]
                    elif channel == "Green":
                        processed_img = original_img[:,:,1]
                    else:
                        processed_img = original_img[:,:,2]

            elif processing_option == "Thresholding":
                # Convert to grayscale for thresholding
                gray_img = cv2.cvtColor(original_img, cv2.COLOR_BGR2GRAY)
                threshold_type = st.sidebar.selectbox(
                    "Select Threshold Type",
                    ["Binary", "Binary Inverse", "Truncate", "To Zero", "To Zero Inverse",
                     "Adaptive Mean", "Adaptive Gaussian", "Otsu"]
                )
                
                if threshold_type in ["Binary", "Binary Inverse", "Truncate", "To Zero", "To Zero Inverse"]:
                    thresh_value = st.sidebar.slider("Threshold Value", 0, 255, 127)
                    max_value = st.sidebar.slider("Maximum Value", 0, 255, 255)
                    processed_img = apply_threshold(gray_img, threshold_type, thresh_value, max_value)
                else:
                    processed_img = apply_threshold(gray_img, threshold_type)

            elif processing_option == "Morphological Operations":
                operation = st.sidebar.selectbox(
                    "Select Operation",
                    ["Erosion", "Dilation", "Opening", "Closing", "Gradient", "Top Hat", "Black Hat"]
                )
                
                kernel_size = st.sidebar.slider("Kernel Size", 3, 15, 5, step=2)
                kernel = np.ones((kernel_size, kernel_size), np.uint8)
                
                if operation == "Erosion":
                    processed_img = cv2.erode(original_img, kernel, iterations=1)
                elif operation == "Dilation":
                    processed_img = cv2.dilate(original_img, kernel, iterations=1)
                elif operation == "Opening":
                    processed_img = cv2.morphologyEx(original_img, cv2.MORPH_OPEN, kernel)
                elif operation == "Closing":
                    processed_img = cv2.morphologyEx(original_img, cv2.MORPH_CLOSE, kernel)
                elif operation == "Gradient":
                    processed_img = cv2.morphologyEx(original_img, cv2.MORPH_GRADIENT, kernel)
                elif operation == "Top Hat":
                    processed_img = cv2.morphologyEx(original_img, cv2.MORPH_TOPHAT, kernel)
                elif operation == "Black Hat":
                    processed_img = cv2.morphologyEx(original_img, cv2.MORPH_BLACKHAT, kernel)

            elif processing_option == "Edge Detection":
                st.sidebar.markdown("""

                ### Edge Detection

                Different methods to detect edges in the image:

                - **Canny**: Advanced edge detector with thresholds

                - **Sobel**: Directional gradient detection

                - **Laplacian**: Detect edges using 2nd derivatives

                - **Scharr**: More accurate gradient calculation

                """)
                
                detector = st.sidebar.selectbox(
                    "Select Detector",
                    ["Canny", "Sobel", "Laplacian", "Scharr"]
                )
                
                if detector == "Canny":
                    threshold1 = st.sidebar.slider("Threshold 1", 0, 255, 100)
                    threshold2 = st.sidebar.slider("Threshold 2", 0, 255, 200)
                    processed_img = cv2.Canny(original_img, threshold1, threshold2)
                
                elif detector == "Sobel":
                    dx = st.sidebar.slider("dx", 0, 2, 1)
                    dy = st.sidebar.slider("dy", 0, 2, 1)
                    ksize = st.sidebar.slider("Kernel Size", 1, 7, 3, step=2)
                    gray = cv2.cvtColor(original_img, cv2.COLOR_BGR2GRAY)
                    processed_img = cv2.Sobel(gray, cv2.CV_64F, dx, dy, ksize=ksize)
                    processed_img = np.uint8(np.absolute(processed_img))
                
                elif detector == "Laplacian":
                    ksize = st.sidebar.slider("Kernel Size", 1, 7, 3, step=2)
                    gray = cv2.cvtColor(original_img, cv2.COLOR_BGR2GRAY)
                    processed_img = cv2.Laplacian(gray, cv2.CV_64F, ksize=ksize)
                    processed_img = np.uint8(np.absolute(processed_img))
                
                elif detector == "Scharr":
                    direction = st.sidebar.selectbox("Direction", ["X", "Y"])
                    gray = cv2.cvtColor(original_img, cv2.COLOR_BGR2GRAY)
                    if direction == "X":
                        processed_img = cv2.Scharr(gray, cv2.CV_64F, 1, 0)
                    else:
                        processed_img = cv2.Scharr(gray, cv2.CV_64F, 0, 1)
                    processed_img = np.uint8(np.absolute(processed_img))

            elif processing_option == "Feature Detection":
                st.sidebar.markdown("""

                ### Feature Detection

                Detect interesting points or features in the image:

                - **Harris Corner**: Detects corner points using intensity changes

                - **Shi-Tomasi**: More robust corner detection

                - **FAST**: High-speed corner detection

                

                Parameters for Harris Corner:

                - **Block Size**: Size of neighborhood considered

                - **Kernel Size**: Aperture parameter for Sobel operator

                - **k**: Harris detector free parameter

                """)
                
                detector = st.sidebar.selectbox(
                    "Select Detector",
                    ["Harris Corner", "Shi-Tomasi", "FAST"]
                )
                
                if detector == "Harris Corner":
                    gray = cv2.cvtColor(original_img, cv2.COLOR_BGR2GRAY)
                    block_size = st.sidebar.slider("Block Size", 2, 10, 2)
                    ksize = st.sidebar.slider("Kernel Size", 3, 31, 3, step=2)
                    k = st.sidebar.slider("k", 0.01, 0.1, 0.04, step=0.01)
                    
                    processed_img = original_img.copy()
                    gray = np.float32(gray)
                    dst = cv2.cornerHarris(gray, block_size, ksize, k)
                    dst = cv2.dilate(dst, None)
                    processed_img[dst > 0.01 * dst.max()] = [0, 0, 255]
                
                elif detector == "Shi-Tomasi":
                    gray = cv2.cvtColor(original_img, cv2.COLOR_BGR2GRAY)
                    corners = cv2.goodFeaturesToTrack(gray, 25, 0.01, 10)
                    corners = np.int0(corners)
                    processed_img = original_img.copy()
                    for i in corners:
                        x, y = i.ravel()
                        cv2.circle(processed_img, (x, y), 3, [0, 0, 255], -1)
                
                elif detector == "FAST":
                    gray = cv2.cvtColor(original_img, cv2.COLOR_BGR2GRAY)
                    fast = cv2.FastFeatureDetector_create()
                    kp = fast.detect(gray, None)
                    processed_img = original_img.copy()
                    cv2.drawKeypoints(original_img, kp, processed_img, color=(0, 0, 255))

            elif processing_option == "Histogram Operations":
                st.sidebar.markdown("""

                ### Histogram Operations

                Analyze and modify image intensity distribution:

                - **Show Histogram**: Display color/intensity distribution

                - **Equalization**: Enhance contrast using histogram equalization

                - **CLAHE**: Contrast Limited Adaptive Histogram Equalization

                """)
                
                operation = st.sidebar.selectbox(
                    "Select Operation",
                    ["Show Histogram", "Equalization", "CLAHE"]
                )
                
                if operation == "Show Histogram":
                    fig, ax = plt.subplots()
                    if len(original_img.shape) == 3:
                        colors = ('b', 'g', 'r')
                        for i, color in enumerate(colors):
                            hist = cv2.calcHist([original_img], [i], None, [256], [0, 256])
                            ax.plot(hist, color=color)
                    else:
                        hist = cv2.calcHist([original_img], [0], None, [256], [0, 256])
                        ax.plot(hist)
                    st.pyplot(fig)
                    processed_img = original_img
                
                elif operation == "Equalization":
                    processed_img = apply_histogram_equalization(original_img, "Simple")
                
                elif operation == "CLAHE":
                    processed_img = apply_histogram_equalization(original_img, "CLAHE")

            elif processing_option == "Advanced Effects":
                st.sidebar.markdown("""

                ### Advanced Effects

                Apply complex image transformations:

                - **Pencil Sketch**: Convert image to pencil drawing style

                - **Cartoon**: Create cartoon-like effect

                - **HDR Effect**: Enhance local details

                """)
                
                effect = st.sidebar.selectbox(
                    "Select Effect",
                    ["Pencil Sketch", "Cartoon", "HDR Effect"]
                )
                
                if effect == "Pencil Sketch":
                    gray = cv2.cvtColor(original_img, cv2.COLOR_BGR2GRAY)
                    inv = 255 - gray
                    blur = cv2.GaussianBlur(inv, (21, 21), 0)
                    processed_img = cv2.divide(gray, 255-blur, scale=256.0)
                
                elif effect == "Cartoon":
                    gray = cv2.cvtColor(original_img, cv2.COLOR_BGR2GRAY)
                    gray = cv2.medianBlur(gray, 5)
                    edges = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 9, 9)
                    color = cv2.bilateralFilter(original_img, 9, 250, 250)
                    processed_img = cv2.bitwise_and(color, color, mask=edges)
                
                elif effect == "HDR Effect":
                    processed_img = cv2.detailEnhance(original_img, sigma_s=12, sigma_r=0.15)

            # Display processed image
            if len(processed_img.shape) == 3:
                st.image(cv2.cvtColor(processed_img, cv2.COLOR_BGR2RGB))
            else:
                st.image(processed_img, clamp=True)

            # Add download button for processed image
            if st.button("Download Processed Image"):
                if len(processed_img.shape) == 3:
                    processed_img_rgb = cv2.cvtColor(processed_img, cv2.COLOR_BGR2RGB)
                else:
                    processed_img_rgb = processed_img
                pil_img = Image.fromarray(processed_img_rgb)
                img_bytes = io.BytesIO()
                pil_img.save(img_bytes, format='PNG')
                st.download_button(
                    label="Download Image",
                    data=img_bytes.getvalue(),
                    file_name="processed_image.png",
                    mime="image/png"
                )

if __name__ == "__main__":
    main()