Skip to content

Commit f3738ad

Browse files
authored
Merge pull request #78 from AvidGameFan/ref_images
2 parents 8e3b569 + 23a4a15 commit f3738ad

3 files changed

Lines changed: 174 additions & 0 deletions

File tree

REF_IMAGES_IMPLEMENTATION.md

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# Reference Images Implementation Guide
2+
3+
## Current Status
4+
5+
### `init_image` Implementation (Already Complete)
6+
7+
The `init_image` feature is fully implemented across the stack:
8+
9+
#### 1. **Frontend/API Layer** - [image_generator.h](include/image_generator.h#L34)
10+
```cpp
11+
struct ImageGenerationParams {
12+
std::string init_image_base64; // Base64 encoded image
13+
float strength = 0.75f; // Strength of the init image
14+
};
15+
```
16+
17+
#### 2. **Backend Processing** - [image_generator.cpp](src/image_generator.cpp#L390-L410)
18+
- **Lines 390-394**: Decode and process init_image
19+
- Checks if img2img mode is enabled and init_image_base64 is provided
20+
- Calls `createInitImage()` to decode and resize the image
21+
22+
- **Lines 499-509**: `createInitImage()` function
23+
- Decodes base64 to image using `base64ToImage()`
24+
- Resizes image to match width/height parameters
25+
- Returns sd_image_t struct
26+
27+
- **Lines 449-451**: Cleanup
28+
- Frees the init_image memory after generation
29+
30+
#### 3. **Core Engine** - [stable-diffusion.h](stable-diffusion.cpp/stable-diffusion.h#L287-L306)
31+
```cpp
32+
typedef struct {
33+
sd_image_t init_image; // The initial image for img2img
34+
sd_image_t* ref_images; // Reference images (NOT YET EXPOSED TO FRONTEND)
35+
int ref_images_count; // Number of reference images
36+
bool auto_resize_ref_image; // Auto-resize setting
37+
// ... other fields
38+
} sd_img_gen_params_t;
39+
```
40+
41+
### `ref_images` Implementation (Backend Only)
42+
43+
The `ref_images` feature **already exists in the backend** but is NOT exposed through the ImageGenerator layer:
44+
45+
#### 1. **Core Engine Support** - [stable-diffusion.h](stable-diffusion.cpp/stable-diffusion.h#L289-L291)
46+
- `sd_image_t* ref_images` - array of reference images
47+
- `int ref_images_count` - count of images
48+
- `bool auto_resize_ref_image` - auto-resize flag
49+
50+
#### 2. **Usage in Backend** - [stable-diffusion.cpp](stable-diffusion.cpp/stable-diffusion.cpp#L3730)
51+
- Line 3730: `if (sd_img_gen_params->init_image.data != nullptr || sd_img_gen_params->ref_images_count > 0)`
52+
- Lines 3662-3664: Reference images are pushed into a vector for processing
53+
- Line 3669: Empty image is used if no ref_images for certain models
54+
- Lines 1753-1764: [conditioner.hpp](stable-diffusion.cpp/conditioner.hpp#L1753-L1764) - Vision processing of ref_images for Qwen image editing
55+
56+
---
57+
58+
## What's Missing
59+
60+
To fully implement `ref_images` support, you need to add to the **frontend layer** ([image_generator.h](include/image_generator.h) and [image_generator.cpp](src/image_generator.cpp)):
61+
62+
### Changes Needed:
63+
64+
1. **Add to ImageGenerationParams struct** ([image_generator.h](include/image_generator.h#L17))
65+
```cpp
66+
struct ImageGenerationParams {
67+
// ... existing fields ...
68+
69+
// Reference images for Qwen/vision-based models
70+
std::vector<std::string> ref_images_base64; // Base64 encoded reference images
71+
bool auto_resize_ref_image = true;
72+
};
73+
```
74+
75+
2. **Add helper function in ImageGenerator class** ([image_generator.cpp](src/image_generator.cpp))
76+
```cpp
77+
// Similar to createInitImage() - create function to process ref_images
78+
// Decode each base64 string, resize, and return vector of sd_image_t
79+
```
80+
81+
3. **Process ref_images in generateImage()** (around line 390-410)
82+
```cpp
83+
// Allocate array for reference images
84+
// Loop through params.ref_images_base64
85+
// Decode and resize each one
86+
// Set gen_params.ref_images and gen_params.ref_images_count
87+
```
88+
89+
4. **Cleanup ref_images memory** (around line 449-451)
90+
```cpp
91+
// Free each reference image after generation
92+
```
93+
94+
---
95+
96+
## File Locations Summary
97+
98+
| Component | File | Key Lines |
99+
|-----------|------|-----------|
100+
| API Definition | [include/image_generator.h](include/image_generator.h) | 17-45 (struct) |
101+
| Frontend Logic | [src/image_generator.cpp](src/image_generator.cpp) | 390-410, 499-509, 449-451 |
102+
| Backend Struct | [stable-diffusion.cpp/stable-diffusion.h](stable-diffusion.cpp/stable-diffusion.h) | 287-306 |
103+
| Backend Usage | [stable-diffusion.cpp/stable-diffusion.cpp](stable-diffusion.cpp/stable-diffusion.cpp) | 3730, 3662-3664, 3669 |
104+
| Vision Processing | [stable-diffusion.cpp/conditioner.hpp](stable-diffusion.cpp/conditioner.hpp) | 1753-1764 |

include/image_generator.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ struct ImageGenerationParams {
4040
float control_strength = 1.0f;
4141
std::string controlnet_model;
4242

43+
// Reference images for vision-based models (Qwen, etc.)
44+
std::vector<std::string> ref_images_base64;
45+
bool auto_resize_ref_image = true;
46+
4347
// Other options
4448
int clip_skip = -1;
4549
};
@@ -75,6 +79,9 @@ class ImageGenerator {
7579
// Create control image from base64 string and apply canny preprocessing
7680
sd_image_t createControlImage(const ImageGenerationParams& params);
7781

82+
// Create reference images from base64 strings (for vision-based models)
83+
std::vector<sd_image_t> createRefImages(const ImageGenerationParams& params);
84+
7885
// Free sd_image_t data
7986
void freeImage(sd_image_t& image);
8087

src/image_generator.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,15 @@ std::vector<std::string> ImageGenerator::generateInternal(const ImageGenerationP
405405
LOG_INFO("Using ControlNet with strength %.2f", params.control_strength);
406406
}
407407

408+
// Reference images for vision-based models (Qwen, etc.)
409+
std::vector<sd_image_t> ref_images_vec = createRefImages(params);
410+
if (!ref_images_vec.empty()) {
411+
gen_params.ref_images = ref_images_vec.data();
412+
gen_params.ref_images_count = static_cast<int>(ref_images_vec.size());
413+
gen_params.auto_resize_ref_image = params.auto_resize_ref_image;
414+
LOG_INFO("Using %d reference image(s)", gen_params.ref_images_count);
415+
}
416+
408417
// VAE tiling (if enabled via CLI)
409418
if (vae_tiling_) {
410419
gen_params.vae_tiling_params.enabled = true;
@@ -457,6 +466,13 @@ std::vector<std::string> ImageGenerator::generateInternal(const ImageGenerationP
457466
freeImage(control_image);
458467
}
459468

469+
// Free reference images if used
470+
for (auto& ref_image : ref_images_vec) {
471+
if (ref_image.data) {
472+
freeImage(ref_image);
473+
}
474+
}
475+
460476
if (!result) {
461477
LOG_ERROR("Image generation failed");
462478

@@ -575,6 +591,53 @@ sd_image_t ImageGenerator::createControlImage(const ImageGenerationParams& param
575591
return control_image;
576592
}
577593

594+
std::vector<sd_image_t> ImageGenerator::createRefImages(const ImageGenerationParams& params) {
595+
std::vector<sd_image_t> ref_images;
596+
597+
if (params.ref_images_base64.empty()) {
598+
return ref_images; // Return empty vector if no ref images
599+
}
600+
601+
LOG_INFO("Processing %zu reference image(s)", params.ref_images_base64.size());
602+
603+
for (size_t i = 0; i < params.ref_images_base64.size(); i++) {
604+
if (params.ref_images_base64[i].empty()) {
605+
LOG_WARNING("Skipping empty reference image at index %zu", i);
606+
continue;
607+
}
608+
609+
// Decode reference image from base64
610+
sd_image_t ref_image = base64ToImage(params.ref_images_base64[i]);
611+
if (!ref_image.data) {
612+
LOG_ERROR("Failed to decode reference image at index %zu", i);
613+
// Free previously decoded images before throwing
614+
for (auto& img : ref_images) {
615+
freeImage(img);
616+
}
617+
throw std::runtime_error("Failed to decode reference image at index " + std::to_string(i));
618+
}
619+
620+
// Resize reference image if auto_resize_ref_image is enabled
621+
if (params.auto_resize_ref_image) {
622+
if (!resizeImage(ref_image, params.width, params.height, true)) {
623+
LOG_ERROR("Failed to resize reference image at index %zu", i);
624+
freeImage(ref_image);
625+
// Free previously decoded images
626+
for (auto& img : ref_images) {
627+
freeImage(img);
628+
}
629+
throw std::runtime_error("Failed to resize reference image at index " + std::to_string(i));
630+
}
631+
}
632+
633+
LOG_DEBUG("Loaded reference image %zu: %ux%u channels", i, ref_image.width, ref_image.height, ref_image.channel);
634+
ref_images.push_back(ref_image);
635+
}
636+
637+
LOG_INFO("Successfully loaded %zu reference image(s)", ref_images.size());
638+
return ref_images;
639+
}
640+
578641
void ImageGenerator::freeImage(sd_image_t& image) {
579642
if (image.data) {
580643
free(image.data); // Works for both stbi allocated and manually allocated memory

0 commit comments

Comments
 (0)