image_generation/
image_generation.rs

1use base64::{engine::general_purpose::STANDARD as BASE64, Engine};
2use gemini_rust::{Gemini, GenerationConfig};
3use std::env;
4use std::fs;
5
6/// Example of using Gemini API for image generation (text-to-image)
7/// This example demonstrates how to generate images using the Gemini 2.5 Flash Image Preview model
8#[tokio::main]
9async fn main() -> Result<(), Box<dyn std::error::Error>> {
10    // Get API key from environment variable
11    let api_key = env::var("GEMINI_API_KEY").expect("GEMINI_API_KEY environment variable not set");
12
13    // Create client with the image generation model
14    let client = Gemini::with_model(api_key, "models/gemini-2.5-flash-image-preview".to_string());
15
16    println!("--- Text-to-Image Generation ---");
17
18    // Example 1: Simple text-to-image generation
19    let response = client
20        .generate_content()
21        .with_user_message(
22            "Create a picture of a nano banana dish in a fancy restaurant with a Gemini theme. \
23             The scene should be photorealistic with elegant lighting and sophisticated presentation."
24        )
25        .with_generation_config(GenerationConfig {
26            temperature: Some(0.7),
27            max_output_tokens: Some(8192), // Higher token limit for image output
28            ..Default::default()
29        })
30        .execute()
31        .await?;
32
33    // Process the response - look for both text and image outputs
34    for (i, candidate) in response.candidates.iter().enumerate() {
35        if let Some(parts) = &candidate.content.parts {
36            println!("Processing candidate {}", i + 1);
37
38            for (j, part) in parts.iter().enumerate() {
39                match part {
40                    gemini_rust::Part::Text { text, .. } => {
41                        println!("Text response {}: {}", j + 1, text);
42                    }
43                    gemini_rust::Part::InlineData { inline_data } => {
44                        println!("Image response {} found!", j + 1);
45                        println!("MIME type: {}", inline_data.mime_type);
46
47                        // Decode base64 image data and save to file
48                        match BASE64.decode(&inline_data.data) {
49                            Ok(image_bytes) => {
50                                let filename = format!("generated_image_{}.png", j + 1);
51                                fs::write(&filename, image_bytes)?;
52                                println!("Image saved as: {}", filename);
53                            }
54                            Err(e) => {
55                                println!("Failed to decode image data: {}", e);
56                            }
57                        }
58                    }
59                    _ => {
60                        println!("Other part type encountered");
61                    }
62                }
63            }
64        }
65    }
66
67    println!("\n--- Advanced Image Generation Examples ---");
68
69    // Example 2: Product mockup
70    let product_response = client
71        .generate_content()
72        .with_user_message(
73            "A high-resolution, studio-lit product photograph of a minimalist ceramic coffee mug \
74             with a matte black finish on a clean white marble surface. The lighting is a \
75             three-point softbox setup to eliminate harsh shadows. The camera angle is a \
76             slightly elevated 45-degree angle to showcase the mug's elegant handle design. \
77             Ultra-realistic, with sharp focus on the mug's texture. 16:9 aspect ratio.",
78        )
79        .execute()
80        .await?;
81
82    save_generated_images(&product_response, "product_mockup")?;
83
84    // Example 3: Logo design with text
85    let logo_response = client
86        .generate_content()
87        .with_user_message(
88            "Create a modern, minimalist logo for a coffee shop called 'The Daily Grind' \
89             with clean sans-serif typography. The design should feature a stylized coffee bean \
90             icon integrated with the text. Use a warm color palette of deep brown and cream. \
91             The background must be transparent.",
92        )
93        .execute()
94        .await?;
95
96    save_generated_images(&logo_response, "coffee_logo")?;
97
98    // Example 4: Artistic illustration
99    let art_response = client
100        .generate_content()
101        .with_user_message(
102            "A kawaii-style sticker of a happy red panda programmer, featuring round eyes and \
103             a cheerful expression, sitting in front of a laptop with tiny hearts floating around. \
104             Use a bright and vibrant color palette with soft pastels. The design should have \
105             clean line art and cell-shaded style. The background must be transparent.",
106        )
107        .execute()
108        .await?;
109
110    save_generated_images(&art_response, "red_panda_sticker")?;
111
112    // Example 5: Minimalist design
113    let minimal_response = client
114        .generate_content()
115        .with_user_message(
116            "A minimalist composition featuring a single, delicate red maple leaf positioned in the \
117             bottom-right of the frame. The background is a vast, empty cream canvas, creating \
118             significant negative space. Soft, subtle lighting. 16:9 aspect ratio."
119        )
120        .execute()
121        .await?;
122
123    save_generated_images(&minimal_response, "minimalist_leaf")?;
124
125    println!("\nAll image generation examples completed!");
126    println!("Check the current directory for the generated image files.");
127
128    Ok(())
129}
130
131/// Helper function to save generated images from a response
132fn save_generated_images(
133    response: &gemini_rust::GenerationResponse,
134    prefix: &str,
135) -> Result<(), Box<dyn std::error::Error>> {
136    for candidate in response.candidates.iter() {
137        if let Some(parts) = &candidate.content.parts {
138            let mut image_count = 0;
139            let mut text_parts = Vec::new();
140
141            for part in parts.iter() {
142                match part {
143                    gemini_rust::Part::Text { text, .. } => {
144                        text_parts.push(text.clone());
145                    }
146                    gemini_rust::Part::InlineData { inline_data } => {
147                        image_count += 1;
148                        match BASE64.decode(&inline_data.data) {
149                            Ok(image_bytes) => {
150                                let filename = format!("{}_{}.png", prefix, image_count);
151                                fs::write(&filename, image_bytes)?;
152                                println!("Generated image saved as: {}", filename);
153                            }
154                            Err(e) => {
155                                println!("Failed to decode image data: {}", e);
156                            }
157                        }
158                    }
159                    _ => {}
160                }
161            }
162
163            // Print any text responses
164            if !text_parts.is_empty() {
165                println!("Text response for {}: {}", prefix, text_parts.join("\n"));
166            }
167        }
168    }
169    Ok(())
170}