AI Wall Art Photography API: Preserve Licensed Artwork
Your catalog has a licensed illustration with a small signature and carefully approved colors. You ask an image model to photograph it above a sofa, and the returned scene looks plausible—but the signature changes and a leaf disappears. An AI wall art photography API workflow should separate the room from the product: generate the background, then place the authorized artwork locally without asking a model to redraw it.
This is a problem-solving workflow for developers building listing images, not a promise that generated interiors are documentary photographs. Deterministic compositing means placing existing image pixels through explicit, repeatable operations rather than generating their content. Keep the licensed master untouched, record any display transformation, and label simulated room scenes where your marketplace or advertising rules require it.
How can you preserve licensed artwork exactly?
Preserve the original file and composite its decoded pixels into the scene without regeneration, resizing, color conversion, or overlays when exact pixel identity is required.
“Exactly” needs a defined acceptance test. Original-file identity means the stored bytes remain unchanged. Pixel identity means the displayed artwork region matches the decoded source. Visual fidelity after resizing is a different standard: interpolation changes pixels even when the composition remains recognizable. A perspective transform also resamples the image, so an angled mockup cannot generally claim unchanged source pixels.
Choose the standard before generating backgrounds. For pixel-exact previews, select a front-facing wall and an artwork asset already sized for its destination. For publication images that require scaling, derive an approved display asset from the master, record that operation, and compare the final composite against that display asset. Do not describe the derived file as byte-identical to the master.
The example below deliberately takes the strict path. It rejects transparency and non-RGB inputs, pastes without scaling, and checks the exported PNG. That scope is narrow enough to audit rather than relying on a prompt such as “keep the painting unchanged.”
What should the image model generate instead of the artwork?
Have the model generate an empty room background with an unobstructed, front-facing wall, leaving the artwork entirely outside the generation step.
Use the GenRelay image API overview to evaluate the image-generation stage, then consult current provider documentation for supported requests. This article does not specify an endpoint, request field, upload method, or model identifier. The local compositor consumes a downloaded image; it does not depend on the provider's response format.
A background brief could read:
Interior listing mockup, straight-on view of a blank plaster wall above a low sideboard, soft diffuse daylight, open central wall area, no paintings, no posters, no lettering, no objects overlapping the central wall.
Treat this as creative guidance, not a geometry guarantee. Review the returned image for slanted walls, unwanted decoration, contradictory shadows, and insufficient clear space. Select a placement rectangle manually for the first template. Store those coordinates with the approved background rather than assuming every newly generated room has the same geometry.
Avoid generating a finished frame with an arbitrary opening unless you also verify that opening. A simple local border is easier to control, but it is not evidence of a real frame's finish, depth, or construction. Use separately photographed, licensed frame assets when those physical properties are part of the product being sold.
How do you composite the artwork locally with Python?
Use Pillow to paste an opaque RGB artwork into a reviewed background, draw borders outside its bounds, and verify the saved artwork region against the source.
Pillow is a Python image-processing library. The following commands and script are local workflow code, not a provider API request. They make no network requests and incur no model-generation charges. Supply your own reviewed room.png and authorized art.png; the latter must already have the pixel dimensions you want to display.
python -m pip install Pillow
python compose_wall_art.py --room room.png --art art.png \
--out listing.png --x 320 --y 160 --border 16
Save this script as compose_wall_art.py:
import argparse
import hashlib
import json
from pathlib import Path
from PIL import Image, ImageChops, ImageDraw, ImageOps
def sha256(path):
return hashlib.sha256(Path(path).read_bytes()).hexdigest()
def require(condition, message):
if not condition:
raise ValueError(message)
def main():
p = argparse.ArgumentParser()
p.add_argument("--room", required=True)
p.add_argument("--art", required=True)
p.add_argument("--out", required=True)
p.add_argument("--x", type=int, required=True)
p.add_argument("--y", type=int, required=True)
p.add_argument("--border", type=int, default=16)
args = p.parse_args()
room_path, art_path, out = map(
Path, (args.room, args.art, args.out)
)
report = out.with_suffix(".qa.json")
sources = {room_path.resolve(), art_path.resolve()}
require(out.suffix.lower() == ".png", "Output must be PNG")
require(out.resolve() not in sources, "Do not overwrite inputs")
require(report.resolve() not in sources, "Report overlaps input")
require(not out.exists() and not report.exists(),
"Choose unused output paths")
require(args.border >= 0, "Border must be nonnegative")
source_hashes = {"art": sha256(art_path),
"room": sha256(room_path)}
with Image.open(art_path) as source:
require(source.mode == "RGB", "Use an approved RGB art asset")
require("transparency" not in source.info,
"Transparent artwork is not supported")
require(source.getexif().get(274, 1) == 1,
"Normalize orientation in an approved display asset")
art = source.copy()
art_profile = source.info.get("icc_profile")
with Image.open(room_path) as source:
room_profile = source.info.get("icc_profile")
room = ImageOps.exif_transpose(source).convert("RGB")
require(art_profile == room_profile,
"Inputs must have matching ICC profiles or both be untagged")
x, y, b = args.x, args.y, args.border
w, h = art.size
require(x - b >= 0 and y - b >= 0 and
x + w + b <= room.width and y + h + b <= room.height,
"Artwork and border must fit inside room")
if b:
ImageDraw.Draw(room).rectangle(
(x - b, y - b, x + w + b - 1, y + h + b - 1),
fill=(45, 40, 35)
)
room.paste(art, (x, y))
out.parent.mkdir(parents=True, exist_ok=True)
room.save(out, format="PNG", icc_profile=art_profile)
with Image.open(out) as saved:
crop = saved.convert("RGB").crop((x, y, x + w, y + h))
exact = ImageChops.difference(crop, art).getbbox() is None
profile_ok = saved.info.get("icc_profile") == art_profile
require(exact and profile_ok, "Export QA failed")
require(sha256(art_path) == source_hashes["art"],
"Source artwork changed during processing")
report.write_text(json.dumps({
"inputs_sha256": source_hashes,
"output_sha256": sha256(out),
"art_rectangle": [x, y, x + w, y + h],
"art_pixels_identical": exact,
"icc_profile_preserved": profile_ok,
"pillow_version": Image.__version__
}, indent=2), encoding="utf-8")
print(f"PASS: {out}; report: {report}")
if __name__ == "__main__":
main()
The coordinates locate the artwork's upper-left corner, not the border's corner. Both images must share the same embedded ICC profile, a description of how pixel values map to color, or both must be untagged. Matching profiles prevent a basic mismatch; untagged files still need an explicitly agreed color-space assumption. Prepare approved color-managed assets upstream rather than silently converting licensed artwork inside this script.
Which quality checks matter beyond pixel equality?
Check product geometry, scene realism, rights, and downstream delivery separately, because identical artwork pixels do not establish a truthful product presentation.
The generated sideboard is not a measuring instrument. If a listing claims a specific print size, use a scene with known dimensions or label the image as illustrative. A small print displayed at an implausibly large scale can mislead buyers even when its pixels are unchanged.
Use a release checklist:
- Artwork integrity: Verify the source hash and exported crop. A hash is a content fingerprint that helps detect file changes; it does not establish ownership.
- Placement: Confirm the border fits, the artwork is level, and no plant or furniture should appear in front of it. This simple script does not model occlusion, where foreground objects hide part of the artwork.
- Color and lighting: Review the composite in a color-managed viewer. Keep shadows and reflections outside the protected artwork region if strict equality is required.
- Rights: Record the license, permitted promotional uses, territory, expiration, and any attribution requirement. Background generation does not expand your artwork license.
- Delivery: Inspect the actual storefront image after upload. Image optimization services may resize or recompress it, invalidating the local equality result.
Keep pixel-exact PNGs as audit outputs. If a storefront needs JPEG derivatives, approve those separately and document that lossy compression changes pixel values. A visually acceptable derivative and an exact-pixel master serve different purposes.
How should you budget for generated room backgrounds?
Budget for background-generation attempts separately from local compositing, using the selected model's listed resolution price and your actual number of attempts.
The supplied GenRelay prices as of October 26, 2026, in USD per generated image, are:
| Model | Listed resolution | Price per image |
|---|---|---|
| Nano Banana Pro | 1K or 2K | $0.030 |
| Nano Banana Pro | 4K | $0.042 |
| Nano Banana 2 | 1K | $0.020 |
| Nano Banana 2 | 4K | $0.036 |
| GPT-image-2 | Resolution not specified in supplied rate | $0.014 |
For example, twenty background generations at Nano Banana Pro's listed 2K rate total $0.60 in generation charges. If ten pass your review, those ten approved backgrounds still represent twenty paid generations in this example. Reusing a background locally for additional authorized artworks does not itself trigger another generation request.
These figures exclude storage, processing, delivery, taxes, and any additional generation or editing operations. They are not quality benchmarks or acceptance-rate predictions. Verify current pricing before a production batch, and choose resolution based on the approved artwork's dimensions and the surrounding scene space you need.
What changes when you turn the mockup into a video?
A video-generation model can alter artwork across frames, so an intact input composite does not establish intact output frames.
The related wall art video API guide covers the adjacent clip-production use case, but preservation requires a separate validation plan. For strict fidelity, consider local camera motion over the completed still rather than regenerating the scene. Even then, scaling and video compression change pixels, so define whether your acceptance criterion is source identity or approved visual fidelity.
For generated motion, examine signatures, text, borders, and illustration details across the clip. If artwork must be inserted after motion generation, the compositor needs tracked placement coordinates for each frame and a policy for foreground objects. That is a different implementation from the static script above.
What do developers frequently ask about this workflow?
The recurring questions concern preservation, rights, color, and the boundary between generation and local processing.
Can a prompt guarantee that an image model preserves my illustration?
No. Prompt instructions are not a pixel-preservation test. Keep the artwork outside generation and verify the composed region against the approved source or display asset.
Can I resize the artwork and still call it exact?
Not at the source-pixel level. Resizing typically changes pixels. Preserve the master, approve a resized display asset separately, and test the composite against that asset.
Does keeping artwork local remove licensing obligations?
No. You still need permission for the intended commercial presentation, derivatives, and distribution. Keeping the file out of a provider request can limit disclosure, but it does not grant usage rights.
Why does the script reject mismatched color profiles?
One output image cannot assign separate color profiles to its artwork and background regions. Rejecting mismatches forces an explicit asset-preparation decision instead of silently changing the artwork's color interpretation.
Is the Python example an integration with GenRelay?
No. It is a provider-independent local compositor and export check. Generate and download a room background through a documented integration, then pass that file to the script with your authorized artwork.