Spaces:
Sleeping
Sleeping
Added more image editing features.
Browse files- app.py +67 -3
- app/image_processing.py +85 -1
- requirements.txt +5 -5
app.py
CHANGED
|
@@ -15,7 +15,13 @@ def process_image_pipeline(
|
|
| 15 |
filter_type: str,
|
| 16 |
bg_color_r: int,
|
| 17 |
bg_color_g: int,
|
| 18 |
-
bg_color_b: int
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
) -> np.ndarray:
|
| 20 |
"""Process image with selected parameters"""
|
| 21 |
try:
|
|
@@ -31,6 +37,17 @@ def process_image_pipeline(
|
|
| 31 |
bg_color = (bg_color_r, bg_color_g, bg_color_b, 255)
|
| 32 |
|
| 33 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
# Process image using the ImageProcessor class
|
| 35 |
# First remove background
|
| 36 |
processed = ImageProcessor.remove_background(
|
|
@@ -78,7 +95,8 @@ def create_gradio_interface():
|
|
| 78 |
|
| 79 |
# Define available filters from the image processor's filter dictionary
|
| 80 |
filter_type = gr.Dropdown(
|
| 81 |
-
choices=["none", "grayscale", "sepia", "blur", "sharpen",
|
|
|
|
| 82 |
value="none",
|
| 83 |
label="Filter Type",
|
| 84 |
info="Select a filter to apply to the image"
|
|
@@ -110,6 +128,46 @@ def create_gradio_interface():
|
|
| 110 |
info="Blue component of background color"
|
| 111 |
)
|
| 112 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
# Define output with PNG format
|
| 114 |
output_image = gr.Image(
|
| 115 |
label="Processed Image",
|
|
@@ -126,7 +184,13 @@ def create_gradio_interface():
|
|
| 126 |
filter_type,
|
| 127 |
bg_color_r,
|
| 128 |
bg_color_g,
|
| 129 |
-
bg_color_b
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
],
|
| 131 |
outputs=output_image,
|
| 132 |
title="Advanced Image Processing Tool",
|
|
|
|
| 15 |
filter_type: str,
|
| 16 |
bg_color_r: int,
|
| 17 |
bg_color_g: int,
|
| 18 |
+
bg_color_b: int,
|
| 19 |
+
brightness: float,
|
| 20 |
+
contrast: float,
|
| 21 |
+
saturation: float,
|
| 22 |
+
rotation: int,
|
| 23 |
+
flip_horizontal: bool,
|
| 24 |
+
flip_vertical: bool
|
| 25 |
) -> np.ndarray:
|
| 26 |
"""Process image with selected parameters"""
|
| 27 |
try:
|
|
|
|
| 37 |
bg_color = (bg_color_r, bg_color_g, bg_color_b, 255)
|
| 38 |
|
| 39 |
try:
|
| 40 |
+
# Apply adjustments first
|
| 41 |
+
input_image = ImageProcessor.adjust_image(
|
| 42 |
+
input_image,
|
| 43 |
+
brightness=brightness,
|
| 44 |
+
contrast=contrast,
|
| 45 |
+
saturation=saturation,
|
| 46 |
+
rotation=rotation,
|
| 47 |
+
flip_horizontal=flip_horizontal,
|
| 48 |
+
flip_vertical=flip_vertical
|
| 49 |
+
)
|
| 50 |
+
|
| 51 |
# Process image using the ImageProcessor class
|
| 52 |
# First remove background
|
| 53 |
processed = ImageProcessor.remove_background(
|
|
|
|
| 95 |
|
| 96 |
# Define available filters from the image processor's filter dictionary
|
| 97 |
filter_type = gr.Dropdown(
|
| 98 |
+
choices=["none", "grayscale", "sepia", "blur", "sharpen",
|
| 99 |
+
"edge_detect", "emboss", "sketch", "watercolor", "invert"],
|
| 100 |
value="none",
|
| 101 |
label="Filter Type",
|
| 102 |
info="Select a filter to apply to the image"
|
|
|
|
| 128 |
info="Blue component of background color"
|
| 129 |
)
|
| 130 |
|
| 131 |
+
with gr.Row():
|
| 132 |
+
brightness = gr.Slider(
|
| 133 |
+
minimum=0.0,
|
| 134 |
+
maximum=2.0,
|
| 135 |
+
value=1.0,
|
| 136 |
+
step=0.1,
|
| 137 |
+
label="Brightness"
|
| 138 |
+
)
|
| 139 |
+
contrast = gr.Slider(
|
| 140 |
+
minimum=0.0,
|
| 141 |
+
maximum=2.0,
|
| 142 |
+
value=1.0,
|
| 143 |
+
step=0.1,
|
| 144 |
+
label="Contrast"
|
| 145 |
+
)
|
| 146 |
+
saturation = gr.Slider(
|
| 147 |
+
minimum=0.0,
|
| 148 |
+
maximum=2.0,
|
| 149 |
+
value=1.0,
|
| 150 |
+
step=0.1,
|
| 151 |
+
label="Saturation"
|
| 152 |
+
)
|
| 153 |
+
|
| 154 |
+
with gr.Row():
|
| 155 |
+
rotation = gr.Slider(
|
| 156 |
+
minimum=-180,
|
| 157 |
+
maximum=180,
|
| 158 |
+
value=0,
|
| 159 |
+
step=90,
|
| 160 |
+
label="Rotation"
|
| 161 |
+
)
|
| 162 |
+
flip_horizontal = gr.Checkbox(
|
| 163 |
+
label="Flip Horizontal",
|
| 164 |
+
value=False
|
| 165 |
+
)
|
| 166 |
+
flip_vertical = gr.Checkbox(
|
| 167 |
+
label="Flip Vertical",
|
| 168 |
+
value=False
|
| 169 |
+
)
|
| 170 |
+
|
| 171 |
# Define output with PNG format
|
| 172 |
output_image = gr.Image(
|
| 173 |
label="Processed Image",
|
|
|
|
| 184 |
filter_type,
|
| 185 |
bg_color_r,
|
| 186 |
bg_color_g,
|
| 187 |
+
bg_color_b,
|
| 188 |
+
brightness,
|
| 189 |
+
contrast,
|
| 190 |
+
saturation,
|
| 191 |
+
rotation,
|
| 192 |
+
flip_horizontal,
|
| 193 |
+
flip_vertical
|
| 194 |
],
|
| 195 |
outputs=output_image,
|
| 196 |
title="Advanced Image Processing Tool",
|
app/image_processing.py
CHANGED
|
@@ -117,7 +117,11 @@ class ImageProcessor:
|
|
| 117 |
"blur": lambda img: cv2.GaussianBlur(img, (5, 5), 0),
|
| 118 |
"sharpen": lambda img: ImageProcessor._sharpen_image(img),
|
| 119 |
"edge_detect": lambda img: cv2.Canny(img, 100, 200),
|
| 120 |
-
"none": lambda img: img
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
}
|
| 122 |
|
| 123 |
# Apply selected filter
|
|
@@ -144,6 +148,86 @@ class ImageProcessor:
|
|
| 144 |
])
|
| 145 |
return cv2.filter2D(image, -1, kernel)
|
| 146 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
@staticmethod
|
| 148 |
def convert_to_format(
|
| 149 |
image: np.ndarray,
|
|
|
|
| 117 |
"blur": lambda img: cv2.GaussianBlur(img, (5, 5), 0),
|
| 118 |
"sharpen": lambda img: ImageProcessor._sharpen_image(img),
|
| 119 |
"edge_detect": lambda img: cv2.Canny(img, 100, 200),
|
| 120 |
+
"none": lambda img: img,
|
| 121 |
+
"emboss": lambda img: ImageProcessor._apply_emboss(img),
|
| 122 |
+
"sketch": lambda img: ImageProcessor._apply_sketch(img),
|
| 123 |
+
"watercolor": lambda img: ImageProcessor._apply_watercolor(img),
|
| 124 |
+
"invert": lambda img: cv2.bitwise_not(img)
|
| 125 |
}
|
| 126 |
|
| 127 |
# Apply selected filter
|
|
|
|
| 148 |
])
|
| 149 |
return cv2.filter2D(image, -1, kernel)
|
| 150 |
|
| 151 |
+
@staticmethod
|
| 152 |
+
def _apply_emboss(image: np.ndarray) -> np.ndarray:
|
| 153 |
+
"""Apply emboss effect"""
|
| 154 |
+
kernel = np.array([[-2,-1,0], [-1,1,1], [0,1,2]])
|
| 155 |
+
return cv2.filter2D(image, -1, kernel) + 128
|
| 156 |
+
|
| 157 |
+
@staticmethod
|
| 158 |
+
def _apply_sketch(image: np.ndarray) -> np.ndarray:
|
| 159 |
+
"""Create pencil sketch effect"""
|
| 160 |
+
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
|
| 161 |
+
blur = cv2.GaussianBlur(gray, (5,5), 0)
|
| 162 |
+
sketch = cv2.divide(gray, blur, scale=256.0)
|
| 163 |
+
return cv2.cvtColor(sketch, cv2.COLOR_GRAY2RGB)
|
| 164 |
+
|
| 165 |
+
@staticmethod
|
| 166 |
+
def _apply_watercolor(image: np.ndarray) -> np.ndarray:
|
| 167 |
+
"""Create watercolor effect"""
|
| 168 |
+
temp = cv2.stylization(image, sigma_s=60, sigma_r=0.6)
|
| 169 |
+
return cv2.edgePreservingFilter(temp, flags=1, sigma_s=64, sigma_r=0.2)
|
| 170 |
+
|
| 171 |
+
@staticmethod
|
| 172 |
+
def adjust_image(
|
| 173 |
+
image: np.ndarray,
|
| 174 |
+
brightness: float = 1.0,
|
| 175 |
+
contrast: float = 1.0,
|
| 176 |
+
saturation: float = 1.0,
|
| 177 |
+
rotation: int = 0,
|
| 178 |
+
flip_horizontal: bool = False,
|
| 179 |
+
flip_vertical: bool = False
|
| 180 |
+
) -> np.ndarray:
|
| 181 |
+
"""
|
| 182 |
+
Apply various adjustments to the image
|
| 183 |
+
|
| 184 |
+
Args:
|
| 185 |
+
image: Input image
|
| 186 |
+
brightness: Brightness factor (0.0 to 2.0)
|
| 187 |
+
contrast: Contrast factor (0.0 to 2.0)
|
| 188 |
+
saturation: Saturation factor (0.0 to 2.0)
|
| 189 |
+
rotation: Rotation angle in degrees
|
| 190 |
+
flip_horizontal: Whether to flip horizontally
|
| 191 |
+
flip_vertical: Whether to flip vertically
|
| 192 |
+
"""
|
| 193 |
+
try:
|
| 194 |
+
# Convert to float for processing
|
| 195 |
+
img_float = image.astype(float)
|
| 196 |
+
|
| 197 |
+
# Apply brightness
|
| 198 |
+
img_float = cv2.multiply(img_float, brightness)
|
| 199 |
+
|
| 200 |
+
# Apply contrast
|
| 201 |
+
mean = np.mean(img_float)
|
| 202 |
+
img_float = (img_float - mean) * contrast + mean
|
| 203 |
+
|
| 204 |
+
# Apply saturation
|
| 205 |
+
if len(image.shape) == 3: # Only for color images
|
| 206 |
+
hsv = cv2.cvtColor(np.clip(img_float, 0, 255).astype(np.uint8), cv2.COLOR_RGB2HSV)
|
| 207 |
+
hsv[:, :, 1] = hsv[:, :, 1] * saturation
|
| 208 |
+
img_float = cv2.cvtColor(hsv, cv2.COLOR_HSV2RGB).astype(float)
|
| 209 |
+
|
| 210 |
+
# Clip values
|
| 211 |
+
img_float = np.clip(img_float, 0, 255).astype(np.uint8)
|
| 212 |
+
|
| 213 |
+
# Apply rotation
|
| 214 |
+
if rotation != 0:
|
| 215 |
+
center = (image.shape[1] // 2, image.shape[0] // 2)
|
| 216 |
+
matrix = cv2.getRotationMatrix2D(center, rotation, 1.0)
|
| 217 |
+
img_float = cv2.warpAffine(img_float, matrix, (image.shape[1], image.shape[0]))
|
| 218 |
+
|
| 219 |
+
# Apply flips
|
| 220 |
+
if flip_horizontal:
|
| 221 |
+
img_float = cv2.flip(img_float, 1)
|
| 222 |
+
if flip_vertical:
|
| 223 |
+
img_float = cv2.flip(img_float, 0)
|
| 224 |
+
|
| 225 |
+
return img_float
|
| 226 |
+
|
| 227 |
+
except Exception as e:
|
| 228 |
+
print(f"Image adjustment error: {e}")
|
| 229 |
+
return image
|
| 230 |
+
|
| 231 |
@staticmethod
|
| 232 |
def convert_to_format(
|
| 233 |
image: np.ndarray,
|
requirements.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
-
numpy>=1.
|
| 2 |
-
opencv-python>=4.
|
| 3 |
-
Pillow>=
|
| 4 |
-
gradio>=3.
|
| 5 |
-
rembg>=2.0.
|
| 6 |
onnxruntime>=1.15.0
|
|
|
|
| 1 |
+
numpy>=1.24.0
|
| 2 |
+
opencv-python>=4.8.0
|
| 3 |
+
Pillow>=10.0.0
|
| 4 |
+
gradio>=3.40.1
|
| 5 |
+
rembg>=2.0.50
|
| 6 |
onnxruntime>=1.15.0
|