diff --git a/image-processing/README.md b/image-processing/README.md index 08d3d072..34693c95 100644 --- a/image-processing/README.md +++ b/image-processing/README.md @@ -1,50 +1,47 @@ -# 🖼️ Image Processing Tool - -This tool is designed to apply various image filters to images using image processing libraries. It can process images from URLs and apply different types of filters including blur, grayscale, and unsharp mask. - -## 🔍 What the Script Does - -1. Fetches an image from a specified URL -2. Applies one of the following filters: - - Gaussian Blur - - Grayscale conversion - - Unsharp Mask -3. Saves the processed image to the output directory - -## 📊 Available Filters - -| Filter | Description | -| ----------- | :--------------------------------------------: | -| `blur` | Applies Gaussian blur with radius 5 | -| `grayscale` | Converts the image to grayscale | -| `unsharp` | Applies unsharp mask filter with radius 5 | - -## 📁 Input - -- Image URL (currently set to a sample Lena image) -- Filter type to apply - -## 📁 Output - -- Processed image saved as `filtered_image.png` in the `/data/outputs` directory - -## How to run your algorithm on Ocean Node - -```bash -1. Open Ocean Protocol vscode-extension -2. Select Algorithm file -3. Select Results folder -4. Press Start Compute Job -``` - -# Python -### Use existing docker image `oceanprotocol/c2d_examples:py-general`: -- Docker image: `oceanprotocol/c2d_examples` -- Docker tag: `py-general` - -# Node -### Use existing docker image `oceanprotocol/c2d_examples:js-general`: -- Docker image: `oceanprotocol/c2d_examples` -- Docker tag: `js-general` +# 🖼️ Image Filters Grid Generator +This project demonstrates how to apply multiple image filters to a single image and visualize them in a neatly arranged grid. The code uses Python's Pillow (PIL) library to apply 20+ filters, save the filtered images, and generate a final combined grid image. + +# ✨ Features +### ✅ Downloads an image from a URL +### ✅ Applies 20+ image filters & transformations (blur, sharpen, emboss, invert, etc.) +### ✅ Saves each filtered image individually +### ✅ Creates a grid collage of all filtered images with labeled names +### ✅ Simple, reusable code structure +### ✅ Works even without external fonts (fallback to default) + +# 📊 Filters Applied +- GaussianBlur +- Grayscale +- UnsharpMask +- BoxBlur +- Contour +- Detail +- EdgeEnhance +- EdgeEnhanceMore +- Emboss +- FindEdges +- Sharpen +- Smooth +- SmoothMore +- MedianFilter +- MinFilter +- MaxFilter +- ModeFilter +- Posterize +- Invert +- Solarize + + + +# 🧠 Why this Project? +**This script can be used for:** + +### Learning & experimenting with image filters + +### Generating visual comparison grids for presentations + +### Building blocks for more complex image processing tasks + +### Simple portfolio/demo projects diff --git a/image-processing/python/image-processing.py b/image-processing/python/image-processing.py index 1e52b3d0..4ca42de1 100644 --- a/image-processing/python/image-processing.py +++ b/image-processing/python/image-processing.py @@ -1,14 +1,16 @@ import requests from io import BytesIO -from PIL import Image, ImageFilter +from PIL import Image, ImageFilter, ImageDraw, ImageFont, ImageOps +import os image_url = "https://raw.githubusercontent.com/mikolalysenko/lena/master/lena.png" +def generate_multiple_images(image_url): -def apply_filters(image_url, filter): - if not filter: - print("Filter is not provided.") + if not image_url: + print("Image URL is not provided.") return + response = requests.get(image_url) if response.status_code == 200: @@ -16,25 +18,77 @@ def apply_filters(image_url, filter): else: print(f"Failed to fetch image: {response.status_code}") print(response.text[:500]) - filtered_img = None - # Apply filter - if filter == "blur": - blurred_img = img.filter(ImageFilter.GaussianBlur(radius=5)) - filtered_img = blurred_img - elif filter == "grayscale": - grayscale_img = img.convert("L") - filtered_img = grayscale_img - elif filter == "unsharp": - unsharp_img = img.filter(ImageFilter.UnsharpMask(radius=5)) - filtered_img = unsharp_img - else: - print("Unknown filter.") - return + return # exit if failed to fetch image + + os.makedirs("/data/outputs", exist_ok=True) + + # List of filters & operations + filters = [ + ("GaussianBlur", img.filter(ImageFilter.GaussianBlur(radius=5))), + ("Grayscale", img.convert("L")), + ("UnsharpMask", img.filter(ImageFilter.UnsharpMask(radius=5))), + ("BoxBlur", img.filter(ImageFilter.BoxBlur(radius=5))), + ("Contour", img.filter(ImageFilter.CONTOUR)), + ("Detail", img.filter(ImageFilter.DETAIL)), + ("EdgeEnhance", img.filter(ImageFilter.EDGE_ENHANCE)), + ("EdgeEnhanceMore", img.filter(ImageFilter.EDGE_ENHANCE_MORE)), + ("Emboss", img.filter(ImageFilter.EMBOSS)), + ("FindEdges", img.filter(ImageFilter.FIND_EDGES)), + ("Sharpen", img.filter(ImageFilter.SHARPEN)), + ("Smooth", img.filter(ImageFilter.SMOOTH)), + ("SmoothMore", img.filter(ImageFilter.SMOOTH_MORE)), + ("MedianFilter", img.filter(ImageFilter.MedianFilter(size=5))), + ("MinFilter", img.filter(ImageFilter.MinFilter(size=5))), + ("MaxFilter", img.filter(ImageFilter.MaxFilter(size=5))), + ("ModeFilter", img.filter(ImageFilter.ModeFilter(size=5))), + ("Posterize", ImageOps.posterize(img, bits=4)), + ("Invert", ImageOps.invert(img.convert("RGB"))), + ("Solarize", ImageOps.solarize(img, threshold=128)) + ] + + # Save individual images + for name, image in filters: + image.save(f"/data/outputs/{name.lower()}_img.png") + + # --- Now create the final grid image --- + + cols = 4 + padding = 20 + label_height = 30 + + # Reload saved images to ensure grayscale modes are handled uniformly + images = [Image.open(f"/data/outputs/{name.lower()}_img.png").convert("RGB") for name, _ in filters] + + img_width, img_height = images[0].size + rows = (len(images) + cols - 1) // cols + grid_width = cols * (img_width + padding) + padding + grid_height = rows * (img_height + label_height + padding) + padding + + grid_img = Image.new("RGB", (grid_width, grid_height), color="white") + + # Font handling + try: + font = ImageFont.truetype("arial.ttf", 20) + except: + font = ImageFont.load_default() + + draw = ImageDraw.Draw(grid_img) + + for idx, (filter_name, _) in enumerate(filters): + x = padding + (idx % cols) * (img_width + padding) + y = padding + (idx // cols) * (img_height + label_height + padding) + + grid_img.paste(images[idx], (x, y)) + + bbox = draw.textbbox((0, 0), filter_name, font=font) + text_width = bbox[2] - bbox[0] + text_x = x + (img_width - text_width) // 2 + text_y = y + img_height + draw.text((text_x, text_y), filter_name, fill="black", font=font) + - return filtered_img + grid_img.save("/data/outputs/filters_grid.png") + print("Grid image saved at: /data/outputs/filters_grid.png") if __name__ == "__main__": - filtered_img = apply_filters(image_url=image_url, filter="unsharp") - filename = "/data/outputs/filtered_image.png" - filtered_img.save(filename) - print(f"Filters applied and images saved successfully as {filename}") \ No newline at end of file + generate_multiple_images(image_url=image_url) diff --git a/results/result-logs-2025-05-16T19-33-44-317Z.txt b/results/result-logs-2025-05-16T19-33-44-317Z.txt new file mode 100644 index 00000000..eef2b92e --- /dev/null +++ b/results/result-logs-2025-05-16T19-33-44-317Z.txt @@ -0,0 +1 @@ +Grid image saved at: /data/outputs/filters_grid.png diff --git a/results/result-output_2025-05-16T19-33-47-550Z.tar b/results/result-output_2025-05-16T19-33-47-550Z.tar new file mode 100644 index 00000000..bb47c513 Binary files /dev/null and b/results/result-output_2025-05-16T19-33-47-550Z.tar differ diff --git a/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/boxblur_img.png b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/boxblur_img.png new file mode 100644 index 00000000..34781f3c Binary files /dev/null and b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/boxblur_img.png differ diff --git a/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/contour_img.png b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/contour_img.png new file mode 100644 index 00000000..83f2c17b Binary files /dev/null and b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/contour_img.png differ diff --git a/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/detail_img.png b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/detail_img.png new file mode 100644 index 00000000..3740b2d4 Binary files /dev/null and b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/detail_img.png differ diff --git a/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/edgeenhance_img.png b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/edgeenhance_img.png new file mode 100644 index 00000000..c3e2fd16 Binary files /dev/null and b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/edgeenhance_img.png differ diff --git a/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/edgeenhancemore_img.png b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/edgeenhancemore_img.png new file mode 100644 index 00000000..dd83e932 Binary files /dev/null and b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/edgeenhancemore_img.png differ diff --git a/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/emboss_img.png b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/emboss_img.png new file mode 100644 index 00000000..259f8f13 Binary files /dev/null and b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/emboss_img.png differ diff --git a/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/filters_grid.png b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/filters_grid.png new file mode 100644 index 00000000..17e983ff Binary files /dev/null and b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/filters_grid.png differ diff --git a/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/findedges_img.png b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/findedges_img.png new file mode 100644 index 00000000..1bd803e1 Binary files /dev/null and b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/findedges_img.png differ diff --git a/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/gaussianblur_img.png b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/gaussianblur_img.png new file mode 100644 index 00000000..04679cf9 Binary files /dev/null and b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/gaussianblur_img.png differ diff --git a/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/grayscale_img.png b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/grayscale_img.png new file mode 100644 index 00000000..cd4adc3f Binary files /dev/null and b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/grayscale_img.png differ diff --git a/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/invert_img.png b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/invert_img.png new file mode 100644 index 00000000..ea643662 Binary files /dev/null and b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/invert_img.png differ diff --git a/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/maxfilter_img.png b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/maxfilter_img.png new file mode 100644 index 00000000..04bfd3b8 Binary files /dev/null and b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/maxfilter_img.png differ diff --git a/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/medianfilter_img.png b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/medianfilter_img.png new file mode 100644 index 00000000..61f31a69 Binary files /dev/null and b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/medianfilter_img.png differ diff --git a/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/minfilter_img.png b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/minfilter_img.png new file mode 100644 index 00000000..d39f14b9 Binary files /dev/null and b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/minfilter_img.png differ diff --git a/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/modefilter_img.png b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/modefilter_img.png new file mode 100644 index 00000000..7b6b64eb Binary files /dev/null and b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/modefilter_img.png differ diff --git a/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/posterize_img.png b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/posterize_img.png new file mode 100644 index 00000000..b9945b0e Binary files /dev/null and b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/posterize_img.png differ diff --git a/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/sharpen_img.png b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/sharpen_img.png new file mode 100644 index 00000000..0a32e4e3 Binary files /dev/null and b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/sharpen_img.png differ diff --git a/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/smooth_img.png b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/smooth_img.png new file mode 100644 index 00000000..7e7710ca Binary files /dev/null and b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/smooth_img.png differ diff --git a/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/smoothmore_img.png b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/smoothmore_img.png new file mode 100644 index 00000000..e22e0a91 Binary files /dev/null and b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/smoothmore_img.png differ diff --git a/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/solarize_img.png b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/solarize_img.png new file mode 100644 index 00000000..486626be Binary files /dev/null and b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/solarize_img.png differ diff --git a/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/unsharpmask_img.png b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/unsharpmask_img.png new file mode 100644 index 00000000..3bc79614 Binary files /dev/null and b/results/result-output_2025-05-16T19-33-47-550Z_extracted/outputs/unsharpmask_img.png differ