From ebcf6a1955d8a7b83751580463264c0d4345007a Mon Sep 17 00:00:00 2001 From: Kevin Gordillo Date: Mon, 24 Aug 2026 21:52:39 +0000 Subject: [PATCH 1/7] feat(genai): migrate ImageGenWithText to gemini-2.5-flash-image - Update ImageGenWithText to use client.models.generateContent with gemini-2.5-flash-image - Update ImageGenerationIT with configurable bucket, safe cleanup, and testImageGenWithText validation - Temporarily ignore remaining deprecated Imagen capability tests pending migration --- .../imagegeneration/ImageGenWithText.java | 51 ++++++++++--------- .../imagegeneration/ImageGenerationIT.java | 38 +++++++++----- 2 files changed, 51 insertions(+), 38 deletions(-) diff --git a/genai/snippets/src/main/java/genai/imagegeneration/ImageGenWithText.java b/genai/snippets/src/main/java/genai/imagegeneration/ImageGenWithText.java index 67ee38b56e1..31266ab76ac 100644 --- a/genai/snippets/src/main/java/genai/imagegeneration/ImageGenWithText.java +++ b/genai/snippets/src/main/java/genai/imagegeneration/ImageGenWithText.java @@ -19,10 +19,13 @@ // [START googlegenaisdk_imggen_with_txt] import com.google.genai.Client; -import com.google.genai.types.GenerateImagesConfig; -import com.google.genai.types.GenerateImagesResponse; -import com.google.genai.types.GeneratedImage; +import com.google.genai.types.Blob; +import com.google.genai.types.Candidate; +import com.google.genai.types.Content; +import com.google.genai.types.GenerateContentConfig; +import com.google.genai.types.GenerateContentResponse; import com.google.genai.types.Image; +import com.google.genai.types.Part; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.File; @@ -33,7 +36,7 @@ public class ImageGenWithText { public static void main(String[] args) throws IOException { // TODO(developer): Replace these variables before running the sample. - String modelId = "imagen-4.0-generate-001"; + String modelId = "gemini-2.5-flash-image"; String outputFile = "resources/output/dog_newspaper.png"; generateImage(modelId, outputFile); } @@ -43,32 +46,32 @@ public static Image generateImage(String modelId, String outputFile) throws IOEx // Client Initialization. Once created, it can be reused for multiple requests. try (Client client = Client.builder().location("global").vertexAI(true).build()) { - GenerateImagesResponse response = - client.models.generateImages( - modelId, - "A dog reading a newspaper", - GenerateImagesConfig.builder().imageSize("2K").outputMimeType("image/png").build()); + GenerateContentConfig config = + GenerateContentConfig.builder().responseModalities("TEXT", "IMAGE").build(); - Image generatedImage = + GenerateContentResponse response = + client.models.generateContent(modelId, "A dog reading a newspaper", config); + + byte[] imageBytes = response - .generatedImages() - .flatMap(generatedImages -> generatedImages.stream().findFirst()) - .flatMap(GeneratedImage::image) + .candidates() + .flatMap(candidates -> candidates.stream().findFirst()) + .flatMap(Candidate::content) + .flatMap(Content::parts) + .flatMap( + parts -> + parts.stream() + .filter(part -> part.inlineData().flatMap(Blob::data).isPresent()) + .findFirst()) + .flatMap(part -> part.inlineData().flatMap(Blob::data)) .orElseThrow(() -> new IllegalStateException("No image was generated by the model.")); - // Read image data and write it to the output file - if (generatedImage.imageBytes().isPresent()) { - BufferedImage image = - ImageIO.read(new ByteArrayInputStream(generatedImage.imageBytes().get())); - ImageIO.write(image, "png", new File(outputFile)); + BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageBytes)); + ImageIO.write(image, "png", new File(outputFile)); - System.out.printf( - "Created output image using %s bytes\n", generatedImage.imageBytes().get().length); - } + System.out.printf("Created output image using %s bytes\n", imageBytes.length); - // Example response: - // Created output image using 1633112 bytes - return generatedImage; + return Image.builder().imageBytes(imageBytes).build(); } } } diff --git a/genai/snippets/src/test/java/genai/imagegeneration/ImageGenerationIT.java b/genai/snippets/src/test/java/genai/imagegeneration/ImageGenerationIT.java index a5e6d43ebc2..43501f02c35 100644 --- a/genai/snippets/src/test/java/genai/imagegeneration/ImageGenerationIT.java +++ b/genai/snippets/src/test/java/genai/imagegeneration/ImageGenerationIT.java @@ -33,6 +33,7 @@ import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -40,11 +41,11 @@ @RunWith(JUnit4.class) public class ImageGenerationIT { - private static final String IMAGEN_3_MODEL = "imagen-3.0-capability-001"; - private static final String BUCKET_NAME = "java-docs-samples-testing"; + private static final String BUCKET_NAME = + System.getenv().getOrDefault("BUCKET_NAME", "java-docs-samples-testing"); private static final String PREFIX = "genai-img-generation-" + UUID.randomUUID(); private static final String OUTPUT_GCS_URI = String.format("gs://%s/%s", BUCKET_NAME, PREFIX); - private static final String IMAGEN_4_MODEL = "imagen-4.0-generate-001"; + private static final String IMAGEN_MODEL = "gemini-2.5-flash-image"; private static final String VIRTUAL_TRY_ON_MODEL = "virtual-try-on-001"; private ByteArrayOutputStream bout; @@ -64,11 +65,15 @@ public static void checkRequirements() { @AfterClass public static void cleanup() { - Storage storage = StorageOptions.getDefaultInstance().getService(); - Page blobs = storage.list(BUCKET_NAME, Storage.BlobListOption.prefix(PREFIX)); - - for (Blob blob : blobs.iterateAll()) { - storage.delete(blob.getBlobId()); + try { + Storage storage = StorageOptions.getDefaultInstance().getService(); + Page blobs = storage.list(BUCKET_NAME, Storage.BlobListOption.prefix(PREFIX)); + + for (Blob blob : blobs.iterateAll()) { + storage.delete(blob.getBlobId()); + } + } catch (Exception e) { + System.err.println("Failed to cleanup bucket " + BUCKET_NAME + ": " + e.getMessage()); } } @@ -85,46 +90,51 @@ public void tearDown() { } @Test + @Ignore("Deprecated Imagen model; migrated to Gemini in follow-up PR") public void testImageGenCannyCtrlTypeWithTextAndImage() { Optional response = ImageGenCannyCtrlTypeWithTextAndImage.cannyEdgeCustomization( - IMAGEN_3_MODEL, OUTPUT_GCS_URI); + IMAGEN_MODEL, OUTPUT_GCS_URI); assertThat(response).isPresent(); assertThat(response.get()).isNotEmpty(); } @Test + @Ignore("Deprecated Imagen model; migrated to Gemini in follow-up PR") public void testImageGenRawReferenceWithTextAndImage() { Optional response = ImageGenRawReferenceWithTextAndImage.styleTransferCustomization( - IMAGEN_3_MODEL, OUTPUT_GCS_URI); + IMAGEN_MODEL, OUTPUT_GCS_URI); assertThat(response).isPresent(); assertThat(response.get()).isNotEmpty(); } @Test + @Ignore("Deprecated Imagen model; migrated to Gemini in follow-up PR") public void testImageGenScribbleCtrlTypeWithTextAndImage() { Optional response = ImageGenScribbleCtrlTypeWithTextAndImage.scribbleCustomization( - IMAGEN_3_MODEL, OUTPUT_GCS_URI); + IMAGEN_MODEL, OUTPUT_GCS_URI); assertThat(response).isPresent(); assertThat(response.get()).isNotEmpty(); } @Test + @Ignore("Deprecated Imagen model; migrated to Gemini in follow-up PR") public void testImageGenStyleReferenceWithTextAndImage() { Optional response = ImageGenStyleReferenceWithTextAndImage.styleCustomization( - IMAGEN_3_MODEL, OUTPUT_GCS_URI); + IMAGEN_MODEL, OUTPUT_GCS_URI); assertThat(response).isPresent(); assertThat(response.get()).isNotEmpty(); } @Test + @Ignore("Deprecated Imagen model; migrated to Gemini in follow-up PR") public void testImageGenSubjectReferenceWithTextAndImage() { Optional response = ImageGenSubjectReferenceWithTextAndImage.subjectCustomization( - IMAGEN_3_MODEL, OUTPUT_GCS_URI); + IMAGEN_MODEL, OUTPUT_GCS_URI); assertThat(response).isPresent(); assertThat(response.get()).isNotEmpty(); } @@ -143,7 +153,7 @@ public void testImageGenVirtualTryOnWithTextAndImage() throws IOException { @Test public void testImageGenWithText() throws IOException { Image image = - ImageGenWithText.generateImage(IMAGEN_4_MODEL, "resources/output/dog_newspaper.png"); + ImageGenWithText.generateImage(IMAGEN_MODEL, "resources/output/dog_newspaper.png"); assertThat(image).isNotNull(); assertThat(image.imageBytes()).isPresent(); From a9e79057220b06c705e80252ef6d8fc0ed326390 Mon Sep 17 00:00:00 2001 From: Kevin Gordillo Date: Mon, 24 Aug 2026 21:53:01 +0000 Subject: [PATCH 2/7] feat(genai): migrate multimodal image generation samples to gemini-2.5-flash-image - Update Canny, RawReference, Scribble, Style, and Subject reference samples to use client.models.generateContent with multimodal Part inputs - Upload generated image bytes directly to destination GCS bucket URI - Enable and un-ignore all tests in ImageGenerationIT --- ...ImageGenCannyCtrlTypeWithTextAndImage.java | 89 +++++++------- .../ImageGenRawReferenceWithTextAndImage.java | 88 ++++++++------ ...geGenScribbleCtrlTypeWithTextAndImage.java | 89 +++++++------- ...mageGenStyleReferenceWithTextAndImage.java | 87 +++++++------- ...geGenSubjectReferenceWithTextAndImage.java | 112 ++++++++---------- .../imagegeneration/ImageGenerationIT.java | 6 - 6 files changed, 242 insertions(+), 229 deletions(-) diff --git a/genai/snippets/src/main/java/genai/imagegeneration/ImageGenCannyCtrlTypeWithTextAndImage.java b/genai/snippets/src/main/java/genai/imagegeneration/ImageGenCannyCtrlTypeWithTextAndImage.java index b42619eee68..b213721e566 100644 --- a/genai/snippets/src/main/java/genai/imagegeneration/ImageGenCannyCtrlTypeWithTextAndImage.java +++ b/genai/snippets/src/main/java/genai/imagegeneration/ImageGenCannyCtrlTypeWithTextAndImage.java @@ -18,21 +18,24 @@ // [START googlegenaisdk_imggen_canny_ctrl_type_with_txt_img] +import com.google.cloud.storage.BlobId; +import com.google.cloud.storage.BlobInfo; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.StorageOptions; import com.google.genai.Client; -import com.google.genai.types.ControlReferenceConfig; -import com.google.genai.types.ControlReferenceImage; -import com.google.genai.types.EditImageConfig; -import com.google.genai.types.EditImageResponse; -import com.google.genai.types.GeneratedImage; -import com.google.genai.types.Image; -import java.util.List; +import com.google.genai.types.Blob; +import com.google.genai.types.Candidate; +import com.google.genai.types.Content; +import com.google.genai.types.GenerateContentConfig; +import com.google.genai.types.GenerateContentResponse; +import com.google.genai.types.Part; import java.util.Optional; public class ImageGenCannyCtrlTypeWithTextAndImage { public static void main(String[] args) { // TODO(developer): Replace these variables before running the sample. - String modelId = "imagen-3.0-capability-001"; + String modelId = "gemini-2.5-flash-image"; String outputGcsUri = "gs://your-bucket/your-prefix"; cannyEdgeCustomization(modelId, outputGcsUri); } @@ -41,44 +44,48 @@ public static void main(String[] args) { public static Optional cannyEdgeCustomization(String modelId, String outputGcsUri) { // Client Initialization. Once created, it can be reused for multiple requests. try (Client client = Client.builder().location("global").vertexAI(true).build()) { - // Create a reference image out of an existing canny edge image signal - // using https://storage.googleapis.com/cloud-samples-data/generative-ai/image/car_canny.png - ControlReferenceImage controlReferenceImage = - ControlReferenceImage.builder() - .referenceId(1) - .referenceImage( - Image.builder() - .gcsUri("gs://cloud-samples-data/generative-ai/image/car_canny.png") - .build()) - .config(ControlReferenceConfig.builder().controlType("CONTROL_TYPE_CANNY").build()) - .build(); - // The `[1]` in the prompt refers to the `referenceId` assigned to - // the control reference image. - EditImageResponse imageResponse = - client.models.editImage( + GenerateContentConfig config = + GenerateContentConfig.builder().responseModalities("TEXT", "IMAGE").build(); + + GenerateContentResponse response = + client.models.generateContent( modelId, - "a watercolor painting of a red car[1] driving on a road", - List.of(controlReferenceImage), - EditImageConfig.builder() - .editMode("EDIT_MODE_CONTROLLED_EDITING") - .numberOfImages(1) - .safetyFilterLevel("BLOCK_MEDIUM_AND_ABOVE") - .personGeneration("ALLOW_ADULT") - .outputGcsUri(outputGcsUri) - .build()); + Content.fromParts( + Part.fromUri( + "gs://cloud-samples-data/generative-ai/image/car_canny.png", "image/png"), + Part.fromText("a watercolor painting of a red car driving on a road")), + config); - Image generatedImage = - imageResponse - .generatedImages() - .flatMap(generatedImages -> generatedImages.stream().findFirst()) - .flatMap(GeneratedImage::image) + byte[] imageBytes = + response + .candidates() + .flatMap(candidates -> candidates.stream().findFirst()) + .flatMap(Candidate::content) + .flatMap(Content::parts) + .flatMap( + parts -> + parts.stream() + .filter(part -> part.inlineData().flatMap(Blob::data).isPresent()) + .findFirst()) + .flatMap(part -> part.inlineData().flatMap(Blob::data)) .orElseThrow(() -> new IllegalStateException("No image was generated by the model.")); - generatedImage.gcsUri().ifPresent(System.out::println); - // Example response: - // gs://your-bucket/your-prefix - return generatedImage.gcsUri(); + Storage storage = StorageOptions.getDefaultInstance().getService(); + String gcsPath = outputGcsUri.replace("gs://", ""); + int slashIdx = gcsPath.indexOf('/'); + String bucket = slashIdx > 0 ? gcsPath.substring(0, slashIdx) : gcsPath; + String objectName = + slashIdx > 0 + ? gcsPath.substring(slashIdx + 1) + "/canny_customization.png" + : "canny_customization.png"; + BlobId blobId = BlobId.of(bucket, objectName); + BlobInfo blobInfo = BlobInfo.newBuilder(blobId).setContentType("image/png").build(); + storage.create(blobInfo, imageBytes); + + String fullGcsUri = String.format("gs://%s/%s", bucket, objectName); + System.out.println("Generated image URI: " + fullGcsUri); + return Optional.of(fullGcsUri); } } } diff --git a/genai/snippets/src/main/java/genai/imagegeneration/ImageGenRawReferenceWithTextAndImage.java b/genai/snippets/src/main/java/genai/imagegeneration/ImageGenRawReferenceWithTextAndImage.java index 51ab77002a7..703abe98edd 100644 --- a/genai/snippets/src/main/java/genai/imagegeneration/ImageGenRawReferenceWithTextAndImage.java +++ b/genai/snippets/src/main/java/genai/imagegeneration/ImageGenRawReferenceWithTextAndImage.java @@ -18,20 +18,24 @@ // [START googlegenaisdk_imggen_raw_reference_with_txt_img] +import com.google.cloud.storage.BlobId; +import com.google.cloud.storage.BlobInfo; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.StorageOptions; import com.google.genai.Client; -import com.google.genai.types.EditImageConfig; -import com.google.genai.types.EditImageResponse; -import com.google.genai.types.GeneratedImage; -import com.google.genai.types.Image; -import com.google.genai.types.RawReferenceImage; -import java.util.List; +import com.google.genai.types.Blob; +import com.google.genai.types.Candidate; +import com.google.genai.types.Content; +import com.google.genai.types.GenerateContentConfig; +import com.google.genai.types.GenerateContentResponse; +import com.google.genai.types.Part; import java.util.Optional; public class ImageGenRawReferenceWithTextAndImage { public static void main(String[] args) { // TODO(developer): Replace these variables before running the sample. - String modelId = "imagen-3.0-capability-001"; + String modelId = "gemini-2.5-flash-image"; String outputGcsUri = "gs://your-bucket/your-prefix"; styleTransferCustomization(modelId, outputGcsUri); } @@ -41,43 +45,49 @@ public static void main(String[] args) { public static Optional styleTransferCustomization(String modelId, String outputGcsUri) { // Client Initialization. Once created, it can be reused for multiple requests. try (Client client = Client.builder().location("global").vertexAI(true).build()) { - // Create a raw reference image of teacup stored in Google Cloud Storage - // using https://storage.googleapis.com/cloud-samples-data/generative-ai/image/teacup-1.png - RawReferenceImage rawReferenceImage = - RawReferenceImage.builder() - .referenceId(1) - .referenceImage( - Image.builder() - .gcsUri("gs://cloud-samples-data/generative-ai/image/teacup-1.png") - .build()) - .build(); - // The `[1]` in the prompt refers to the `referenceId` assigned to the raw reference image. - EditImageResponse imageResponse = - client.models.editImage( + GenerateContentConfig config = + GenerateContentConfig.builder().responseModalities("TEXT", "IMAGE").build(); + + GenerateContentResponse response = + client.models.generateContent( modelId, - "transform the subject in the image so that " - + "the teacup[1] is made entirely out of chocolate", - List.of(rawReferenceImage), - EditImageConfig.builder() - .editMode("EDIT_MODE_DEFAULT") - .numberOfImages(1) - .safetyFilterLevel("BLOCK_MEDIUM_AND_ABOVE") - .personGeneration("ALLOW_ADULT") - .outputGcsUri(outputGcsUri) - .build()); + Content.fromParts( + Part.fromUri( + "gs://cloud-samples-data/generative-ai/image/teacup-1.png", "image/png"), + Part.fromText( + "transform the subject in the image so that the teacup is made entirely out of chocolate")), + config); - Image generatedImage = - imageResponse - .generatedImages() - .flatMap(generatedImages -> generatedImages.stream().findFirst()) - .flatMap(GeneratedImage::image) + byte[] imageBytes = + response + .candidates() + .flatMap(candidates -> candidates.stream().findFirst()) + .flatMap(Candidate::content) + .flatMap(Content::parts) + .flatMap( + parts -> + parts.stream() + .filter(part -> part.inlineData().flatMap(Blob::data).isPresent()) + .findFirst()) + .flatMap(part -> part.inlineData().flatMap(Blob::data)) .orElseThrow(() -> new IllegalStateException("No image was generated by the model.")); - generatedImage.gcsUri().ifPresent(System.out::println); - // Example response: - // gs://your-bucket/your-prefix - return generatedImage.gcsUri(); + Storage storage = StorageOptions.getDefaultInstance().getService(); + String gcsPath = outputGcsUri.replace("gs://", ""); + int slashIdx = gcsPath.indexOf('/'); + String bucket = slashIdx > 0 ? gcsPath.substring(0, slashIdx) : gcsPath; + String objectName = + slashIdx > 0 + ? gcsPath.substring(slashIdx + 1) + "/raw_reference.png" + : "raw_reference.png"; + BlobId blobId = BlobId.of(bucket, objectName); + BlobInfo blobInfo = BlobInfo.newBuilder(blobId).setContentType("image/png").build(); + storage.create(blobInfo, imageBytes); + + String fullGcsUri = String.format("gs://%s/%s", bucket, objectName); + System.out.println("Generated image URI: " + fullGcsUri); + return Optional.of(fullGcsUri); } } } diff --git a/genai/snippets/src/main/java/genai/imagegeneration/ImageGenScribbleCtrlTypeWithTextAndImage.java b/genai/snippets/src/main/java/genai/imagegeneration/ImageGenScribbleCtrlTypeWithTextAndImage.java index 1cfb351e7a8..559b4be07e1 100644 --- a/genai/snippets/src/main/java/genai/imagegeneration/ImageGenScribbleCtrlTypeWithTextAndImage.java +++ b/genai/snippets/src/main/java/genai/imagegeneration/ImageGenScribbleCtrlTypeWithTextAndImage.java @@ -18,21 +18,24 @@ // [START googlegenaisdk_imggen_scribble_ctrl_type_with_txt_img] +import com.google.cloud.storage.BlobId; +import com.google.cloud.storage.BlobInfo; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.StorageOptions; import com.google.genai.Client; -import com.google.genai.types.ControlReferenceConfig; -import com.google.genai.types.ControlReferenceImage; -import com.google.genai.types.EditImageConfig; -import com.google.genai.types.EditImageResponse; -import com.google.genai.types.GeneratedImage; -import com.google.genai.types.Image; -import java.util.List; +import com.google.genai.types.Blob; +import com.google.genai.types.Candidate; +import com.google.genai.types.Content; +import com.google.genai.types.GenerateContentConfig; +import com.google.genai.types.GenerateContentResponse; +import com.google.genai.types.Part; import java.util.Optional; public class ImageGenScribbleCtrlTypeWithTextAndImage { public static void main(String[] args) { // TODO(developer): Replace these variables before running the sample. - String modelId = "imagen-3.0-capability-001"; + String modelId = "gemini-2.5-flash-image"; String outputGcsUri = "gs://your-bucket/your-prefix"; scribbleCustomization(modelId, outputGcsUri); } @@ -41,46 +44,48 @@ public static void main(String[] args) { public static Optional scribbleCustomization(String modelId, String outputGcsUri) { // Client Initialization. Once created, it can be reused for multiple requests. try (Client client = Client.builder().location("global").vertexAI(true).build()) { - // Create a reference image out of an existing scribble image signal - // using - // https://storage.googleapis.com/cloud-samples-data/generative-ai/image/car_scribble.png - ControlReferenceImage controlReferenceImage = - ControlReferenceImage.builder() - .referenceId(1) - .referenceImage( - Image.builder() - .gcsUri("gs://cloud-samples-data/generative-ai/image/car_scribble.png") - .build()) - .config(ControlReferenceConfig.builder().controlType("CONTROL_TYPE_SCRIBBLE").build()) - .build(); - // The `[1]` in the prompt refers to the `referenceId` assigned to the - // control reference image. - EditImageResponse imageResponse = - client.models.editImage( + GenerateContentConfig config = + GenerateContentConfig.builder().responseModalities("TEXT", "IMAGE").build(); + + GenerateContentResponse response = + client.models.generateContent( modelId, - "an oil painting showing the side of a red car[1]", - List.of(controlReferenceImage), - EditImageConfig.builder() - .editMode("EDIT_MODE_CONTROLLED_EDITING") - .numberOfImages(1) - .safetyFilterLevel("BLOCK_MEDIUM_AND_ABOVE") - .personGeneration("ALLOW_ADULT") - .outputGcsUri(outputGcsUri) - .build()); + Content.fromParts( + Part.fromUri( + "gs://cloud-samples-data/generative-ai/image/car_scribble.png", "image/png"), + Part.fromText("an oil painting showing the side of a red car")), + config); - Image generatedImage = - imageResponse - .generatedImages() - .flatMap(generatedImages -> generatedImages.stream().findFirst()) - .flatMap(GeneratedImage::image) + byte[] imageBytes = + response + .candidates() + .flatMap(candidates -> candidates.stream().findFirst()) + .flatMap(Candidate::content) + .flatMap(Content::parts) + .flatMap( + parts -> + parts.stream() + .filter(part -> part.inlineData().flatMap(Blob::data).isPresent()) + .findFirst()) + .flatMap(part -> part.inlineData().flatMap(Blob::data)) .orElseThrow(() -> new IllegalStateException("No image was generated by the model.")); - generatedImage.gcsUri().ifPresent(System.out::println); - // Example response: - // gs://your-bucket/your-prefix + Storage storage = StorageOptions.getDefaultInstance().getService(); + String gcsPath = outputGcsUri.replace("gs://", ""); + int slashIdx = gcsPath.indexOf('/'); + String bucket = slashIdx > 0 ? gcsPath.substring(0, slashIdx) : gcsPath; + String objectName = + slashIdx > 0 + ? gcsPath.substring(slashIdx + 1) + "/scribble_customization.png" + : "scribble_customization.png"; + BlobId blobId = BlobId.of(bucket, objectName); + BlobInfo blobInfo = BlobInfo.newBuilder(blobId).setContentType("image/png").build(); + storage.create(blobInfo, imageBytes); - return generatedImage.gcsUri(); + String fullGcsUri = String.format("gs://%s/%s", bucket, objectName); + System.out.println("Generated image URI: " + fullGcsUri); + return Optional.of(fullGcsUri); } } } diff --git a/genai/snippets/src/main/java/genai/imagegeneration/ImageGenStyleReferenceWithTextAndImage.java b/genai/snippets/src/main/java/genai/imagegeneration/ImageGenStyleReferenceWithTextAndImage.java index ee49522f6ec..4e4e3bcff93 100644 --- a/genai/snippets/src/main/java/genai/imagegeneration/ImageGenStyleReferenceWithTextAndImage.java +++ b/genai/snippets/src/main/java/genai/imagegeneration/ImageGenStyleReferenceWithTextAndImage.java @@ -18,21 +18,24 @@ // [START googlegenaisdk_imggen_style_reference_with_txt_img] +import com.google.cloud.storage.BlobId; +import com.google.cloud.storage.BlobInfo; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.StorageOptions; import com.google.genai.Client; -import com.google.genai.types.EditImageConfig; -import com.google.genai.types.EditImageResponse; -import com.google.genai.types.GeneratedImage; -import com.google.genai.types.Image; -import com.google.genai.types.StyleReferenceConfig; -import com.google.genai.types.StyleReferenceImage; -import java.util.List; +import com.google.genai.types.Blob; +import com.google.genai.types.Candidate; +import com.google.genai.types.Content; +import com.google.genai.types.GenerateContentConfig; +import com.google.genai.types.GenerateContentResponse; +import com.google.genai.types.Part; import java.util.Optional; public class ImageGenStyleReferenceWithTextAndImage { public static void main(String[] args) { // TODO(developer): Replace these variables before running the sample. - String modelId = "imagen-3.0-capability-001"; + String modelId = "gemini-2.5-flash-image"; String outputGcsUri = "gs://your-bucket/your-prefix"; styleCustomization(modelId, outputGcsUri); } @@ -41,44 +44,48 @@ public static void main(String[] args) { public static Optional styleCustomization(String modelId, String outputGcsUri) { // Client Initialization. Once created, it can be reused for multiple requests. try (Client client = Client.builder().location("global").vertexAI(true).build()) { - // Create a style reference image of a neon sign stored in Google Cloud Storage - // using https://storage.googleapis.com/cloud-samples-data/generative-ai/image/neon.png - StyleReferenceImage styleReferenceImage = - StyleReferenceImage.builder() - .referenceId(1) - .referenceImage( - Image.builder() - .gcsUri("gs://cloud-samples-data/generative-ai/image/neon.png") - .build()) - .config(StyleReferenceConfig.builder().styleDescription("neon sign").build()) - .build(); - // The `[1]` in the prompt refers to the `referenceId` assigned to the style reference image. - EditImageResponse imageResponse = - client.models.editImage( + GenerateContentConfig config = + GenerateContentConfig.builder().responseModalities("TEXT", "IMAGE").build(); + + GenerateContentResponse response = + client.models.generateContent( modelId, - "generate an image of a neon sign [1] with the words: have a great day", - List.of(styleReferenceImage), - EditImageConfig.builder() - .editMode("EDIT_MODE_DEFAULT") - .numberOfImages(1) - .safetyFilterLevel("BLOCK_MEDIUM_AND_ABOVE") - .personGeneration("ALLOW_ADULT") - .outputGcsUri(outputGcsUri) - .build()); + Content.fromParts( + Part.fromUri("gs://cloud-samples-data/generative-ai/image/neon.png", "image/png"), + Part.fromText( + "generate an image of a neon sign with the words: have a great day")), + config); - Image generatedImage = - imageResponse - .generatedImages() - .flatMap(generatedImages -> generatedImages.stream().findFirst()) - .flatMap(GeneratedImage::image) + byte[] imageBytes = + response + .candidates() + .flatMap(candidates -> candidates.stream().findFirst()) + .flatMap(Candidate::content) + .flatMap(Content::parts) + .flatMap( + parts -> + parts.stream() + .filter(part -> part.inlineData().flatMap(Blob::data).isPresent()) + .findFirst()) + .flatMap(part -> part.inlineData().flatMap(Blob::data)) .orElseThrow(() -> new IllegalStateException("No image was generated by the model.")); - generatedImage.gcsUri().ifPresent(System.out::println); - // Example response: - // gs://your-bucket/your-prefix + Storage storage = StorageOptions.getDefaultInstance().getService(); + String gcsPath = outputGcsUri.replace("gs://", ""); + int slashIdx = gcsPath.indexOf('/'); + String bucket = slashIdx > 0 ? gcsPath.substring(0, slashIdx) : gcsPath; + String objectName = + slashIdx > 0 + ? gcsPath.substring(slashIdx + 1) + "/style_customization.png" + : "style_customization.png"; + BlobId blobId = BlobId.of(bucket, objectName); + BlobInfo blobInfo = BlobInfo.newBuilder(blobId).setContentType("image/png").build(); + storage.create(blobInfo, imageBytes); - return generatedImage.gcsUri(); + String fullGcsUri = String.format("gs://%s/%s", bucket, objectName); + System.out.println("Generated image URI: " + fullGcsUri); + return Optional.of(fullGcsUri); } } } diff --git a/genai/snippets/src/main/java/genai/imagegeneration/ImageGenSubjectReferenceWithTextAndImage.java b/genai/snippets/src/main/java/genai/imagegeneration/ImageGenSubjectReferenceWithTextAndImage.java index 58b10eb5a94..b2170dfbe2b 100644 --- a/genai/snippets/src/main/java/genai/imagegeneration/ImageGenSubjectReferenceWithTextAndImage.java +++ b/genai/snippets/src/main/java/genai/imagegeneration/ImageGenSubjectReferenceWithTextAndImage.java @@ -18,89 +18,79 @@ // [START googlegenaisdk_imggen_subj_refer_ctrl_refer_with_txt_imgs] +import com.google.cloud.storage.BlobId; +import com.google.cloud.storage.BlobInfo; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.StorageOptions; import com.google.genai.Client; -import com.google.genai.types.ControlReferenceConfig; -import com.google.genai.types.ControlReferenceImage; -import com.google.genai.types.EditImageConfig; -import com.google.genai.types.EditImageResponse; -import com.google.genai.types.GeneratedImage; -import com.google.genai.types.Image; -import com.google.genai.types.SubjectReferenceConfig; -import com.google.genai.types.SubjectReferenceImage; -import java.util.List; +import com.google.genai.types.Blob; +import com.google.genai.types.Candidate; +import com.google.genai.types.Content; +import com.google.genai.types.GenerateContentConfig; +import com.google.genai.types.GenerateContentResponse; +import com.google.genai.types.Part; import java.util.Optional; public class ImageGenSubjectReferenceWithTextAndImage { public static void main(String[] args) { // TODO(developer): Replace these variables before running the sample. - String modelId = "imagen-3.0-capability-001"; + String modelId = "gemini-2.5-flash-image"; String outputGcsUri = "gs://your-bucket/your-prefix"; subjectCustomization(modelId, outputGcsUri); } // Generates an image using subject customization by adapting a subject reference image - // with a control reference image and a text prompt. + // with a text prompt. public static Optional subjectCustomization(String modelId, String outputGcsUri) { // Client Initialization. Once created, it can be reused for multiple requests. try (Client client = Client.builder().location("global").vertexAI(true).build()) { - // Create subject and control reference images of a photograph stored in Google Cloud Storage - // using https://storage.googleapis.com/cloud-samples-data/generative-ai/image/person.png - SubjectReferenceImage subjectReferenceImage = - SubjectReferenceImage.builder() - .referenceId(1) - .referenceImage( - Image.builder() - .gcsUri("gs://cloud-samples-data/generative-ai/image/person.png") - .build()) - .config( - SubjectReferenceConfig.builder() - .subjectDescription("a headshot of a woman") - .subjectType("SUBJECT_TYPE_PERSON") - .build()) - .build(); - ControlReferenceImage controlReferenceImage = - ControlReferenceImage.builder() - .referenceId(2) - .referenceImage( - Image.builder() - .gcsUri("gs://cloud-samples-data/generative-ai/image/person.png") - .build()) - .config( - ControlReferenceConfig.builder().controlType("CONTROL_TYPE_FACE_MESH").build()) - .build(); + GenerateContentConfig config = + GenerateContentConfig.builder().responseModalities("TEXT", "IMAGE").build(); - // The `[1]` and `[2]` in the prompt refer to the `referenceId` assigned to - // the subject reference and control reference images. - EditImageResponse imageResponse = - client.models.editImage( + GenerateContentResponse response = + client.models.generateContent( modelId, - "a portrait of a woman[1] in the pose of the control image[2]in a watercolor style by" - + " a professional artist, light and low-contrast stokes, bright pastel colors," - + " a warm atmosphere, clean background, grainy paper, bold visible brushstrokes," - + " patchy details", - List.of(subjectReferenceImage, controlReferenceImage), - EditImageConfig.builder() - .editMode("EDIT_MODE_DEFAULT") - .numberOfImages(1) - .safetyFilterLevel("BLOCK_MEDIUM_AND_ABOVE") - .personGeneration("ALLOW_ADULT") - .outputGcsUri(outputGcsUri) - .build()); + Content.fromParts( + Part.fromUri( + "gs://cloud-samples-data/generative-ai/image/person.png", "image/png"), + Part.fromText( + "a portrait of the woman in a watercolor style by a professional artist," + + " light and low-contrast strokes, bright pastel colors, a warm" + + " atmosphere, clean background, grainy paper, bold visible" + + " brushstrokes, patchy details")), + config); - Image generatedImage = - imageResponse - .generatedImages() - .flatMap(generatedImages -> generatedImages.stream().findFirst()) - .flatMap(GeneratedImage::image) + byte[] imageBytes = + response + .candidates() + .flatMap(candidates -> candidates.stream().findFirst()) + .flatMap(Candidate::content) + .flatMap(Content::parts) + .flatMap( + parts -> + parts.stream() + .filter(part -> part.inlineData().flatMap(Blob::data).isPresent()) + .findFirst()) + .flatMap(part -> part.inlineData().flatMap(Blob::data)) .orElseThrow(() -> new IllegalStateException("No image was generated by the model.")); - generatedImage.gcsUri().ifPresent(System.out::println); - // Example response: - // gs://your-bucket/your-prefix + Storage storage = StorageOptions.getDefaultInstance().getService(); + String gcsPath = outputGcsUri.replace("gs://", ""); + int slashIdx = gcsPath.indexOf('/'); + String bucket = slashIdx > 0 ? gcsPath.substring(0, slashIdx) : gcsPath; + String objectName = + slashIdx > 0 + ? gcsPath.substring(slashIdx + 1) + "/subject_customization.png" + : "subject_customization.png"; + BlobId blobId = BlobId.of(bucket, objectName); + BlobInfo blobInfo = BlobInfo.newBuilder(blobId).setContentType("image/png").build(); + storage.create(blobInfo, imageBytes); - return generatedImage.gcsUri(); + String fullGcsUri = String.format("gs://%s/%s", bucket, objectName); + System.out.println("Generated image URI: " + fullGcsUri); + return Optional.of(fullGcsUri); } } } diff --git a/genai/snippets/src/test/java/genai/imagegeneration/ImageGenerationIT.java b/genai/snippets/src/test/java/genai/imagegeneration/ImageGenerationIT.java index 43501f02c35..de59c9b1aa6 100644 --- a/genai/snippets/src/test/java/genai/imagegeneration/ImageGenerationIT.java +++ b/genai/snippets/src/test/java/genai/imagegeneration/ImageGenerationIT.java @@ -33,7 +33,6 @@ import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -90,7 +89,6 @@ public void tearDown() { } @Test - @Ignore("Deprecated Imagen model; migrated to Gemini in follow-up PR") public void testImageGenCannyCtrlTypeWithTextAndImage() { Optional response = ImageGenCannyCtrlTypeWithTextAndImage.cannyEdgeCustomization( @@ -100,7 +98,6 @@ public void testImageGenCannyCtrlTypeWithTextAndImage() { } @Test - @Ignore("Deprecated Imagen model; migrated to Gemini in follow-up PR") public void testImageGenRawReferenceWithTextAndImage() { Optional response = ImageGenRawReferenceWithTextAndImage.styleTransferCustomization( @@ -110,7 +107,6 @@ public void testImageGenRawReferenceWithTextAndImage() { } @Test - @Ignore("Deprecated Imagen model; migrated to Gemini in follow-up PR") public void testImageGenScribbleCtrlTypeWithTextAndImage() { Optional response = ImageGenScribbleCtrlTypeWithTextAndImage.scribbleCustomization( @@ -120,7 +116,6 @@ public void testImageGenScribbleCtrlTypeWithTextAndImage() { } @Test - @Ignore("Deprecated Imagen model; migrated to Gemini in follow-up PR") public void testImageGenStyleReferenceWithTextAndImage() { Optional response = ImageGenStyleReferenceWithTextAndImage.styleCustomization( @@ -130,7 +125,6 @@ public void testImageGenStyleReferenceWithTextAndImage() { } @Test - @Ignore("Deprecated Imagen model; migrated to Gemini in follow-up PR") public void testImageGenSubjectReferenceWithTextAndImage() { Optional response = ImageGenSubjectReferenceWithTextAndImage.subjectCustomization( From 5e2a9a918f4c49328ce6f8cee36b4c8c31c72143 Mon Sep 17 00:00:00 2001 From: Kevin Gordillo Date: Mon, 24 Aug 2026 16:05:04 -0600 Subject: [PATCH 3/7] Update genai/snippets/src/main/java/genai/imagegeneration/ImageGenWithText.java Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../src/main/java/genai/imagegeneration/ImageGenWithText.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/genai/snippets/src/main/java/genai/imagegeneration/ImageGenWithText.java b/genai/snippets/src/main/java/genai/imagegeneration/ImageGenWithText.java index 31266ab76ac..507eed252e0 100644 --- a/genai/snippets/src/main/java/genai/imagegeneration/ImageGenWithText.java +++ b/genai/snippets/src/main/java/genai/imagegeneration/ImageGenWithText.java @@ -66,8 +66,7 @@ public static Image generateImage(String modelId, String outputFile) throws IOEx .flatMap(part -> part.inlineData().flatMap(Blob::data)) .orElseThrow(() -> new IllegalStateException("No image was generated by the model.")); - BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageBytes)); - ImageIO.write(image, "png", new File(outputFile)); + java.nio.file.Files.write(java.nio.file.Paths.get(outputFile), imageBytes); System.out.printf("Created output image using %s bytes\n", imageBytes.length); From 9b99ea8df5320286d7530abbe30d587075852645 Mon Sep 17 00:00:00 2001 From: Kevin Gordillo Date: Mon, 24 Aug 2026 22:10:32 +0000 Subject: [PATCH 4/7] fix(genai): wrap long line in ImageGenRawReferenceWithTextAndImage to fix checkstyle --- .../imagegeneration/ImageGenRawReferenceWithTextAndImage.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/genai/snippets/src/main/java/genai/imagegeneration/ImageGenRawReferenceWithTextAndImage.java b/genai/snippets/src/main/java/genai/imagegeneration/ImageGenRawReferenceWithTextAndImage.java index 703abe98edd..b20247a1d2a 100644 --- a/genai/snippets/src/main/java/genai/imagegeneration/ImageGenRawReferenceWithTextAndImage.java +++ b/genai/snippets/src/main/java/genai/imagegeneration/ImageGenRawReferenceWithTextAndImage.java @@ -56,7 +56,8 @@ public static Optional styleTransferCustomization(String modelId, String Part.fromUri( "gs://cloud-samples-data/generative-ai/image/teacup-1.png", "image/png"), Part.fromText( - "transform the subject in the image so that the teacup is made entirely out of chocolate")), + "transform the subject in the image so that " + + "the teacup is made entirely out of chocolate")), config); byte[] imageBytes = From 3d7aba9cbaaddd52ef73f418420e79f6027a656d Mon Sep 17 00:00:00 2001 From: Kevin Gordillo Date: Mon, 24 Aug 2026 23:09:00 +0000 Subject: [PATCH 5/7] fix(genai): prevent IndexOutOfBounds in BoundingBoxWithTxtImg and add retry in ImageGenerationMmFlashIT --- .../main/java/genai/boundingbox/BoundingBoxWithTxtImg.java | 5 ++++- .../java/genai/imagegeneration/ImageGenerationMmFlashIT.java | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/genai/snippets/src/main/java/genai/boundingbox/BoundingBoxWithTxtImg.java b/genai/snippets/src/main/java/genai/boundingbox/BoundingBoxWithTxtImg.java index 8988c7c8d01..88428106a44 100644 --- a/genai/snippets/src/main/java/genai/boundingbox/BoundingBoxWithTxtImg.java +++ b/genai/snippets/src/main/java/genai/boundingbox/BoundingBoxWithTxtImg.java @@ -90,8 +90,11 @@ public static void plotBoundingBoxes(String imageUrl, List bounding Color.ORANGE); for (int i = 0; i < boundingBoxes.size(); i++) { - BoundingBox boundingBox = boundingBoxes.get((i)); + BoundingBox boundingBox = boundingBoxes.get(i); List box2d = boundingBox.getBox2d(); + if (box2d == null || box2d.size() < 4) { + continue; + } // Scale normalized coordinates (0-1000) to image dimensions. int topY = (int) (box2d.get(0) / 1000.0 * height); diff --git a/genai/snippets/src/test/java/genai/imagegeneration/ImageGenerationMmFlashIT.java b/genai/snippets/src/test/java/genai/imagegeneration/ImageGenerationMmFlashIT.java index e9c4d011d00..85b260bcce6 100644 --- a/genai/snippets/src/test/java/genai/imagegeneration/ImageGenerationMmFlashIT.java +++ b/genai/snippets/src/test/java/genai/imagegeneration/ImageGenerationMmFlashIT.java @@ -79,6 +79,9 @@ public void testImageGenMmFlashLocaleAwareWithText() throws IOException { @Test public void testImageGenMmFlashMultipleImagesWithText() throws IOException { List images = ImageGenMmFlashMultipleImagesWithText.generateContent(GEMINI_FLASH_IMAGE); + if (images.isEmpty()) { + images = ImageGenMmFlashMultipleImagesWithText.generateContent(GEMINI_FLASH_IMAGE); + } assertThat(images).isNotEmpty(); } From c4aeb219e9904c2c2cbb8970262df07b3bd6d144 Mon Sep 17 00:00:00 2001 From: Kevin Gordillo Date: Wed, 26 Aug 2026 09:28:00 -0600 Subject: [PATCH 6/7] Prevent IndexOutOfBoundsException in BoundingBoxWithTxtImg Added a check to prevent IndexOutOfBoundsException when accessing box2d. --- .../src/main/java/genai/boundingbox/BoundingBoxWithTxtImg.java | 1 + 1 file changed, 1 insertion(+) diff --git a/genai/snippets/src/main/java/genai/boundingbox/BoundingBoxWithTxtImg.java b/genai/snippets/src/main/java/genai/boundingbox/BoundingBoxWithTxtImg.java index 88428106a44..e7b13c11507 100644 --- a/genai/snippets/src/main/java/genai/boundingbox/BoundingBoxWithTxtImg.java +++ b/genai/snippets/src/main/java/genai/boundingbox/BoundingBoxWithTxtImg.java @@ -92,6 +92,7 @@ public static void plotBoundingBoxes(String imageUrl, List bounding for (int i = 0; i < boundingBoxes.size(); i++) { BoundingBox boundingBox = boundingBoxes.get(i); List box2d = boundingBox.getBox2d(); + // Prevents IndexOutOfBoundsException if (box2d == null || box2d.size() < 4) { continue; } From 35bd9cddc154ef76d0a59affb1d64ecd6f20ef35 Mon Sep 17 00:00:00 2001 From: Kevin Gordillo Date: Thu, 27 Aug 2026 18:04:36 +0000 Subject: [PATCH 7/7] Trigger GitHub Actions