{"schema_version":"1","site":"https://decosa.ai","id":"virtual-staging","num":"68","name":"Disclosed virtual staging","tool_name":"Stage a listing photo with disclosure","short":"Virtual staging","blurb":"Stages an empty-room listing photo with open image models, adding movable furniture only, then checks that the walls, windows, doors, views, floors and visible damage were left alone, pixel by pixel and with a side-by-side model comparison. Requests to remove power lines, hide cracks or make a room look bigger are refused. It also checks images staged anywhere else. A photo that passes gets a burned-in 'Virtually staged' label linking to the unaltered original on a public page, a C2PA credential naming the original as its parent, and a signed pair record: the disclosure California's AB 723 has asked for since 1 Jan 2026. Not legal advice.","status":"live","labels":{"industry":["creative-media","sales-marketing"],"job":["generate","attest"],"input":["files"],"deploy":["hosted","selfhost"],"status":"live","output":["media","record"],"data":["none"],"hardware":"gpu-96","licence":"permissive"},"industries":["creative-media","sales-marketing"],"runs_in":["hosted","selfhost"],"part_of":["studio-brand"],"built_from":["studio-render","content-credentials","signed-record"],"models":"Wan2.2-VACE-Fun-A14B (furniture inpainting) · Qwen3.8-27B (structure map, furniture boxes, side-by-side check)","where":"Hosted or self-host","hardware":"Wan2.2-VACE-Fun-A14B (fp8) and Qwen3.8-27B: one 96 GB GPU (measured on two shared ones); the check, label, credential and record run on CPU","final_artifact":"A staged photo with a burned-in 'Virtually staged' label, the original on a public page, a C2PA credential naming the original, and a signed pair record.","self_host_first":false,"verification":{"receipt_coverage":"partial","summary":"Receipt per model call; C2PA credential with the original as parent; signed pair record binding both images, the check and the label","manual_qa":{"hosted":{"date":"2026-09-26","result":"pass","p50_ms":15200,"p95_ms":null,"runs":null,"receipts_per_run":3,"cost_per_run_usd":0.0021},"selfhost":{"date":"2026-09-26","result":"pass","method":"Fresh clone into a clean directory, docker build of the api image (31 s), the api on host networking with named volumes against the running local vLLM (Qwen3.8-27B with vision) and ComfyUI; then torn down.","notes":"The rehearsal bundle passed 13 of 13 (doctored view refused, honest staging labelled, read back, credentialed and verified, record tamper caught, power-lines refused); a Berlin staging rendered and passed in 12.8 s with attested receipts. When ComfyUI runs outside the api container, set DECOSA_STUDIO_COMFY_INPUT_DIR or delete its input copies yourself."},"known_limits":["Hosted verification ran on the pre-release server (decosa-api the pre-release branch on our server, gateway route); production gets this tool when the branch merges.","Measured on 8 CC0 rooms with planted Pillow edits and our own renders, labelled by the building agent; images staged by other tools were not tested.","The renderer redraws patterned floors (a tile border) around furniture; the check then refuses to label it, so such rooms often end not disclosed.","Hairline cracks on patterned walls can be missed (2 of 3 on a marble-effect wall).","The C2PA credential uses a development certificate; public validators show it as untrusted."],"nightly_covers":null},"nightly":"https://api.decosa.ai/verify/status"},"eval_summary":{"metrics":[{"name":"Planted edits caught (check)","value":"38 / 40","unit":null,"n":40,"split":"test","note":"Window removed 10/10, wall recoloured 10/10, view replaced 10/10, crack or damage hidden 8/10."},{"name":"False alarms on clean stagings","value":"0 / 10","unit":null,"n":10,"split":"test","note":null},{"name":"Planted edits caught, pixel check alone","value":"38 / 40","unit":null,"n":40,"split":"test","note":null},{"name":"Planted edits caught, model alone","value":"29 / 40","unit":null,"n":40,"split":"test","note":null},{"name":"Label read back after 1024 px and JPEG q70","value":"30 / 30","unit":null,"n":30,"split":"test","note":null},{"name":"First render passes the check","value":"18 of 32 (56%)","unit":null,"n":32,"split":"synthetic","note":"8 rooms x 4 seeds; 0 of 4 on a tiled floor with a border"}],"dataset":"8 CC0 empty-room photos (Poly Haven panoramas cut to views, Wikimedia Commons), split by room: 3 dev, 5 test. 17 clean stagings by this service labelled by eye; 62 planted Pillow edits (window removed, wall recoloured, view replaced, crack or damage hidden).","held_out":true,"caveats":["The same author wrote the planted edits and the checker and labelled the clean stagings; some edits are crude.","Small: 10 clean and 40 planted test cases from 5 rooms; no images staged by other tools.","Thresholds were tuned on dev in three rounds; the floor-pattern rule was set after seeing renders of a test room.","One redrawn floor patch in a staging that looked otherwise clean passed the check (0 of 1).","Not legal advice or a compliance determination."],"date":"2026-09-26","doc_url":"https://decosa.ai/metrics/evals/virtual-staging"},"stack":{"summary":"Send an empty-room photo. Qwen3.8-27B maps its windows, doors, fixtures and visible damage; Wan2.2-VACE-Fun-A14B draws furniture on the open floor only, and only the furniture pixels are kept. A structural check then compares the staged image with the original, pixel by pixel and with a side-by-side model comparison, and refuses to label anything that changed the property. Requests like 'remove the power lines' or 'make the room bigger' are refused before anything renders. Check mode runs the same check on images staged by any other tool. A photo that passes gets a 'Virtually staged' label with a link to the unaltered original, a C2PA credential naming the original as its parent, and a signed pair record. For listing agents, brokerages and listing photographers.","tagline":"Stage an empty listing photo with furniture only, check that walls, windows, doors, views, floors and damage were left alone, and ship the AB 723 disclosure: a burned-in label, the original on a public page, a C2PA credential and a signed pair record.","deployment":"hosted-or-self-host","regulatory_note":"Not legal advice or a compliance determination; checked against the primary text on 26 Sep 2026. California Business and Professions Code section 10140.8, added by AB 723 (Pellerin; Stats. 2025, ch. 497; approved and filed 10 Oct 2025; as a regular statute with no date of its own it took effect 1 Jan 2026, Cal. Const. art. IV, sec. 8(c)): a real estate broker or salesperson, or a person acting for them, who includes a digitally altered image in an advertisement or promotional material for the sale of real property must include a reasonably conspicuous statement, on or adjacent to the image, that it has been altered, and a link, URL or QR code to a publicly accessible site that includes and clearly identifies the original, unaltered image; on a website they control, the unaltered image must be in the posting or linked. 'Digitally altered' covers adding, removing or changing elements with photo-editing software or AI, including furniture, fixtures, flooring, walls, paint colour, landscape and elements visible from the property such as utility poles, views through windows and neighbouring properties; lighting, white balance, cropping, exposure and similar adjustments that do not change the representation of the property are excluded. A willful violation of the Real Estate Law is a crime. MLSs and portals may have their own rules (not checked here). Model licences: Apache-2.0 (Qwen3.8-27B, Wan2.2-VACE-Fun-A14B, Wan2.2-Lightning).","components":[{"id":"llm","role":"Maps the original's windows, doors, fixtures, damage and open floor; boxes each piece of furniture on the render; compares original and staged side by side; the typed judgment on instructions (typed-judgment, vertical 24)","name":"Qwen3.8-27B (NVIDIA NVFP4)","hf_repo":"nvidia/Qwen3.8-27B-NVFP4","license":"Apache-2.0","params":"27.8B","quant":"NVFP4 (MLP) + FP8 (attention/GDN), FP8 KV cache, MTP speculative decoding k=3","vram_gb":57,"memory_gb_estimate":null,"engine":"vLLM 0.29.0, served with its vision tower","receipt_coverage":"strong","in_hosted_demo":true,"tiers":["lite","standard"],"alternative_to":null},{"id":"render","role":"Draws furniture inside the zone (the open floor, with windows, doors, radiators, built-ins and damage cut out): one inpainted still per attempt, VACE control image plus mask","name":"Wan2.2-VACE-Fun-A14B","hf_repo":"alibaba-pai/Wan2.2-VACE-Fun-A14B","license":"Apache-2.0","params":"A14B (two 14B experts, high and low noise)","quant":"fp8_e4m3fn weights","vram_gb":null,"memory_gb_estimate":null,"engine":"ComfyUI, core WanVaceToVideo (length 1)","receipt_coverage":"partial","in_hosted_demo":true,"tiers":["standard"],"alternative_to":null},{"id":"lightning","role":"4-step distillation LoRAs for the render","name":"Wan2.2-Lightning T2V 4-step LoRAs","hf_repo":"lightx2v/Wan2.2-Lightning","license":"Apache-2.0","params":null,"quant":"bf16","vram_gb":null,"memory_gb_estimate":null,"engine":"ComfyUI LoraLoaderModelOnly","receipt_coverage":"none","in_hosted_demo":true,"tiers":["standard"],"alternative_to":null},{"id":"check","role":"The zone, the furniture-only composite, the pixel check (added, removed, recoloured regions; floor pattern; shadows; what each touches), the verdict, the burned-in label and its OCR read-back, the C2PA credential, the public original page and the signed pair record (no model; CPU)","name":"decosa-api staging module (decosa_api/verticals/staging)","hf_repo":null,"license":"AGPL-3.0-or-later","params":null,"quant":null,"vram_gb":0,"memory_gb_estimate":null,"engine":"Python 3.12, NumPy, Pillow, Tesseract 5, c2pa-python","receipt_coverage":"partial","in_hosted_demo":true,"tiers":["lite","standard"],"alternative_to":null},{"id":"edit","role":"Instruction-following image editing ('add a grey sofa by the left wall') that keeps the rest of the photo by design","name":"Qwen-Image-Edit-2511","hf_repo":"Qwen/Qwen-Image-Edit-2511","license":"Apache-2.0","params":"20B","quant":null,"vram_gb":null,"memory_gb_estimate":null,"engine":"diffusers or ComfyUI","receipt_coverage":"none","in_hosted_demo":false,"tiers":["alternates"],"alternative_to":null}],"tiers":[{"id":"lite","label":"Lite · check only (any staging tool)","summary":"No render: check an image staged elsewhere against its original, then label, credential and pair it when it passes. Two image calls per check.","components":["llm","check"],"hardware":"1x RTX 5090 32 GB for the model (not measured) or the hosted gateway","quality_evidence":[{"metric":"held-out test: planted edits caught (window removed, wall recoloured, view replaced, crack hidden) / false alarms on clean stagings","value":"38 of 40 / 0 of 10","source":"decosa-api docs/evals/virtual-staging.md, measured on our server 2026-09-26, gateway route"},{"metric":"burned-in label read back by OCR after resizing to 1024 px and JPEG quality 70","value":"30 of 30","source":"decosa-api docs/evals/virtual-staging.md, 2026-09-26"}],"latency_note":"measured: seconds per check under load; a fraction of a cent at list price","in_hosted_demo":true,"receipt_coverage":"strong","receipt_note":"Gateway receipt per image call on the hosted route.","hosting":null},{"id":"standard","label":"Standard · stage and check (hosted demo)","summary":"Furniture drawn on the open floor by Wan2.2-VACE-Fun-A14B, only the furniture kept, the same check; a render the check rejects is drawn again (up to 3 seeds) and never labelled.","components":["llm","render","lightning","check"],"hardware":"Qwen3.8-27B and VACE A14B on two 96 GB cards (measured, shared)","quality_evidence":[{"metric":"first render passes the check, 8 rooms x 4 seeds","value":"18 of 32 (0 of 4 on a tiled floor with a border, 0 of 4 on a cluttered room)","source":"decosa-api docs/evals/virtual-staging.md, 2026-09-26"},{"metric":"passing stagings that look furniture-only to a person (the building agent)","value":"17 of 18","source":"decosa-api docs/evals/virtual-staging.md, 2026-09-26"}],"latency_note":"measured: seconds for one render end to end; a fraction of a cent of model calls plus the render","in_hosted_demo":true,"receipt_coverage":"partial","receipt_note":"Model calls are receipted by the gateway; the render is recorded in the pair record and the C2PA credential, not receipted.","hosting":null}],"alternates":[{"id":"upgrade","label":"instruction image editing","components":["edit"],"hardware":"not measured","use":"Qwen-Image-Edit-2511 (Apache-2.0) edits by instruction and keeps the rest of the photo by design; not installed here.","status":"not served"}],"services":[{"name":"decosa-api","port":8445,"image":"${DECOSA_REGISTRY}/decosa-api:0.1.0","purpose":"GET /staging/info, /staging/samples; POST /staging/runs (SSE or JSON; stage or check); GET /staging/runs/{id}[/export]; public GET /staging/p/{pair_id} (the original's page). No GPU; Tesseract and c2pa inside. The studio worker renders through ComfyUI."},{"name":"decosa-llm","port":8000,"image":"${DECOSA_REGISTRY}/decosa-llm:0.1.0","purpose":"vLLM OpenAI endpoint for Qwen3.8-27B, served with its vision tower (image input). Internal to the compose network."},{"name":"ComfyUI with Wan2.2-VACE-Fun-A14B","port":8189,"image":null,"purpose":"Stage mode only: the render. No published image yet; install ComfyUI and the weights. Check mode needs no GPU render."}],"tools":[{"name":"Cal. Bus. & Prof. Code 10140.8 (AB 723, Stats. 2025, ch. 497)","url":"https://leginfo.legislature.ca.gov/faces/billTextClient.xhtml?bill_id=202520260AB723","license":"California statute (public)","purpose":"The disclosure the label, the original's page and the pair record are built for."},{"name":"C2PA via c2pa-python (provenance kit)","url":"https://github.com/contentauth/c2pa-python","license":"MIT OR Apache-2.0","purpose":"The credential on the staged image: c2pa.opened (the original as parent ingredient) and c2pa.edited (compositeWithTrainedAlgorithmicMedia)."},{"name":"Tesseract OCR 5","url":"https://github.com/tesseract-ocr/tesseract","license":"Apache-2.0","purpose":"Reads the burned-in label back from the delivered pixels."},{"name":"decosa typed-judgment (vertical 24)","url":"https://decosa.ai/apps/typed-judgment","license":"AGPL-3.0-or-later (decosa-api)","purpose":"The model layer of the instruction policy: furniture, property change or unclear."},{"name":"decosa record (vertical 07) and POST /record/verify","url":"https://decosa.ai/apps/record","license":"AGPL-3.0-or-later (decosa-api)","purpose":"The signed pair record anyone can re-check."},{"name":"scripts/staging_eval.py","url":null,"license":"Apache-2.0","purpose":"8 CC0 rooms (Poly Haven, Wikimedia Commons), 17 clean stagings and 62 planted edits, split by room into dev and test."}],"hardware":[{"tier":"2x RTX PRO 6000 Blackwell 96 GB (shared)","fits":true,"notes":"Measured: the hosted demo's Qwen3.8-27B on one card, ComfyUI (VACE, fp8) on the other beside other studio services."},{"tier":"1x RTX PRO 6000 96 GB","fits":null,"notes":"Not measured: Qwen3.8-27B NVFP4 (about 20 GB of weights) and VACE A14B fp8 together should fit with a reduced KV cache."},{"tier":"CPU only","fits":true,"notes":"The pixel check, label, credential, pair page and record run on CPU; the map and comparison need the model (check mode), the render needs a GPU (stage mode)."}],"latency":[{"lane":"check mode (an image staged elsewhere), hosted gateway route","typical_ms":5200,"source":"measured on our server 2026-09-26: p50 over 50 test checks, under load from other workloads"},{"lane":"stage mode, one render, hosted","typical_ms":15200,"source":"measured on our server 2026-09-26: the recorded Berlin run (3 model calls, a 6 s render); up to 3 renders when the check rejects one"},{"lane":"stage mode, self-host direct route","typical_ms":12800,"source":"measured on our server 2026-09-26: fresh-clone api image against the local vLLM and ComfyUI"}],"benchmark":{"title":"Does the check catch a doctored listing photo?","intro":"Eight CC0 empty rooms were staged by this service; each honest staging was then doctored four ways with ordinary photo edits. Thresholds were tuned on three rooms and frozen; five rooms were held out.","rows":[{"label":"Planted edits caught (held-out 40)","value":"38 of 40","detail":"both misses are hairline cracks on a marble-effect wall"},{"label":"Honest stagings flagged","value":"0 of 10","detail":null},{"label":"Window removed / view replaced","value":"10 of 10 / 10 of 10","detail":null},{"label":"Wall recoloured / crack or damage hidden","value":"10 of 10 / 8 of 10","detail":null},{"label":"Label read back after resizing and JPEG q70","value":"30 of 30","detail":null}],"points":[{"heading":"Where it fails","text":"Hairline cracks on a patterned wall; a patch of floor redrawn around a chair on a floor with little pattern; and our own renderer on a tiled floor with a border, which it redraws (the check refuses those, so the room is not disclosed)."},{"heading":"What this does not show","text":"Images staged by other tools, careful retouching, independent labels and legal judgement are not measured. The planted edits are ordinary Pillow edits written by the same author as the checker."}],"source":"decosa-api docs/evals/virtual-staging.md, 2026-09-26"},"notes":[]},"buyer_facts":[{"label":"What it checks","value":"Whether a staged photo changes the property: windows painted over or hidden, doors, the view through a window, wall colour, floor patterns painted out, fixtures, and visible damage hidden or covered, plus framing. Refuses instructions to remove, hide or improve anything."},{"label":"What it does not do","value":"It is not legal advice or a compliance determination, and it can miss things (2 of 40 planted edits in the held-out test, a redrawn floor patch). A person should look at the pair before it is published. It does not check MLS or portal rules."},{"label":"Data retention","value":"Uploads live in a temporary folder for the run and are deleted when it ends. A disclosed pair (the original, the staged image and the record) is kept for 30 days on a public page, because the label links to it: download the pair to host it on your own listing site. Runs are kept in memory for one hour for the token or key that made them. Logs carry ids, verdicts and counts, never images or instructions."},{"label":"What leaves the box (hosted demo)","value":"The photos go to Qwen3.8-27B through our gateway and to the studio GPU, both operated by Decosa. The disclosed pair is public by design. Self-hosted, nothing leaves the machine unless you publish the pair."},{"label":"Cost per image","value":"A fraction of a cent to check an image staged elsewhere (two image calls) and a little more plus a short GPU render to stage one, at the gateway list price. Staging services typically charge tens of dollars per image."},{"label":"Output","value":"A JPEG with 'Virtually staged' and the original's link burned in, a C2PA credential naming the original as its parent, a public page with the unaltered original, and a signed pair record (verify at /record/verify)."}],"data_handling":{"page":"/data#virtual-staging","self_host":{"level":"confidential","leaves":"nothing","summary":"Runs on your machine; nothing is sent to Decosa or a third party by default."},"hosted":{"level":"operator-processed","demo_only":false,"summary":"TLS to Decosa's server, then decrypted and processed by Decosa's API server, with the open models run by NEAR AI through OpenRouter, with Reka AI as the only fallback under Decosa's account.","gpus":"operator-contracted","third_parties":[],"retention":"Uploads live in a temporary folder for the run and are deleted when it ends. A disclosed pair (the original, the staged image and the record) is kept for 30 days on a public page, because the label links to it: download the pair to host it on your own listing site. Runs are kept in memory for one hour for the token or key that made them. Logs carry ids, verdicts and counts, never images or instructions.","used_for_training":false,"encrypted_while_processed":false},"sealed_tier":{"applies":false,"note":"The sealed tier (raw chat only, never use-case pipelines) is paused at launch (/docs/sealed-tier)."},"external_calls":[]},"console":{"href":"/tools/operations/virtual-staging","input":"staging","lanes":[{"id":"check","title":"Structural check","kind":"list"},{"id":"compare","title":"Before and after","kind":"list"},{"id":"disclosure","title":"Disclosure","kind":"list"},{"id":"record","title":"Pair record","kind":"json"}],"samples":[{"n":1,"id":"house-living","title":"House living","deep_link":"/tools/operations/virtual-staging?sample=1&autorun=0"},{"n":2,"id":"berlin-living","title":"Berlin living","deep_link":"/tools/operations/virtual-staging?sample=2&autorun=0"},{"n":3,"id":"old-room","title":"Old room","deep_link":"/tools/operations/virtual-staging?sample=3&autorun=0"},{"n":4,"id":"room-marble","title":"Room marble","deep_link":"/tools/operations/virtual-staging?sample=4&autorun=0"},{"n":5,"id":"check-view-replaced","title":"Check view replaced","deep_link":"/tools/operations/virtual-staging?sample=5&autorun=0"},{"n":6,"id":"check-clean","title":"Check clean","deep_link":"/tools/operations/virtual-staging?sample=6&autorun=0"},{"n":7,"id":"refuse-power-lines","title":"Refuse power lines","deep_link":"/tools/operations/virtual-staging?sample=7&autorun=0"},{"n":8,"id":"refuse-bigger","title":"Refuse bigger","deep_link":"/tools/operations/virtual-staging?sample=8&autorun=0"}],"deep_link_params":{"sample":"1-based index into samples, or a sample id","autorun":"1 = start the run once the sample is loaded; 0 (default) = only preselect","reduce-motion":"1 = turn off animations"}},"api":{"base":"https://api.decosa.ai","contract":"/api/contract.json","contract_markdown":"/api/contract.md","reference":"/docs/api","keys":"/account/keys"},"prompts":{"hosted":"/prompts/virtual-staging-hosted.md","selfhost":"/prompts/virtual-staging-selfhost.md","assemble":"/prompts/virtual-staging-assemble.md","mac":null},"rehearsal":{"bundle":"/samples/virtual-staging.zip","bundle_url":"https://decosa.ai/samples/virtual-staging.zip","folder":"/samples/virtual-staging/","expected":"/samples/virtual-staging/expected.json","files":["/samples/virtual-staging/expected.json","/samples/virtual-staging/inputs/original.jpg","/samples/virtual-staging/inputs/staged.jpg","/samples/virtual-staging/inputs/view-replaced.jpg"],"bytes":591408,"checks":["the doctored image is not labelled","the check fails it","the changed view is named","no pair is made for it","the honest staging is labelled and paired","the check passes it","the burned-in label reads back","it carries a C2PA credential","the pair record verifies","a record with the original's hash changed no longer verifies","'remove the power lines' is refused by the word patterns","the refusal happens before any model call","every model call has a signed receipt"],"licence":"Old Room by Sergej Majboroda and Fish Hoek Beach by Greg Zaal and Rico Cilliers (Poly Haven, CC0 1.0); the furniture was drawn by Wan2.2-VACE-Fun-A14B in decosa-api. Part of decosa-api, AGPL-3.0-or-later.","about":"An empty old room with arched windows (CC0, Poly Haven) and two images staged from it. In one, the view out of the arched window was swapped for a beach: the check must refuse to label it and name the view. The other only adds furniture: it must pass, get the burned-in 'Virtually staged' label (read back by OCR), a C2PA credential and a signed pair record that verifies, and fails once the original's hash is changed. A request to 'remove the power lines' must be refused before any model call or render.","run":{"containers":"docker compose exec api python scripts/rehearse.py virtual-staging","checkout":"python scripts/rehearse.py virtual-staging --bundle virtual-staging.zip --base-url http://127.0.0.1:8445","mac":".venv/bin/python scripts/rehearse.py virtual-staging"},"guidance":"Set up with a coding agent (we recommend Claude Code with Claude Opus 5.5; any capable coding agent works) on mock data only, run the rehearsal until every check passes, then run your own data locally yourself. Never give the agent real data during setup."},"hardware_fit":{"check":"/self-host/hardware?use=virtual-staging","data":"/api/hardware.json","tiers":[{"id":"lite","gpu_gb":57.6,"basis":"stack","unknown":[]},{"id":"standard","gpu_gb":91.6,"basis":"estimate","unknown":[]},{"id":"alternate-upgrade","gpu_gb":49,"basis":"estimate","unknown":[]}],"mac":null},"links":{"page":"/tools/operations/virtual-staging","json":"/use-cases/virtual-staging.json","metrics":"/metrics/virtual-staging","console":"/tools/operations/virtual-staging","console_sample":"/tools/operations/virtual-staging?sample=1&autorun=0","stack":"/tools/operations/virtual-staging#stack","try_live":"/tools/operations/virtual-staging","watch":"/tools/operations/virtual-staging","build":"/tools/operations/virtual-staging#build","self_host":"/tools/operations/virtual-staging#self-host","prompts":{"hosted":"/prompts/virtual-staging-hosted.md","selfhost":"/prompts/virtual-staging-selfhost.md","assemble":"/prompts/virtual-staging-assemble.md","mac":null}}}