Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions MultiImageClient/Enums/GoogleImageAspectRatio.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
namespace MultiImageClient
{
public enum GoogleImageAspectRatio
{
Ratio1x1,
Ratio2x3,
Ratio3x2,
Ratio3x4,
Ratio4x3,
Ratio4x5,
Ratio5x4,
Ratio9x16,
Ratio16x9,
Ratio21x9
}

public static class GoogleImageAspectRatioExtensions
{
public static string ToApiString(this GoogleImageAspectRatio ratio)
{
return ratio switch
{
GoogleImageAspectRatio.Ratio1x1 => "1:1",
GoogleImageAspectRatio.Ratio2x3 => "2:3",
GoogleImageAspectRatio.Ratio3x2 => "3:2",
GoogleImageAspectRatio.Ratio3x4 => "3:4",
GoogleImageAspectRatio.Ratio4x3 => "4:3",
GoogleImageAspectRatio.Ratio4x5 => "4:5",
GoogleImageAspectRatio.Ratio5x4 => "5:4",
GoogleImageAspectRatio.Ratio9x16 => "9:16",
GoogleImageAspectRatio.Ratio16x9 => "16:9",
GoogleImageAspectRatio.Ratio21x9 => "21:9",
_ => "1:1"
};
}
}
}
23 changes: 23 additions & 0 deletions MultiImageClient/Enums/GoogleImageSize.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace MultiImageClient
{
public enum GoogleImageSize
{
Size1K,
Size2K,
Size4K // Note: 4K only supported by Gemini, not Imagen 4
}

public static class GoogleImageSizeExtensions
{
public static string ToApiString(this GoogleImageSize size)
{
return size switch
{
GoogleImageSize.Size1K => "1K",
GoogleImageSize.Size2K => "2K",
GoogleImageSize.Size4K => "4K",
_ => "1K"
};
}
}
}
31 changes: 31 additions & 0 deletions MultiImageClient/Enums/GoogleOutputMimeType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
namespace MultiImageClient
{
public enum GoogleOutputMimeType
{
Png, // Default - lossless
Jpeg // Lossy, smaller file size, supports compression quality
}

public static class GoogleOutputMimeTypeExtensions
{
public static string ToApiString(this GoogleOutputMimeType mimeType)
{
return mimeType switch
{
GoogleOutputMimeType.Png => "image/png",
GoogleOutputMimeType.Jpeg => "image/jpeg",
_ => "image/png"
};
}

public static string ToFileExtension(this GoogleOutputMimeType mimeType)
{
return mimeType switch
{
GoogleOutputMimeType.Png => ".png",
GoogleOutputMimeType.Jpeg => ".jpg",
_ => ".png"
};
}
}
}
23 changes: 23 additions & 0 deletions MultiImageClient/Enums/GooglePersonGeneration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace MultiImageClient
{
public enum GooglePersonGeneration
{
AllowAdult, // Default - allow generation of adults only (no celebrities)
DontAllow, // Disable people/faces in generated images
AllowAll // Allow all person generation (most permissive)
}

public static class GooglePersonGenerationExtensions
{
public static string ToApiString(this GooglePersonGeneration setting)
{
return setting switch
{
GooglePersonGeneration.AllowAdult => "allow_adult",
GooglePersonGeneration.DontAllow => "dont_allow",
GooglePersonGeneration.AllowAll => "ALLOW_ALL",
_ => "allow_adult"
};
}
}
}
25 changes: 25 additions & 0 deletions MultiImageClient/Enums/GoogleSafetyFilterLevel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
namespace MultiImageClient
{
public enum GoogleSafetyFilterLevel
{
BlockLowAndAbove, // Highest safety - most filtering
BlockMediumAndAbove, // Default - balanced filtering
BlockOnlyHigh, // Lowest safety - least filtering (may increase objectionable content)
BlockNone // Disable safety filtering entirely (if supported)
}

public static class GoogleSafetyFilterLevelExtensions
{
public static string ToApiString(this GoogleSafetyFilterLevel level)
{
return level switch
{
GoogleSafetyFilterLevel.BlockLowAndAbove => "block_low_and_above",
GoogleSafetyFilterLevel.BlockMediumAndAbove => "block_medium_and_above",
GoogleSafetyFilterLevel.BlockOnlyHigh => "block_only_high",
GoogleSafetyFilterLevel.BlockNone => "BLOCK_NONE",
_ => "block_medium_and_above"
};
}
}
}
47 changes: 36 additions & 11 deletions MultiImageClient/ImageGenerators/GoogleGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
Expand All @@ -17,33 +17,52 @@ public class GoogleGenerator : IImageGenerator
private MultiClientRunStats _stats;
private string _name;
private ImageGeneratorApiType _apiType;
private GoogleImageSize _imageSize;
private GoogleImageAspectRatio _aspectRatio;

public ImageGeneratorApiType ApiType => ImageGeneratorApiType.GoogleNanoBanana;

public GoogleGenerator(ImageGeneratorApiType apiType, string apiKey, int maxConcurrency,
MultiClientRunStats stats, string name = "")
public GoogleGenerator(
ImageGeneratorApiType apiType,
string apiKey,
int maxConcurrency,
MultiClientRunStats stats,
string name = "",
GoogleImageSize imageSize = GoogleImageSize.Size1K,
GoogleImageAspectRatio aspectRatio = GoogleImageAspectRatio.Ratio1x1)
{
_apiKey = apiKey;
_googleSemaphore = new SemaphoreSlim(maxConcurrency);
_httpClient = new HttpClient();
_name = string.IsNullOrEmpty(name) ? "" : name;
_stats = stats;
_apiType = apiType;

_imageSize = imageSize;
_aspectRatio = aspectRatio;
}

public string GetFilenamePart(PromptDetails pd)
{
return $"{_apiType}";
var namePart = string.IsNullOrEmpty(_name) ? "" : $"-{_name}";
return $"{_apiType}{namePart}_{_imageSize.ToApiString()}_{_aspectRatio.ToApiString().Replace(":", "x")}";
}

public decimal GetCost()
{
// Gemini 2.5 Flash Image uses token-based pricing
// $30 per 1 million tokens for image output (1290 tokens per image up to 1024x1024px)
// Higher resolutions consume proportionally more tokens
if (_apiType == ImageGeneratorApiType.GoogleNanoBanana)
{
return (30m / 1000000m) * 1290m;
var baseTokens = 1290m;
var multiplier = _imageSize switch
{
GoogleImageSize.Size1K => 1.0m,
GoogleImageSize.Size2K => 4.0m, // 2x2 = 4x pixels
GoogleImageSize.Size4K => 16.0m, // 4x4 = 16x pixels
_ => 1.0m
};
return (30m / 1000000m) * baseTokens * multiplier;
}
else if (_apiType == ImageGeneratorApiType.GoogleImagen4)
{
Expand All @@ -57,18 +76,19 @@ public decimal GetCost()

public List<string> GetRightParts()
{
return new List<string> { _apiType.ToString() };
var namePart = string.IsNullOrEmpty(_name) ? "" : _name;
return new List<string> { _apiType.ToString(), namePart, _imageSize.ToApiString(), _aspectRatio.ToApiString() };
}

public string GetGeneratorSpecPart()
{
if (string.IsNullOrEmpty(_name))
{
return $"google-{_apiType.ToString()}";
return $"google-{_apiType.ToString()}\n{_imageSize.ToApiString()} {_aspectRatio.ToApiString()}";
}
else
{
return _name;
return $"{_name}\n{_imageSize.ToApiString()} {_aspectRatio.ToApiString()}";
}
}

Expand Down Expand Up @@ -96,7 +116,12 @@ public async Task<TaskProcessResult> ProcessPromptAsync(IImageGenerator generato
},
generationConfig = new
{
responseModalities = new[] { "TEXT", "IMAGE" }
responseModalities = new[] { "TEXT", "IMAGE" },
imageConfig = new
{
imageSize = _imageSize.ToApiString(),
aspectRatio = _aspectRatio.ToApiString()
}
}
};

Expand Down Expand Up @@ -214,4 +239,4 @@ public void Dispose()
}
}

}
}
136 changes: 136 additions & 0 deletions MultiImageClient/ImageGenerators/GoogleImageGenerationOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
namespace MultiImageClient
{
/// <summary>
/// Configuration options for Google image generation APIs (both Gemini and Imagen 4).
/// Not all options are supported by all APIs - see individual property docs.
/// </summary>
public class GoogleImageGenerationOptions
{
/// <summary>
/// Output image resolution. Gemini supports 1K/2K/4K, Imagen 4 only supports 1K/2K.
/// Default: Size1K
/// </summary>
public GoogleImageSize ImageSize { get; set; } = GoogleImageSize.Size1K;

/// <summary>
/// Aspect ratio of generated images.
/// Supported: 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9
/// Default: Ratio1x1
/// </summary>
public GoogleImageAspectRatio AspectRatio { get; set; } = GoogleImageAspectRatio.Ratio1x1;

/// <summary>
/// Controls generation of people/faces in images.
/// Imagen 4 only. Default: AllowAdult
/// </summary>
public GooglePersonGeneration PersonGeneration { get; set; } = GooglePersonGeneration.AllowAdult;

/// <summary>
/// Safety filter threshold level.
/// Imagen 4 only. Default: BlockMediumAndAbove
/// </summary>
public GoogleSafetyFilterLevel SafetyFilterLevel { get; set; } = GoogleSafetyFilterLevel.BlockMediumAndAbove;

/// <summary>
/// Output image format.
/// Imagen 4 only. Default: Png
/// </summary>
public GoogleOutputMimeType OutputMimeType { get; set; } = GoogleOutputMimeType.Png;

/// <summary>
/// JPEG compression quality (0-100). Only applies when OutputMimeType is Jpeg.
/// Imagen 4 only. Default: 75
/// </summary>
public int CompressionQuality { get; set; } = 75;

/// <summary>
/// Whether to use LLM-based prompt enhancement for higher quality images.
/// Imagen 4 only. Default: false (to preserve exact prompts)
/// </summary>
public bool EnhancePrompt { get; set; } = false;

/// <summary>
/// Whether to add a SynthID digital watermark to generated images.
/// When true, seed parameter is ignored.
/// Imagen 4 only. Default: false
/// </summary>
public bool AddWatermark { get; set; } = false;

/// <summary>
/// Random seed for deterministic output. Only works when AddWatermark=false and EnhancePrompt=false.
/// Set to null for random generation.
/// Imagen 4 only. Default: null
/// </summary>
public uint? Seed { get; set; } = null;

/// <summary>
/// Number of images to generate per request (1-4).
/// Imagen 4 only. Default: 1
/// </summary>
public int NumberOfImages { get; set; } = 1;

/// <summary>
/// Whether to include RAI (Responsible AI) filter reason in responses.
/// Imagen 4 only. Default: true
/// </summary>
public bool IncludeRaiReason { get; set; } = true;

/// <summary>
/// Creates a copy of this options object with potentially modified values.
/// </summary>
public GoogleImageGenerationOptions Clone()
{
return new GoogleImageGenerationOptions
{
ImageSize = this.ImageSize,
AspectRatio = this.AspectRatio,
PersonGeneration = this.PersonGeneration,
SafetyFilterLevel = this.SafetyFilterLevel,
OutputMimeType = this.OutputMimeType,
CompressionQuality = this.CompressionQuality,
EnhancePrompt = this.EnhancePrompt,
AddWatermark = this.AddWatermark,
Seed = this.Seed,
NumberOfImages = this.NumberOfImages,
IncludeRaiReason = this.IncludeRaiReason
};
}

/// <summary>
/// Validates options for Imagen 4 API compatibility.
/// </summary>
public void ValidateForImagen4()
{
if (ImageSize == GoogleImageSize.Size4K)
{
throw new System.ArgumentException("Imagen 4 does not support 4K resolution. Use 1K or 2K.");
}

if (NumberOfImages < 1 || NumberOfImages > 4)
{
throw new System.ArgumentException("NumberOfImages must be between 1 and 4.");
}

if (CompressionQuality < 0 || CompressionQuality > 100)
{
throw new System.ArgumentException("CompressionQuality must be between 0 and 100.");
}
}

/// <summary>
/// Gets a string representation of the key options for display/logging.
/// </summary>
public string ToDisplayString()
{
return $"{ImageSize.ToApiString()} {AspectRatio.ToApiString()}";
}

/// <summary>
/// Gets a filename-safe string representation of key options.
/// </summary>
public string ToFilenamePart()
{
return $"{ImageSize.ToApiString()}_{AspectRatio.ToApiString().Replace(":", "x")}";
}
}
}
Loading