Contents · 15 sections
- 1The question, stated precisely
- 2Method
- 3Experiment 1 — a real product photograph
- 4Experiment 2 — twenty-six letters, fifteen of them gone
- 5Experiment 3 — the dose-response curve
- 6Experiment 4 — is it the prompt?
- 7Experiment 5 — marking the image instead
- 8Experiment 6 — the same prompt, the other model
- 9Experiment 7 — what happens when the two channels disagree
- 10What the seven experiments say together
- 11Fidelity: what survives an edit
- 12Price, resolution, and what you actually get
- 13Limitations
- 14What we changed
- 15What we learned, beyond the fix
Every image editor built on a generative model inherits a problem from linguistics. To edit an object you must first refer to it, and a reference only works if it picks out exactly one thing. Photographs are full of things that resist that: five identical spheres, two mugs from the same set, three men in black shirts, twenty-six letters where fifteen must go and eleven must stay.
Our own editor failed it in the most literal way possible. Click a detected object, press delete, and every object of the same kind disappears with it. Not sometimes — reliably, because of what the software was sending. This is the study we ran to find out what to send instead, and it turned into a controlled comparison of the two models most people are choosing between right now: gemini-3.1-flash-image (Nano Banana 2) and gpt-image-2.5-sunburst (GPT Image 2.5), with.
Eighty-nine generations, seven experiments, about $8 of API spend. The headline result is not that one model wins. It is that the two models disagree about something more fundamental — which channel to believe when the words and the coordinates point at different objects.
The question, stated precisely
A detection row in our database carries two ways of pointing at the same object: a label the scanner wrote (“young man in black tank top”) and a bounding box in coordinates normalised 0–1000. Call these the linguistic channel and the geometric channel. The question is which one a generative editor actually resolves a reference with, and what happens when they conflict.
Three hypotheses were on the table before we started:
- H1: the label alone is insufficient whenever two objects satisfy it — a **collision**, not a model failure.
- H2: the box alone is sufficient, and adding it repairs the **collision**.
- H3: models weight the two channels differently, so a fix that works on one will not transfer.
H1 was confirmed in ten minutes. H2 is what we expected to confirm, spent most of the day trying to confirm, and it is wrong. H3 is what we ended up with, and it has a practical consequence: the correct instruction is not the same for both models.
Method
What the editor was sending
Before anything else, the baseline. Clicking Remove on a detection produced this instruction, verbatim, with the box left behind in the browser:
REMOVE AND REVEAL: Remove “penguin” from the image. Do NOT leave a hole, blank space, or blur. Reveal what would naturally be underneath or behind it.
Run against a scene holding twenty objects of that kind, it removes twenty of twenty. The model is not confused; it is compliant. Every edit in the product travelled as a bare noun.
A phantom, so the scoring is arithmetic
Photographs make poor instruments for this question, because whether something was “removed” is arguable. Medical imaging solves the same problem with a phantom: a manufactured object whose true properties are known exactly, used to calibrate a machine before it is pointed at a patient. We built the equivalent.
Four synthetic scenes — eight, twelve, twenty and thirty identical figures, placed on a jittered grid so that no natural-language ordinal (“third from the left”) can single one out, with deliberate clusters where three overlap and one is left only forty per cent visible. Every figure gets a silhouette mask with the figures painted over it subtracted, so an occluded figure is judged only on the pixels where it actually shows. Removal is then a number: the collapse of dark pixels inside that mask. Over-removal is countable too, and turns out to matter more than the hits.

Controls and discipline
- One variable per run. Where a prompt changed, only one clause changed; the two closing sentences were byte-identical across all eight wordings.
- Cross-model runs used the same string, verified by hash, and the same base image, verified by hash.
- Two negative controls: the original image differenced against itself, which must be black at any amplification; and the production label-only instruction, which must fail.
- Scoring is blind to intent — it reads pixels inside masks, not what we hoped would happen.
- The large claims are single runs. Where a result contradicted an earlier conclusion, it was repeated; those repeats are marked in the text.
| test image | source file | fed to both models | gemini-3.1-flash-image returned | gpt-image-2.5-sunburst returned |
|---|---|---|---|---|
| phantom scenes | synthetic PNG 1024×1024 | 1024×1024 PNG | 1024×1024 JPEG | 1024×1024 PNG |
| product render | JPEG 5056×3392 · 17.2 MP | 1536×1024 PNG | 1264×843 JPEG · 1.07 MP | 1536×1024 PNG · 1.57 MP |
| alphabet sheet | JPEG 2528×1696 · 4.3 MP | 1536×1024 PNG | 1264×843 JPEG · 1.07 MP | 1536×1024 PNG · 1.57 MP |
| concert crowd | JPEG 1200×800 · 0.96 MP | 1536×1024 PNG | 1264×843 JPEG · 1.07 MP | 1536×1024 PNG · 1.57 MP |
Experiment 1 — a real product photograph
A 3D product render with eighteen detections from our own scanner, and two collisions that got there by accident. Five nearly identical blurred spheres float in the background; only two are detected, and those two were requested. Two mugs carry the identical label; one was requested. Seven targets, addressed by coordinate alone, no names attached.

gemini-3.1-flash-image removed four of seven. It left both spheres and it resolved the mug collision the wrong way, deleting the one we wanted kept. It also produced the single strangest artefact of the study:

gpt-image-2.5-sunburst removed all seven, kept the correct mug and kept the console. It also removed the three background spheres nobody asked about. From two instances it inferred a category and applied the deletion to the class — the original bug arriving from the opposite direction: not a label that matches too much, but an induction we never requested.
| gemini-3.1-flash-image | gpt-image-2.5-sunburst | |
|---|---|---|
| of the 7 requested, removed | 4 | 7 |
| the two blurred spheres | left both | removed both |
| two identically labelled mugs, one requested | removed the wrong one | correct |
| objects touched that were never requested | 2 | 3 — the undetected spheres |
| failure mode | substitution and early stop | categorical over-generalisation |
Experiment 2 — twenty-six letters, fifteen of them gone
A cleaner instrument. A specimen sheet of pop-culture letters: fifteen requested of twenty-six, scattered, never two adjacent, addressed only by coordinate. The prompt contains no letter, no name and not the word “letter”. Because the twenty-six glyphs are mutually distinct, there is no category available to over-generalise — which makes this the control for the sphere result above.

| gemini-3.1-flash-image | gpt-image-2.5-sunburst | |
|---|---|---|
| of the 15 requested, removed | 7 | 15 |
| of the 11 to keep, disturbed | 0 | 0 |
| survivors left in position | no — the bottom row slid left to close the gaps | yes, every cell |
Experiment 3 — the dose-response curve
Both real-photograph experiments suggest the same thing: the count of simultaneous targets is a variable in its own right. On the phantom we can measure it properly. Fifteen rungs, climbing from one simultaneous target to fifteen across the four scene densities.
Twenty-nine of seventy across the ladder for the production prompt. The shape matters more than the total: in ten of fifteen rungs there was zero collateral damage. The model does not remove the wrong figure — it stops early. Ask for ten, receive three, cleanly composited, in an image that looks finished. A system that silently completes a third of a job is more dangerous than one that fails loudly, because the output carries no signal that it stopped.
Experiment 4 — is it the prompt?
The obvious confound. Our production wrapper is 255 words of inherited scaffolding: a preserve list, a conflict-resolution clause, an inventory of what the model can already see. We took the hardest rung — thirty figures, fifteen targets — and rewrote the instruction eight ways, one variable at a time.
Nothing beat nine. Not the shortest version, not the longest, not the one written in the model's own native coordinate order. Two deliberate attacks on the early-stopping behaviour — stating the count in prose, numbering the list — both made it worse, and numbering made it dramatically worse: an image returned all but untouched.
One effect did replicate. A single sentence of restraint — “leave every other object exactly the same” — produced the lowest collateral damage in the study at no cost in hits. The hundred-word preserve list bought the same restraint and some timidity with it. With eight phrasings spanning 87 to 255 words all landing between one and nine, we can retire the prompt-length hypothesis: it is not the lever.
Experiment 5 — marking the image instead
If describing the target is unreliable, mark it. Draw a red X on the object and send the annotated photograph. The objection is immediate: you have painted over the customer's picture, and every pixel under the mark must be invented back.

That one is solvable with the boxes alone — choose the point inside the target's box that no other box covers, which lands dead centre on an unobstructed object and slides onto the visible sliver of a buried one. With that in place we mirrored all fifteen rungs: same scenes, same targets, only the pointing changed. Twenty-two of seventy, against twenty-nine for coordinates.
Marking bought nothing and introduced two failure modes coordinates never showed. In two rungs the model took the cheap reading of the instruction: it erased the red crosses and left every figure standing — an output indistinguishable from success without a pixel comparison. In one rung it dissolved the clusters and re-laid the scene into a tidy grid. We also measured the degradation we had feared and found it absent: a marked photograph did not re-render any worse than a clean one.
Experiment 6 — the same prompt, the other model

| model | removed | collateral | ≈ cost / image | behaviour |
|---|---|---|---|---|
| gemini-3.1-flash-image Nano Banana 2 | 9 / 15 | 3 | $0.084 | stops early, cleanly |
| gpt-image-2.5-sunburst GPT Image 2.5, quality high | 14 / 15 | 2 | $0.053 | follows the list |
Experiment 7 — what happens when the two channels disagree
Everything so far pointed at a tidy conclusion: send the box as well as the label and the ambiguity goes away. Testing that properly needs an image where a label genuinely collides, on real people rather than clip art. A concert crowd, scanned by our own detection pipeline, which found four foreground people — three of them wearing black.

Four conditions, same target throughout. The scanner's descriptive label alone; that label plus its box; a deliberately ambiguous label (“a man in a black shirt”) alone; and the ambiguous label plus the box of the person we actually meant. The last condition is the whole experiment: language and geometry pointing at different people, and we get to see which one the model believes.
| what we sent | gemini-3.1-flash-image | gpt-image-2.5-sunburst |
|---|---|---|
| the scanner's descriptive label | correct | correct |
| descriptive label + box | correct | correct |
| ambiguous label alone | removed nobody | removed the wrong person |
| ambiguous label + the correct box | correct — the box won | wrong person again — the words won |
| repeat of the row above | — | wrong person again |

gemini-3.1-flash-image resolves the reference geometrically. Handed a phrase that fits three people and a box around one, it deletes the one in the box. gpt-image-2.5-sunburst resolves it linguistically. Handed the same pair, it commits to its own reading of the phrase and deletes a man the box does not touch — twice out of twice, with the coordinates sitting right there in the prompt.
This retro-explains the oddest result we had already collected and set aside. On the product render we asked for a single object — the soccer ball — with its box and no name: the only noun in the instruction was the bare word “object”. gpt-image-2.5-sunburst ignored the coordinate and deleted the shopping bag, the hero of the picture. Assuming a bad sample, we repeated the identical prompt twice more. Three runs, three times the wrong object. Reformatted as a bulleted list rather than a sentence: same. With two boxes in the list it got the second one right and still took the bag. That is five box-only requests on this image and five wrong deletions — on the same image where seven boxes in one list came back perfect. A vague noun gives the linguistic channel nothing to resolve, so it falls back on salience and the box does not get a vote. Add the specific name to that same request and it is correct, twice out of twice.

What the seven experiments say together
- The label alone is a collision hazard, and the collision is ours, not the model's. Twenty of twenty figures removed is correct behaviour for the sentence we sent.
- The box alone is not a universal repair. It repairs an under-specified reference in gemini-3.1-flash-image and is overridden by a confident wrong reading in gpt-image-2.5-sunburst.
- A specific, discriminating label is the strongest single signal for both models — which is an argument for the scanner writing distinguishing descriptions, not generic nouns, and ours already does.
- Simultaneous targets degrade completion independently of ambiguity, and no wording we tried moved that ceiling.
- Over-removal appears when objects share a category, not when there are many of them. Twenty-six distinct letters: no over-removal. Five near-identical spheres: all five.
Which makes the operational rule a boring one, and different from the one we set out to write. Send both channels, and make the linguistic one specific enough to stand alone — because on one of the two models it will decide the outcome no matter what the coordinates say.
Fidelity: what survives an edit
Wipe between an original and either output and the untouched objects look identical. They are not: both models regenerate the entire frame. Neither returns your pixels.

Amplification misrepresents visibility, so: compared pixel to pixel at identical dimensions with nothing resampled between them, the letters we asked to keep differ by a mean of 4 to 10 out of 255, with peaks near 100 on a small fraction of pixels. To the eye, identical.
The obvious explanation — a colour profile, or export compression — is testable and false. Neither file carries an ICC profile, both are 8-bit sRGB, PNG is lossless, and the global signed offset is about −1 per channel. Apply a perfect global tone correction and the residual does not improve by a single point. The residual is instead distributed exactly as regeneration predicts: flat interiors differ by 4, contours and glow by 23. The glyphs were not recoloured; they were redrawn in place, very slightly differently.
On a flat backdrop this is irrelevant. On fine structure it is worth knowing, because contours are what small type, logos and fabric weave are made of. Measured on two brand logos well outside any requested region, both survived at a mean difference of 4, with gpt-image-2.5-sunburst altering 1.4% of their pixels against gemini-3.1-flash-image's 6%.
There is a confound in those numbers that we caught only while writing this up, and it changes one of our own figures, so: gemini-3.1-flash-image returns JPEG. There is no output-format parameter — whatever you send, the bytes that come back are lossy. gpt-image-2.5-sunburst returns what you ask for, and we asked for PNG. Part of the drift measured on Gemini is therefore its own encoder rather than regeneration. The clean measurement — flat interiors differing by 4, contours by 23, and a perfect tone correction improving nothing — was taken on GPT's PNG against a PNG original, and that one stands. Gemini's drift figures should be read as an upper bound that includes a JPEG pass we could not switch off.
Where byte-fidelity is required, the fix is compositional rather than generative: keep the original everywhere and take the model's output only inside the requested boxes, with a soft edge. Measured, 100% of the image outside those regions returns byte-identical. Two implementation notes, both learned by getting them wrong — the mask must be fully opaque across the box and ramp only outside it, or the deleted object's own edge survives at half opacity as a ghost outline; and a shadow falling outside the box will not be cleaned up, because those pixels are by construction the original.
Price, resolution, and what you actually get
| gemini-3.1-flash-image | gpt-image-2.5-sunburst | |
|---|---|---|
| input tokens | 538 | 1,762 (1,024 = the image) |
| output tokens | 1,390 | 1,372 |
| cost per image | ~$0.084 | ~$0.053 |
| per additional reference image | ~$0.0003 | ~$0.0082 |
| size requested | 1K tier | 1536×1024 |
| size returned | 1264×843 | 1536×1024 |
| reference resolution effect | sizes by total area, so 3:2 lands smaller than 1:1 | returns the literal size requested |
The resolution row is the one that surprises people. Gemini sizes its output by total pixel area rather than by long edge, so a 3:2 image at the 1K tier comes back at 1264×843 — narrower than the 1536-wide input we handed it. The edit is returned as a downscale, which is a quality cost that never appears on an invoice.
| gemini-3.1-flash-image | gpt-image-2.5-sunburst | |
|---|---|---|
| how you ask for size | a tier: 1K / 2K / 4K | literal pixels, e.g. 1536×1024 |
| what decides the result | total pixel area — 3:2 lands smaller than 1:1 at the same tier | the number you asked for |
| practical ceiling | 4K tier = 4096×4096 = 16.8 MP | 8.29 MP hard cap, and above 2560×1440 OpenAI marks it experimental — so ~2K is the working ceiling |
| other limits | — | long edge 3840, both edges multiples of 16, ratio at most 3:1 |
| output encoding | JPEG, no parameter to change it | PNG, JPEG or WebP, your choice |
Per image, gpt-image-2.5-sunburst came out about a third cheaper: $0.053 against $0.084. Per pixel actually delivered the gap is wider, because the same request returned 1.57 MP from one model and 1.07 MP from the other — $0.034 per megapixel against $0.078, roughly 2.3× cheaper. That ordering inverts with reference count, since GPT charges about $0.0082 per reference image and Gemini about $0.0003: one reference and GPT wins, six and it loses badly. Worth checking against what an operation costs here before planning around either.
Limitations
- Sample size. Most cells are a single generation. Findings that contradicted an earlier conclusion were repeated — the channel-conflict result twice, the single-object result three times plus two reformats — and those are flagged in the text. The rest should be read as direction, not as a benchmark.
- Instrument bias. The phantom is flat vector art on a plain field, which makes inpainting easy and may understate the difficulty of removal in textured photographs.
- One prompt family. Every instruction descends from the same three-sentence ancestor. A structurally different formulation — a conversation, a mask image, a tool call — was not tested.
- No repeats across models on the real photographs. The crowd conflict is the exception and the one we would most want replicated at scale.
- Provider drift. These are dated measurements against hosted models that change without notice. Every number here is from a single day.
What we changed
- Every detection edit now carries its box. Remove, recolour, change material and free-text edits inherit the same repair; all four were suffering the same ambiguity silently.
- The box travels with the label, never instead of it — and the scanner's obligation to write discriminating labels is now a correctness requirement, not a nicety.
- One sentence of restraint replaces the hundred-word preserve list. Lowest collateral damage in the study, at no cost in hits.
- Splitting many targets across sequential passes remains open. Fifteen at once tops out near nine for gemini-3.1-flash-image, and no wording moved it; chained single-target calls are the obvious next experiment and the expensive one, at a generation per object.
Eighty-nine generations and about $8 to discover that the answer was a column we already had, and that it only works on one of the two models unless the column next to it is written well. We will take it.
What we learned, beyond the fix
Five things survived the day that were not on the list of questions we started with, and they are the part we would actually tell a colleague over coffee.
| on the evidence here | gemini-3.1- flash-image | gpt-image-2.5- sunburst | where it was measured |
|---|---|---|---|
| Follows a bounding box when the label is vague | ✓ | ✗ | single-object test · 3 identical runs + 2 reformats, all box-only |
| Follows a bounding box when the label is wrong | ✓ | ✗ | the crowd test, 2 runs of 2 |
| Follows a discriminating label | ✓ | ✓ | both correct, with or without the box |
| Completes a long list of targets | ✗ | ✓ | 7 of 15 against 15 of 15 on the alphabet |
| Leaves unrequested objects alone | ✓ | ≈ | it generalised a category and took all five spheres |
| Keeps survivors in position | ≈ | ✓ | it slid a row left to close the gaps |
| Returns the size you asked for | ✗ | ✓ | 1264×843 for a 1536×1024 request |
| Returns lossless pixels | ✗ | ✓ | JPEG with no parameter to change it |
| Cheap with one reference image | ≈ | ✓ | $0.084 against $0.053 |
| Cheap with six reference images | ✓ | ✗ | $0.0003 against $0.0082 each |
- A model can fail silently and beautifully. The most dangerous output in the whole study was the one that erased our red marks and left every object standing: perfectly composited, plausible, and completely wrong. Any pipeline that edits images on a user's behalf needs a verification step that is not a human glance.
- For this task, on this evidence, gpt-image-2.5-sunburst is the one we would ship. It finished long lists where gemini-3.1-flash-image stopped around nine, it left unrequested objects in position, it returned the size we asked for in a lossless format, and it cost about a third less per image. We are not going to dress that up as “it depends”.
- The condition on that recommendation is not small. gpt-image-2.5-sunburst resolves the reference from the words, so a vague or wrong label is obeyed over a correct coordinate — it deleted the wrong person twice with the right box sitting in the prompt. It is the better model here only because our scanner writes discriminating labels. On top of a scanner that writes “person”, it becomes the worse choice and gemini-3.1-flash-image, which follows the box, becomes the safer one.
- Our own prompt was the confound we examined last. A hundred words of inherited preserve list had quietly turned a capable model into a timid one, and we spent hours suspecting the model before suspecting ourselves.
- The database already knew. The scanner had been writing a discriminating label and an exact box into the same row since the day it shipped. The failure was entirely in what we chose to read out of it.
And one open question we are leaving on the table honestly: we never found a way to make a single call reliably complete more than about nine deletions. No wording moved it, no marker moved it, and only one of three models got past it. Either that ceiling is a property of how these models plan a composite edit, or we have not found the right shape of request yet. We would rather say that than round it off.



