diff --git a/generative_ai/image_generation/imagen3_editing/edit_image_3_inpainting_insert_mask.py b/generative_ai/image_generation/imagen3_editing/edit_image_3_inpainting_insert_mask.py new file mode 100644 index 000000000000..fd2a51ddd8b6 --- /dev/null +++ b/generative_ai/image_generation/imagen3_editing/edit_image_3_inpainting_insert_mask.py @@ -0,0 +1,80 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Google Cloud Vertex AI sample for editing an image using a mask file with Imagen 3. + Inpainting can insert the object designated by the prompt into the masked + area. +""" +import os + +from vertexai.preview import vision_models + +PROJECT_ID = os.getenv("GOOGLE_CLOUD_PROJECT") + + +def edit_image_3_inpainting_insert_mask( + input_file: str, + mask_file: str, + output_file: str, + prompt: str, +) -> vision_models.ImageGenerationResponse: + # [START generativeaionvertexai_imagen_3_edit_image_inpainting_insert_mask] + + import vertexai + from vertexai.preview.vision_models import Image, ImageGenerationModel, MaskReferenceImage, RawReferenceImage + + # TODO(developer): Update and un-comment below lines + # PROJECT_ID = "your-project-id" + # input_file = "input-image.png" + # mask_file = "mask-image.png" + # output_file = "output-image.png" + # prompt = "" # The text prompt describing what you want to see inserted. + + vertexai.init(project=PROJECT_ID, location="us-central1") + + model = ImageGenerationModel.from_pretrained("imagen-3.0-capability-001") + base_img = Image.load_from_file(location=input_file) + mask_img = Image.load_from_file(location=mask_file) + base_ref_image = RawReferenceImage(image=base_img, reference_id=0) + mask_ref_image = MaskReferenceImage( + reference_id=1, image=mask_img, mask_mode="user_provided" + ) + + images = model.edit_image( + reference_images=[base_ref_image, mask_ref_image], + prompt=prompt, + edit_mode="inpainting-insert", + ) + + images[0].save(location=output_file, include_generation_parameters=False) + + # Optional. View the edited image in a notebook. + # images[0].show() + + print(f"Created output image using {len(images[0]._image_bytes)} bytes") + # Example response: + # Created output image using 1400814 bytes + + # [END generativeaionvertexai_imagen_3_edit_image_inpainting_insert_mask] + + return images + + +if __name__ == "__main__": + edit_image_3_inpainting_insert_mask( + input_file="../test_resources/woman.png", + mask_file="../test_resources/woman_inpainting_insert_mask.png", + output_file="../test_resources/woman_with_hat.png", + prompt="red hat", + ) diff --git a/generative_ai/image_generation/imagen3_editing/edit_image_3_inpainting_insert_mask_mode.py b/generative_ai/image_generation/imagen3_editing/edit_image_3_inpainting_insert_mask_mode.py new file mode 100644 index 000000000000..0895b00dfa96 --- /dev/null +++ b/generative_ai/image_generation/imagen3_editing/edit_image_3_inpainting_insert_mask_mode.py @@ -0,0 +1,80 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Google Cloud Vertex AI sample for editing an image using a mask mode. The + mask mode is used to automatically select the background, foreground (i.e., + the primary subject of the image), or an object based on segmentation class. + Inpainting can insert the object or background designated by the prompt. +""" +import os + +from vertexai.preview import vision_models + +PROJECT_ID = os.getenv("GOOGLE_CLOUD_PROJECT") + + +def edit_image_3_inpainting_insert_mask_mode( + input_file: str, + mask_mode: str, + output_file: str, + prompt: str, +) -> vision_models.ImageGenerationResponse: + # [START generativeaionvertexai_imagen_edit_image_3_inpainting_insert_mask_mode] + + import vertexai + from vertexai.preview.vision_models import Image, ImageGenerationModel, MaskReferenceImage, RawReferenceImage + + # TODO(developer): Update and un-comment below lines + # PROJECT_ID = "your-project-id" + # input_file = "input-image.png" + # mask_mode = "background" # 'background', 'foreground', or 'semantic' + # output_file = "output-image.png" + # prompt = "" # The text prompt describing what you want to see inserted. + + vertexai.init(project=PROJECT_ID, location="us-central1") + + model = ImageGenerationModel.from_pretrained("imagen-3.0-capability-001") + base_img = Image.load_from_file(location=input_file) + base_ref_image = RawReferenceImage(image=base_img, reference_id=0) + mask_ref_image = MaskReferenceImage( + reference_id=1, image=None, mask_mode=mask_mode + ) + + images = model.edit_image( + reference_images=[base_ref_image, mask_ref_image], + prompt=prompt, + edit_mode="inpainting-insert", + ) + + images[0].save(location=output_file, include_generation_parameters=False) + + # Optional. View the edited image in a notebook. + # images[0].show() + + print(f"Created output image using {len(images[0]._image_bytes)} bytes") + # Example response: + # Created output image using 1234567 bytes + + # [END generativeaionvertexai_imagen_edit_image_3_inpainting_insert_mask_mode] + + return images + + +if __name__ == "__main__": + edit_image_3_inpainting_insert_mask_mode( + input_file="../test_resources/woman.png", + mask_mode="background", + output_file="../test_resources/woman_at_beach.png", + prompt="beach", + ) diff --git a/generative_ai/image_generation/imagen3_editing/edit_image_3_inpainting_insert_mask_mode_test.py b/generative_ai/image_generation/imagen3_editing/edit_image_3_inpainting_insert_mask_mode_test.py new file mode 100644 index 000000000000..62450c246ebb --- /dev/null +++ b/generative_ai/image_generation/imagen3_editing/edit_image_3_inpainting_insert_mask_mode_test.py @@ -0,0 +1,41 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +import backoff + +import edit_image_3_inpainting_insert_mask_mode + +from google.api_core.exceptions import ResourceExhausted + +_RESOURCES = os.path.join(os.path.dirname(__file__), "../test_resources") +_INPUT_FILE = os.path.join(_RESOURCES, "woman.png") +_MASK_MODE = "background" +_OUTPUT_FILE = os.path.join(_RESOURCES, "woman_at_beach.png") +_PROMPT = "beach" + + +@backoff.on_exception(backoff.expo, ResourceExhausted, max_time=60) +def test_edit_image_3_inpainting_insert_mask_mode() -> None: + response = ( + edit_image_3_inpainting_insert_mask_mode.edit_image_3_inpainting_insert_mask_mode( + _INPUT_FILE, + _MASK_MODE, + _OUTPUT_FILE, + _PROMPT, + ) + ) + + assert len(response[0]._image_bytes) > 1000 diff --git a/generative_ai/image_generation/imagen3_editing/edit_image_3_inpainting_insert_mask_test.py b/generative_ai/image_generation/imagen3_editing/edit_image_3_inpainting_insert_mask_test.py new file mode 100644 index 000000000000..f2636912dd35 --- /dev/null +++ b/generative_ai/image_generation/imagen3_editing/edit_image_3_inpainting_insert_mask_test.py @@ -0,0 +1,39 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os + +import backoff + +import edit_image_3_inpainting_insert_mask + +from google.api_core.exceptions import ResourceExhausted + + +_RESOURCES = os.path.join(os.path.dirname(__file__), "../test_resources") +_INPUT_FILE = os.path.join(_RESOURCES, "woman.png") +_MASK_FILE = os.path.join(_RESOURCES, "woman_inpainting_insert_mask.png") +_OUTPUT_FILE = os.path.join(_RESOURCES, "woman_with_hat.png") +_PROMPT = "hat" + + +@backoff.on_exception(backoff.expo, ResourceExhausted, max_time=60) +def test_edit_image_3_inpainting_insert_mask() -> None: + response = edit_image_3_inpainting_insert_mask.edit_image_3_inpainting_insert_mask( + _INPUT_FILE, + _MASK_FILE, + _OUTPUT_FILE, + _PROMPT, + ) + + assert len(response[0]._image_bytes) > 1000 diff --git a/generative_ai/image_generation/imagen3_editing/edit_image_3_inpainting_remove_mask.py b/generative_ai/image_generation/imagen3_editing/edit_image_3_inpainting_remove_mask.py new file mode 100644 index 000000000000..c5d7c87b9e2c --- /dev/null +++ b/generative_ai/image_generation/imagen3_editing/edit_image_3_inpainting_remove_mask.py @@ -0,0 +1,81 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Google Cloud Vertex AI sample for editing an image using a mask file. + Inpainting can remove an object from the masked area. +""" +import os + +from vertexai.preview import vision_models + +PROJECT_ID = os.getenv("GOOGLE_CLOUD_PROJECT") + + +def edit_image_3_inpainting_remove_mask( + input_file: str, + mask_file: str, + output_file: str, + prompt: str, +) -> vision_models.ImageGenerationResponse: + # [START generativeaionvertexai_imagen_edit_image_3_inpainting_remove_mask] + + import vertexai + from vertexai.preview.vision_models import Image, ImageGenerationModel, MaskReferenceImage, RawReferenceImage + + # TODO(developer): Update and un-comment below lines + # PROJECT_ID = "your-project-id" + # input_file = "input-image.png" + # mask_file = "mask-image.png" + # output_file = "output-image.png" + # prompt = "" # The text prompt describing the entire image. + + vertexai.init(project=PROJECT_ID, location="us-central1") + + model = ImageGenerationModel.from_pretrained("imagen-3.0-capability-001") + base_img = Image.load_from_file(location=input_file) + mask_img = Image.load_from_file(location=mask_file) + base_ref_image = RawReferenceImage(image=base_img, reference_id=0) + mask_ref_image = MaskReferenceImage( + reference_id=1, image=mask_img, mask_mode="user_provided" + ) + + images = model.edit_image( + reference_images=[base_ref_image, mask_ref_image], + prompt=prompt, + edit_mode="inpainting-remove", + # Optional parameters + # negative_prompt="", # Describes the object being removed (i.e., "person") + ) + + images[0].save(location=output_file, include_generation_parameters=False) + + # Optional. View the edited image in a notebook. + # images[0].show() + + print(f"Created output image using {len(images[0]._image_bytes)} bytes") + # Example response: + # Created output image using 12345678 bytes + + # [END generativeaionvertexai_imagen_edit_image_3_inpainting_remove_mask] + + return images + + +if __name__ == "__main__": + edit_image_3_inpainting_remove_mask( + input_file="../test_resources/volleyball_game.png", + mask_file="../test_resources/volleyball_game_inpainting_remove_mask.png", + output_file="../test_resources/volleyball_game_single_blue_player.png", + prompt="volleyball game", + ) diff --git a/generative_ai/image_generation/imagen3_editing/edit_image_3_inpainting_remove_mask_mode.py b/generative_ai/image_generation/imagen3_editing/edit_image_3_inpainting_remove_mask_mode.py new file mode 100644 index 000000000000..90ae3299b602 --- /dev/null +++ b/generative_ai/image_generation/imagen3_editing/edit_image_3_inpainting_remove_mask_mode.py @@ -0,0 +1,80 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Google Cloud Vertex AI sample for editing an image using a mask mode. The + mask mode is used to automatically select the background, foreground (i.e., + the primary subject of the image), or an object based on segmentation class. + Inpainting can remove the object or background designated by the prompt. +""" +import os + +from vertexai.preview import vision_models + +PROJECT_ID = os.getenv("GOOGLE_CLOUD_PROJECT") + + +def edit_image_3_inpainting_remove_mask_mode( + input_file: str, + mask_mode: str, + output_file: str, + prompt: str, +) -> vision_models.ImageGenerationResponse: + # [START generativeaionvertexai_imagen_edit_image_3_inpainting_remove_mask_mode] + + import vertexai + from vertexai.preview.vision_models import Image, ImageGenerationModel, MaskReferenceImage, RawReferenceImage + + # TODO(developer): Update and un-comment below lines + # PROJECT_ID = "your-project-id" + # input_file = "input-image.png" + # mask_mode = "foreground" # 'background', 'foreground', or 'semantic' + # output_file = "output-image.png" + # prompt = "" # The text prompt describing what you want to see in the edited image. + + vertexai.init(project=PROJECT_ID, location="us-central1") + + model = ImageGenerationModel.from_pretrained("imagen-3.0-capability-001") + base_img = Image.load_from_file(location=input_file) + base_ref_image = RawReferenceImage(image=base_img, reference_id=0) + mask_ref_image = MaskReferenceImage( + reference_id=1, image=None, mask_mode=mask_mode, dilation=0.01 + ) + + images = model.edit_image( + reference_images=[base_ref_image, mask_ref_image], + prompt=prompt, + edit_mode="inpainting-remove", + ) + + images[0].save(location=output_file, include_generation_parameters=False) + + # Optional. View the edited image in a notebook. + # images[0].show() + + print(f"Created output image using {len(images[0]._image_bytes)} bytes") + # Example response: + # Created output image using 1279948 bytes + + # [END generativeaionvertexai_imagen_edit_image_3_inpainting_remove_mask_mode] + + return images + + +if __name__ == "__main__": + edit_image_3_inpainting_remove_mask_mode( + input_file="../test_resources/woman.png", + mask_mode="foreground", + output_file="../test_resources/background.png", + prompt="", + ) diff --git a/generative_ai/image_generation/imagen3_editing/edit_image_3_inpainting_remove_mask_mode_test.py b/generative_ai/image_generation/imagen3_editing/edit_image_3_inpainting_remove_mask_mode_test.py new file mode 100644 index 000000000000..78e984e8e19b --- /dev/null +++ b/generative_ai/image_generation/imagen3_editing/edit_image_3_inpainting_remove_mask_mode_test.py @@ -0,0 +1,42 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +import backoff + +import edit_image_3_inpainting_remove_mask_mode + +from google.api_core.exceptions import ResourceExhausted + + +_RESOURCES = os.path.join(os.path.dirname(__file__), "../test_resources") +_INPUT_FILE = os.path.join(_RESOURCES, "woman.png") +_MASK_MODE = "foreground" +_OUTPUT_FILE = os.path.join(_RESOURCES, "background.png") +_PROMPT = "" + + +@backoff.on_exception(backoff.expo, ResourceExhausted, max_time=60) +def test_edit_image_3_inpainting_remove_mask_mode() -> None: + response = ( + edit_image_3_inpainting_remove_mask_mode.edit_image_3_inpainting_remove_mask_mode( + _INPUT_FILE, + _MASK_MODE, + _OUTPUT_FILE, + _PROMPT, + ) + ) + + assert len(response[0]._image_bytes) > 1000 diff --git a/generative_ai/image_generation/imagen3_editing/edit_image_3_inpainting_remove_mask_test.py b/generative_ai/image_generation/imagen3_editing/edit_image_3_inpainting_remove_mask_test.py new file mode 100644 index 000000000000..30c79c601f08 --- /dev/null +++ b/generative_ai/image_generation/imagen3_editing/edit_image_3_inpainting_remove_mask_test.py @@ -0,0 +1,40 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +import backoff + +import edit_image_3_inpainting_remove_mask + +from google.api_core.exceptions import ResourceExhausted + + +_RESOURCES = os.path.join(os.path.dirname(__file__), "../test_resources") +_INPUT_FILE = os.path.join(_RESOURCES, "volleyball_game.png") +_MASK_FILE = os.path.join(_RESOURCES, "volleyball_game_inpainting_remove_mask.png") +_OUTPUT_FILE = os.path.join(_RESOURCES, "volleyball_game_single_blue_player.png") +_PROMPT = "volleyball game" + + +@backoff.on_exception(backoff.expo, ResourceExhausted, max_time=60) +def test_edit_image_3_inpainting_remove_mask() -> None: + response = edit_image_3_inpainting_remove_mask.edit_image_3_inpainting_remove_mask( + _INPUT_FILE, + _MASK_FILE, + _OUTPUT_FILE, + _PROMPT, + ) + + assert len(response[0]._image_bytes) > 1000 diff --git a/generative_ai/image_generation/imagen3_editing/edit_image_3_outpainting_mask.py b/generative_ai/image_generation/imagen3_editing/edit_image_3_outpainting_mask.py new file mode 100644 index 000000000000..c3707645a653 --- /dev/null +++ b/generative_ai/image_generation/imagen3_editing/edit_image_3_outpainting_mask.py @@ -0,0 +1,80 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Google Cloud Vertex AI sample for editing an image using a mask file. + Outpainting lets you expand the content of a base image to fit a larger or + differently sized mask canvas. +""" +import os + +from vertexai.preview import vision_models + +PROJECT_ID = os.getenv("GOOGLE_CLOUD_PROJECT") + + +def edit_image_3_outpainting_mask( + input_file: str, + mask_file: str, + output_file: str, + prompt: str, +) -> vision_models.ImageGenerationResponse: + # [START generativeaionvertexai_imagen_edit_image_3_outpainting_mask] + + import vertexai + from vertexai.preview.vision_models import Image, ImageGenerationModel, MaskReferenceImage, RawReferenceImage + + # TODO(developer): Update and un-comment below lines + # PROJECT_ID = "your-project-id" + # input_file = "input-image.png" + # mask_file = "mask-image.png" + # output_file = "output-image.png" + # prompt = "" # The optional text prompt describing what you want to see inserted. + + vertexai.init(project=PROJECT_ID, location="us-central1") + + model = ImageGenerationModel.from_pretrained("imagen-3.0-capability-001") + base_img = Image.load_from_file(location=input_file) + mask_img = Image.load_from_file(location=mask_file) + base_ref_image = RawReferenceImage(image=base_img, reference_id=0) + mask_ref_image = MaskReferenceImage( + reference_id=1, image=mask_img, mask_mode="user_provided", dilation=0.03, + ) + + images = model.edit_image( + reference_images=[base_ref_image, mask_ref_image], + prompt=prompt, + edit_mode="outpainting", + ) + + images[0].save(location=output_file, include_generation_parameters=False) + + # Optional. View the edited image in a notebook. + # images[0].show() + + print(f"Created output image using {len(images[0]._image_bytes)} bytes") + # Example response: + # Created output image using 1234567 bytes + + # [END generativeaionvertexai_imagen_edit_image_3_outpainting_mask] + + return images + + +if __name__ == "__main__": + edit_image_3_outpainting_mask( + input_file="../test_resources/roller_skaters.png", + mask_file="../test_resources/roller_skaters_mask.png", + output_file="../test_resources/roller_skaters_downtown.png", + prompt="city with skyscrapers", + ) diff --git a/generative_ai/image_generation/imagen3_editing/edit_image_3_outpainting_mask_test.py b/generative_ai/image_generation/imagen3_editing/edit_image_3_outpainting_mask_test.py new file mode 100644 index 000000000000..2dfdd65acc8a --- /dev/null +++ b/generative_ai/image_generation/imagen3_editing/edit_image_3_outpainting_mask_test.py @@ -0,0 +1,40 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +import backoff + +import edit_image_3_outpainting_mask + +from google.api_core.exceptions import ResourceExhausted + + +_RESOURCES = os.path.join(os.path.dirname(__file__), "../test_resources") +_INPUT_FILE = os.path.join(_RESOURCES, "roller_skaters.png") +_MASK_FILE = os.path.join(_RESOURCES, "roller_skaters_mask.png") +_OUTPUT_FILE = os.path.join(_RESOURCES, "roller_skaters_downtown.png") +_PROMPT = "city with skyscrapers" + + +@backoff.on_exception(backoff.expo, ResourceExhausted, max_time=60) +def test_edit_image_3_outpainting_mask() -> None: + response = edit_image_3_outpainting_mask.edit_image_3_outpainting_mask( + _INPUT_FILE, + _MASK_FILE, + _OUTPUT_FILE, + _PROMPT, + ) + + assert len(response[0]._image_bytes) > 1000 diff --git a/generative_ai/image_generation/imagen3_editing/edit_image_3_product_image.py b/generative_ai/image_generation/imagen3_editing/edit_image_3_product_image.py new file mode 100644 index 000000000000..289f2343142e --- /dev/null +++ b/generative_ai/image_generation/imagen3_editing/edit_image_3_product_image.py @@ -0,0 +1,75 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Google Cloud Vertex AI sample for editing a product image. You can + modify the background content but preserve the product's appearance. +""" +import os + +from vertexai.preview import vision_models + +PROJECT_ID = os.getenv("GOOGLE_CLOUD_PROJECT") + + +def edit_image_3_product_image( + input_file: str, + output_file: str, + prompt: str, +) -> vision_models.ImageGenerationResponse: + # [START generativeaionvertexai_imagen_edit_image_3_product_image] + + import vertexai + from vertexai.preview.vision_models import Image, ImageGenerationModel, MaskReferenceImage, RawReferenceImage + + # TODO(developer): Update and un-comment below lines + # PROJECT_ID = "your-project-id" + # input_file = "input-image.png" + # output_file = "output-image.png" + # prompt = "" # The text prompt describing what you want to see in the background. + + vertexai.init(project=PROJECT_ID, location="us-central1") + + model = ImageGenerationModel.from_pretrained("imagen-3.0-capability-001") + base_img = Image.load_from_file(location=input_file) + base_ref_image = RawReferenceImage(image=base_img, reference_id=0) + mask_ref_image = MaskReferenceImage( + reference_id=1, image=None, mask_mode="background" + ) + + images = model.edit_image( + prompt=prompt, + edit_mode="background-swap", + reference_images=[base_ref_image, mask_ref_image], + ) + + images[0].save(location=output_file, include_generation_parameters=False) + + # Optional. View the edited image in a notebook. + # images[0].show() + + print(f"Created output image using {len(images[0]._image_bytes)} bytes") + # Example response: + # Created output image using 1234567 bytes + + # [END generativeaionvertexai_imagen_edit_image_3_product_image] + + return images + + +if __name__ == "__main__": + edit_image_3_product_image( + input_file="../test_resources/pillow.png", + output_file="../test_resources/pillow_on_beach.png", + prompt="beach", + ) diff --git a/generative_ai/image_generation/imagen3_editing/edit_image_3_product_image_test.py b/generative_ai/image_generation/imagen3_editing/edit_image_3_product_image_test.py new file mode 100644 index 000000000000..ec578119626d --- /dev/null +++ b/generative_ai/image_generation/imagen3_editing/edit_image_3_product_image_test.py @@ -0,0 +1,38 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +import backoff + +import edit_image_3_product_image + +from google.api_core.exceptions import ResourceExhausted + + +_RESOURCES = os.path.join(os.path.dirname(__file__), "../test_resources") +_INPUT_FILE = os.path.join(_RESOURCES, "pillow.png") +_OUTPUT_FILE = os.path.join(_RESOURCES, "pillow_on_beach.png") +_PROMPT = "a beach with sand and water" + + +@backoff.on_exception(backoff.expo, ResourceExhausted, max_time=60) +def test_edit_image_3_product_image() -> None: + response = edit_image_3_product_image.edit_image_3_product_image( + _INPUT_FILE, + _OUTPUT_FILE, + _PROMPT, + ) + + assert len(response[0]._image_bytes) > 1000 diff --git a/generative_ai/image_generation/requirements.txt b/generative_ai/image_generation/requirements.txt index 5d8bc64d330b..8b00ce5d08f9 100644 --- a/generative_ai/image_generation/requirements.txt +++ b/generative_ai/image_generation/requirements.txt @@ -3,7 +3,7 @@ pandas==2.0.3; python_version == '3.8' pandas==2.1.4; python_version > '3.8' pillow==10.3.0; python_version < '3.8' pillow==10.3.0; python_version >= '3.8' -google-cloud-aiplatform[all]==1.69.0 +google-cloud-aiplatform[all]==1.76.0 sentencepiece==0.2.0 google-auth==2.29.0 anthropic[vertex]==0.28.0