A visual image processing workflow application built with Qt6 and OpenCV. It provides both a graphical user interface and command-line interface for building custom image processing pipelines using connected processing nodes.
- Features
- Architecture
- Running Modes
- Node Types
- Workflow JSON Format
- Building the Project
- Usage
- Examples
- 🎨 Visual Workflow Editor: Drag-and-drop nodes and connect them to build processing pipelines
- 🔧 Multiple Image Processing Operations: Crop, resize, rotate, flip, color adjustment, and more
- 🎯 Channel Processing: Split and merge color channels with individual channel adjustments
- ✂️ Smart Cutout: Automatic orange color detection and background removal
- 📊 Image Assessment: Evaluate roundness and color saturation of orange regions
- 🤖 VLM Integration: Call Vision-Language Models via OpenRouter API
- 💻 Command-Line Support: Execute saved workflows directly via CLI
- 💾 Workflow Persistence: Save and load workflows in JSON format
The project follows a modular architecture with clear separation of concerns:
image_workflow/
├── include/ # Header files
│ ├── MainWindow.h # Main application window
│ ├── GraphScene.h # QGraphicsScene for workflow canvas
│ ├── NodeItem.h # Base class for processing nodes
│ ├── ConnectionItem.h # Connection lines between nodes
│ ├── ImageProcessor.h # Image processing logic
│ ├── WorkflowEngine.h # CLI workflow execution engine
│ ├── NetworkManager.h # HTTP requests for VLM nodes
│ ├── ImageUtils.h # Utility functions
│ └── json.hpp # JSON parsing library (nlohmann)
├── src/ # Source files
│ ├── main.cpp # Entry point with CLI/GUI mode detection
│ ├── MainWindow.cpp # GUI implementation
│ ├── GraphScene.cpp # Scene interaction logic
│ ├── NodeItem.cpp # Node rendering and behavior
│ ├── ConnectionItem.cpp # Connection rendering
│ ├── ImageProcessor.cpp # Processing algorithms
│ ├── WorkflowEngine.cpp # CLI execution engine
│ ├── NetworkManager.cpp # Network communication
│ └── ImageUtils.cpp # Image conversion utilities
├── image_workflow.h/cpp # Legacy QMainWindow wrapper
├── image_workflow.ui # Qt Designer UI file
├── image_workflow.qrc # Qt resource file
└── Help.md # Chinese help documentation
-
MainWindow 🖥️: The main GUI window containing the menu bar, image display area, workflow canvas, and log panel.
-
GraphScene 🎨: A custom QGraphicsScene that manages nodes and connections, handling mouse events for creating connections.
-
NodeItem 📦: Represents a processing node in the workflow canvas. Each node has input/output ports and adjustable parameters.
-
ConnectionItem 🔗: Visual representation of connections between nodes, showing the flow of image data.
-
ImageProcessor ⚙️: Contains all image processing algorithms for different node types (crop, resize, color adjustment, cutout, etc.).
-
WorkflowEngine 🚀: Executes workflows in CLI mode using topological sorting to process nodes in the correct order.
-
NetworkManager 🌐: Handles HTTP requests to external APIs (OpenRouter) for VLM nodes.
Input Image → [Node1] → [Node2] → ... → [NodeN] → Output Image
↓ ↓ ↓
Parameters Parameters Parameters
Nodes are connected in a directed acyclic graph (DAG). The workflow engine uses topological sorting to execute nodes in the correct dependency order.
The application automatically detects which mode to use based on command-line arguments.
Launch without any arguments:
image_workflow.exeThis opens the graphical interface where you can interactively build and execute workflows.
Provide the required arguments to execute a workflow directly:
image_workflow.exe --workflow <workflow.json> --input <input.jpg> --output <output.png>| Argument | Short | Required | Description |
|---|---|---|---|
--workflow |
-workflow |
Yes | Path to workflow JSON file |
--input |
-input |
Yes | Input image path |
--output |
-output |
Yes | Output image save path |
⚠️ VLM nodes are skipped in CLI mode (requires network interaction)- 🔇 OpenCV log level is set to WARNING to reduce console output
⚠️ If the workflow contains a cutout node and the output format doesn't support Alpha channel (e.g., JPG), a warning is displayed
Crops the image to a specified rectangular region.
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
| X | Integer | 0 | Crop start X coordinate |
| Y | Integer | 0 | Crop start Y coordinate |
| Width | Integer | 100 | Crop width |
| Height | Integer | 100 | Crop height |
Scales the image by a factor.
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
| ScaleFactor | Float | 1.0 | Scale factor (0.5 = half size, 2.0 = double size) |
Rotates the image by multiples of 90 degrees.
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
| Angle | Integer | 90 | Rotation angle (90, 180, or 270) |
Flips the image horizontally or vertically.
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
| Direction | String | "Horizontal" | Flip direction ("Horizontal" or "Vertical") |
Adjusts image saturation, brightness, and contrast.
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
| Saturation | Float | 1.0 | Saturation multiplier (1.0 = original) |
| Brightness | Float | 0.0 | Brightness adjustment (can be positive or negative) |
| Contrast | Float | 1.0 | Contrast multiplier (1.0 = original) |
A composite unit that automatically creates a channel processing pipeline:
channel_split → [color] → [color] → [color] → channel_merge
↓ ↓ ↓
B ch G ch R ch
- channel_split: Splits BGR image into 3 single-channel images
- 3× color nodes: Adjust each channel individually (B, G, R)
- channel_merge: Merges processed channels back into a BGR image
Use this when you need fine-grained control over individual color channels.
Detects orange regions and makes everything else transparent.
Algorithm:
- 🎨 Convert image to HSV color space
- 🎯 Apply color threshold (H: 0-25, S: 40-255, V: 40-255) to detect orange
- 🔧 Apply morphological operations (close) to refine the mask
- 🔍 Find the largest orange contour
- 🖼️ Convert BGR to BGRA (add Alpha channel)
- 🎭 Set Alpha to 0 (transparent) for non-orange regions
Output: 4-channel image (BGRA) with transparent background
Important:
Applies an artistic stylization effect to the image.
Parameters: None (uses default: sigma_s=60, sigma_r=0.45)
Works with both 3-channel and 4-channel images.
Evaluates the quality of orange regions in the image.
Metrics:
- Roundness 🔵:
4 × π × area / perimeter²(0-1, where 1 = perfect circle) - Color 🎨: Average saturation in HSV space (0-1)
- Total Score 📈:
Roundness × 0.6 + Color × 0.4, expressed as percentage
Output: No image output (passes input through). Results are saved to assess_result.txt:
Roundness: 0.85, Color: 0.72, Total: 79.80
Calls a Vision-Language Model API to analyze images and get text descriptions.
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
| ApiKey | String | "" | OpenRouter API key |
| Prompt | Text | "Describe this image" | Prompt sent to the VLM |
| Model | String | "sourceful/riverflow-v2-fast" | VLM model name |
Supported Models (via OpenRouter):
qwen/qwen3-vl-8b-instruct(default)- Other OpenRouter-supported vision models
Output: No image output (passes input through). Response is saved to vlm_response.txt.
Requirements:
- ✅ Valid OpenRouter API key
- 🌐 Network connection
- 🔒 OpenSSL support (for HTTPS)
Note:
Workflows are saved as JSON files with nodes and connections arrays.
| Field | Type | Required | Description |
|---|---|---|---|
| id | Integer | Yes | Unique node identifier |
| type | String | Yes | Node type (see Node Types section) |
| x | Float | No | Node X position on canvas |
| y | Float | No | Node Y position on canvas |
| params | Object | No | Node parameters (varies by type) |
| Field | Type | Required | Description |
|---|---|---|---|
| from_id | Integer | Yes | Source node ID (output) |
| to_id | Integer | Yes | Target node ID (input) |
crop- ✂️ Crop noderesize- 🔍 Resize noderotate- 🔄 Rotate nodeflip-↔️ Flip nodecolor- 🎨 Color adjustment nodechannel_split- 🎯 Channel split nodechannel_merge- 🎯 Channel merge nodecutout- ✂️ Orange cutout nodestylization- 🎨 Stylization nodeassess- 📊 Assessment nodevlm- 🤖 Vision-Language Model node
The workflow engine uses topological sorting to execute nodes in dependency order:
- 🏗️ Build a node graph based on connections
- 🔢 Calculate in-degrees (number of inputs) for each node
- 🚀 Start from nodes with in-degree 0 (starting nodes)
▶️ Process sequentially:- Execute current node, get output image
- Pass output to all connected downstream nodes
- When all inputs for a node are ready, add it to the execution queue
- 🏁 Final output: The last node's output (or nodes without output connections)
- Visual Studio 2022 or later
- Qt 6.x development libraries
- OpenCV 4.12+
- OpenSSL (for VLM functionality)
- 📂 Import Image: File → Import (supports PNG, JPG, JPEG, BMP, TIFF)
- ➕ Add Nodes: Node menu → Select node type
- 🔗 Connect Nodes:
- Click source node (output port) - node highlights
- Click target node (input port) - connection created
- Click empty area to cancel connection mode
- ⚙️ Set Parameters: Right-click node → Set parameters in dialog → OK
▶️ Run Workflow: Run → Run All- 💾 Export Result: File → Export
- Each node can have only one input connection (from a previous node)
- Each node can have multiple output connections (to multiple subsequent nodes)
- Nodes without input connections are starting nodes
image_workflow.exe --workflow workflow.json --input input.jpg --output output.pngThe CLI mode will:
- 📂 Load the workflow JSON
- 🖼️ Read the input image
- ⚙️ Execute all nodes in topological order
- 💾 Save the result to the output path
- 📝 Print success/error messages to console
First rotate 90 degrees, then crop a region.
JSON:
{
"nodes": [
{
"id": 1,
"type": "rotate",
"x": -100,
"y": 0,
"params": {
"Angle": 90
}
},
{
"id": 2,
"type": "crop",
"x": 100,
"y": 0,
"params": {
"X": 50,
"Y": 50,
"Width": 200,
"Height": 200
}
}
],
"connections": [
{
"from_id": 1,
"to_id": 2
}
]
}Detect and cut out orange regions from an image.
JSON:
{
"nodes": [
{
"id": 1,
"type": "cutout",
"x": 0,
"y": 0,
"params": {}
}
],
"connections": []
}Command:
image_workflow.exe --workflow cutout.json --input orange.jpg --output result.pngAdjust saturation, brightness, and contrast.
JSON:
{
"nodes": [
{
"id": 1,
"type": "color",
"x": 0,
"y": 0,
"params": {
"Saturation": 1.5,
"Brightness": 20,
"Contrast": 1.2
}
}
]
}