feat: add new examples from pocketflow-academy
@@ -0,0 +1,72 @@
|
||||
# PocketFlow BatchFlow Example
|
||||
|
||||
This example demonstrates the BatchFlow concept in PocketFlow by implementing an image processor that applies different filters to multiple images.
|
||||
|
||||
## What this Example Demonstrates
|
||||
|
||||
- How to use BatchFlow to run a Flow multiple times with different parameters
|
||||
- Key concepts of BatchFlow:
|
||||
1. Creating a base Flow for single-item processing
|
||||
2. Using BatchFlow to process multiple items with different parameters
|
||||
3. Managing parameters across multiple Flow executions
|
||||
|
||||
## Project Structure
|
||||
```
|
||||
pocketflow-batch-flow/
|
||||
├── README.md
|
||||
├── requirements.txt
|
||||
├── images/
|
||||
│ ├── cat.jpg # Sample image 1
|
||||
│ ├── dog.jpg # Sample image 2
|
||||
│ └── bird.jpg # Sample image 3
|
||||
├── main.py # Entry point
|
||||
├── flow.py # Flow and BatchFlow definitions
|
||||
└── nodes.py # Node implementations for image processing
|
||||
```
|
||||
|
||||
## How it Works
|
||||
|
||||
The example processes multiple images with different filters:
|
||||
|
||||
1. **Base Flow**: Processes a single image
|
||||
- Load image
|
||||
- Apply filter (grayscale, blur, or sepia)
|
||||
- Save processed image
|
||||
|
||||
2. **BatchFlow**: Processes multiple image-filter combinations
|
||||
- Takes a list of parameters (image + filter combinations)
|
||||
- Runs the base Flow for each parameter set
|
||||
- Organizes output in a structured way
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
python main.py
|
||||
```
|
||||
|
||||
## Sample Output
|
||||
|
||||
```
|
||||
Processing images with filters...
|
||||
|
||||
Processing cat.jpg with grayscale filter...
|
||||
Processing cat.jpg with blur filter...
|
||||
Processing dog.jpg with sepia filter...
|
||||
...
|
||||
|
||||
All images processed successfully!
|
||||
Check the 'output' directory for results.
|
||||
```
|
||||
|
||||
## Key Concepts Illustrated
|
||||
|
||||
1. **Parameter Management**: Shows how BatchFlow manages different parameter sets
|
||||
2. **Flow Reuse**: Demonstrates running the same Flow multiple times
|
||||
3. **Batch Processing**: Shows how to process multiple items efficiently
|
||||
4. **Real-world Application**: Provides a practical example of batch processing
|
||||
@@ -0,0 +1,48 @@
|
||||
from pocketflow import Flow, BatchFlow
|
||||
from nodes import LoadImage, ApplyFilter, SaveImage
|
||||
|
||||
def create_base_flow():
|
||||
"""Create the base Flow for processing a single image."""
|
||||
# Create nodes
|
||||
load = LoadImage()
|
||||
filter_node = ApplyFilter()
|
||||
save = SaveImage()
|
||||
|
||||
# Connect nodes
|
||||
load - "apply_filter" >> filter_node
|
||||
filter_node - "save" >> save
|
||||
|
||||
# Create and return flow
|
||||
return Flow(start=load)
|
||||
|
||||
class ImageBatchFlow(BatchFlow):
|
||||
"""BatchFlow for processing multiple images with different filters."""
|
||||
|
||||
def prep(self, shared):
|
||||
"""Generate parameters for each image-filter combination."""
|
||||
# List of images to process
|
||||
images = ["cat.jpg", "dog.jpg", "bird.jpg"]
|
||||
|
||||
# List of filters to apply
|
||||
filters = ["grayscale", "blur", "sepia"]
|
||||
|
||||
# Generate all combinations
|
||||
params = []
|
||||
for img in images:
|
||||
for f in filters:
|
||||
params.append({
|
||||
"input": img,
|
||||
"filter": f
|
||||
})
|
||||
|
||||
return params
|
||||
|
||||
def create_flow():
|
||||
"""Create the complete batch processing flow."""
|
||||
# Create base flow for single image processing
|
||||
base_flow = create_base_flow()
|
||||
|
||||
# Wrap in BatchFlow for multiple images
|
||||
batch_flow = ImageBatchFlow(start=base_flow)
|
||||
|
||||
return batch_flow
|
||||
|
After Width: | Height: | Size: 249 KiB |
|
After Width: | Height: | Size: 214 KiB |
|
After Width: | Height: | Size: 147 KiB |
@@ -0,0 +1,17 @@
|
||||
import os
|
||||
from PIL import Image
|
||||
import numpy as np
|
||||
from flow import create_flow
|
||||
|
||||
def main():
|
||||
# Create and run flow
|
||||
print("Processing images with filters...")
|
||||
|
||||
flow = create_flow()
|
||||
flow.run({})
|
||||
|
||||
print("\nAll images processed successfully!")
|
||||
print("Check the 'output' directory for results.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,76 @@
|
||||
"""Node implementations for image processing."""
|
||||
|
||||
import os
|
||||
from PIL import Image, ImageEnhance, ImageFilter
|
||||
from pocketflow import Node
|
||||
|
||||
class LoadImage(Node):
|
||||
"""Node that loads an image file."""
|
||||
|
||||
def prep(self, shared):
|
||||
"""Get image path from parameters."""
|
||||
return os.path.join("images", self.params["input"])
|
||||
|
||||
def exec(self, image_path):
|
||||
"""Load the image using PIL."""
|
||||
return Image.open(image_path)
|
||||
|
||||
def post(self, shared, prep_res, exec_res):
|
||||
"""Store the image in shared store."""
|
||||
shared["image"] = exec_res
|
||||
return "apply_filter"
|
||||
|
||||
class ApplyFilter(Node):
|
||||
"""Node that applies a filter to an image."""
|
||||
|
||||
def prep(self, shared):
|
||||
"""Get image and filter type."""
|
||||
return shared["image"], self.params["filter"]
|
||||
|
||||
def exec(self, inputs):
|
||||
"""Apply the specified filter."""
|
||||
image, filter_type = inputs
|
||||
|
||||
if filter_type == "grayscale":
|
||||
return image.convert("L")
|
||||
elif filter_type == "blur":
|
||||
return image.filter(ImageFilter.BLUR)
|
||||
elif filter_type == "sepia":
|
||||
# Sepia implementation
|
||||
enhancer = ImageEnhance.Color(image)
|
||||
grayscale = enhancer.enhance(0.3)
|
||||
colorize = ImageEnhance.Brightness(grayscale)
|
||||
return colorize.enhance(1.2)
|
||||
else:
|
||||
raise ValueError(f"Unknown filter: {filter_type}")
|
||||
|
||||
def post(self, shared, prep_res, exec_res):
|
||||
"""Store the filtered image."""
|
||||
shared["filtered_image"] = exec_res
|
||||
return "save"
|
||||
|
||||
class SaveImage(Node):
|
||||
"""Node that saves the processed image."""
|
||||
|
||||
def prep(self, shared):
|
||||
"""Get filtered image and prepare output path."""
|
||||
# Create output directory if it doesn't exist
|
||||
os.makedirs("output", exist_ok=True)
|
||||
|
||||
# Generate output filename
|
||||
input_name = os.path.splitext(self.params["input"])[0]
|
||||
filter_name = self.params["filter"]
|
||||
output_path = os.path.join("output", f"{input_name}_{filter_name}.jpg")
|
||||
|
||||
return shared["filtered_image"], output_path
|
||||
|
||||
def exec(self, inputs):
|
||||
"""Save the image to file."""
|
||||
image, output_path = inputs
|
||||
image.save(output_path, "JPEG")
|
||||
return output_path
|
||||
|
||||
def post(self, shared, prep_res, exec_res):
|
||||
"""Print success message."""
|
||||
print(f"Saved filtered image to: {exec_res}")
|
||||
return "default"
|
||||
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 41 KiB |
@@ -0,0 +1,2 @@
|
||||
pocketflow
|
||||
Pillow>=10.0.0
|
||||