Vibecode PhotoRoom
track this build5 phases, 15 steps, beginner friendly0%The core loop is buildable, but a dependable replacement becomes a real weekend or multi-day project. For PhotoRoom, remove or replace backgrounds and batch-export consistent product images. The hard boundary is specialized vision models, mobile capture, templates, and high-volume apis, plus frontier models, compute, and data.
You are building a lean indie version of PhotoRoom. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # PhotoRoom · indie build A pipeline that takes a folder of product photographs, cuts out the subject with a segmentation model, composites onto a chosen background, applies consistent framing and padding, and exports at the exact sizes each destination needs, with every output reproducible from the source and a settings file. Estimated effort: **weekend**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | Python 3.12 | the image and model ecosystem lives here and fighting that is pointless | | Segmentation | rembg for speed, BiRefNet for edge quality, behind one interface | the right choice depends on your subjects, so make it a setting rather than a decision | | Compositing | Pillow with alpha compositing in linear light | compositing in the usual gamma-encoded space produces dark fringes around every edge | | Framing | Subject bounding box from the alpha mask, then padding rules | consistent framing across a catalogue is what makes a set look professional | | Storage | Sources untouched, outputs plus a settings hash per run | a catalogue you cannot regenerate is a catalogue you cannot correct | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Python 3.12 or newer** · free - Why: Runs the pipeline and hosts the model runtimes. - Get it: python.org or your package manager, then a virtual environment. - Verify: python3 --version prints 3.12 or higher - [ ] **A machine that can run segmentation, ideally with a GPU** · free if you have suitable hardware; renting GPU time is the alternative and has a per-hour cost - Why: rembg runs acceptably on a processor; higher quality models are much slower without a GPU, which changes whether batch processing is practical. - Get it: Check what you have first. Time one image through both models before committing to a model for a two hundred image catalogue. - Verify: One image through the chosen model completes in a time you can multiply by your catalogue size - [ ] **Segmentation model files with licences checked** · free to download; check commercial use terms individually - Why: They are the engine, and their licences differ in ways that matter for commercial product photography. - Get it: rembg downloads its models on first use. Higher quality models are separate downloads. Read each licence before using it commercially rather than after. - Verify: Each model runs and its licence is recorded - [ ] **Twenty real product photographs** · free - Why: Including at least one with hair or fur, one on a busy background and one where the subject touches the frame edge. Clean studio shots hide every problem this pipeline exists to solve. - Get it: Use your own catalogue. If you do not have one, this build has no use case yet and the pipeline decisions cannot be made well. - Verify: Twenty images are present with the three hard cases included - [ ] **Background assets you are licensed to use** · free for generated backgrounds - Why: A composited background is redistributed with every product image you publish. - Get it: Solid colours and gradients you generate are safest. If you use photographs, check the licence permits commercial redistribution as part of a composite. - Verify: Every background asset's licence is recorded ## Quick start ```sh pip install rembg pillow rembg i input.jpg output.png ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Product-specific vision models trained for this exact task. - Mobile capture with live preview. - Template and generated background libraries. - High volume hosted processing. - specialized vision models, mobile capture, templates, and high-volume APIs - frontier proprietary models - hosted GPU capacity - licensed training data - moderation and fast global delivery If one of those is essential to you, that is the reason to keep paying for PhotoRoom, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · PhotoRoom The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a batch background remover and product image pipeline running on your own machine to replace PhotoRoom. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Do not train a segmentation model. Two strong open ones exist and the work worth doing is the pipeline around them: consistent framing, reproducible output, and a batch that does not need supervision. Choose the model deliberately. rembg is the fast, dependency-light option that installs and runs, and its ceiling is edge quality. BiRefNet is a high-resolution model with sub-pixel alpha matting that handles hair, fur and lace far better, at a real compute cost. If your products have hard edges, the cheap option is genuinely enough. Checked September 2026. ### Stack (fixed, do not substitute) - Runtime: Python 3.12 · the image and model ecosystem lives here and fighting that is pointless - Segmentation: rembg for speed, BiRefNet for edge quality, behind one interface · the right choice depends on your subjects, so make it a setting rather than a decision - Compositing: Pillow with alpha compositing in linear light · compositing in the usual gamma-encoded space produces dark fringes around every edge - Framing: Subject bounding box from the alpha mask, then padding rules · consistent framing across a catalogue is what makes a set look professional - Storage: Sources untouched, outputs plus a settings hash per run · a catalogue you cannot regenerate is a catalogue you cannot correct ### Data model (create this before Phase 1) - runs(id, at, settings_hash, model, source_dir, output_dir) - images(id, run_id, source_path, output_path, subject_bbox_json, alpha_coverage, warnings_json) - settings(id, hash, model, background_json, padding_json, output_presets_json) · hashed so an output can always be traced to how it was made - The settings hash on every output is what makes a catalogue reproducible: the same sources and the same hash produce the same images. ### Phase 1 · One cutout, and an honest look at the edges Build: Run one image through rembg and save the alpha mask separately from the composite. Then run the same image through BiRefNet. Compare the masks at the edges, zoomed in, rather than comparing the composites at normal size. Trap: Comparing composites on a white background hides almost every edge failure, because a soft or wrong edge against white looks fine. Look at the alpha mask itself, and composite onto a saturated colour where a bad edge is obvious. Done when: Both models produce a cutout of the same image and their alpha masks are saved separately. Compositing each onto a saturated background shows the edge quality difference clearly. The choice of model for your subjects is written down with the reason. Do not build yet: No batch, no framing, no export presets. ### Phase 2 · Compositing without fringes Build: Composite in linear light: convert to linear, composite with the alpha mask, convert back. Handle partially transparent pixels correctly. Test against a subject with fine detail on both a light and a dark background. Trap: Alpha compositing in gamma-encoded space produces a dark fringe around every soft edge, which is subtle on white and glaring on a coloured background. Convert to linear light first, composite, then convert back. This one step is the difference between output that looks composited and output that looks photographed. Done when: A cutout with fine edge detail composited onto a mid-grey background shows no dark fringe, checked at full resolution. The same cutout on a light and a dark background both look correct. Compositing onto a transparent background and then onto a colour gives the same result as compositing directly. Do not build yet: No framing or batch yet. ### Phase 3 · Consistent framing across a catalogue Build: Find the subject bounding box from the alpha mask. Scale so the subject occupies a chosen proportion of the frame, and pad to the target aspect ratio. Anchor consistently, usually centred horizontally and sitting on a baseline vertically. Trap: Centring the bounding box vertically makes tall and short products look like they are floating at different heights when placed side by side in a grid. Anchor to a baseline instead, so everything sits on the same line, which is what makes a catalogue look deliberate. Done when: Fifty images of different subject sizes produce outputs where the subject occupies the same proportion of the frame. Placed in a grid, subjects sit on a common baseline rather than floating. A subject touching the edge of its source frame is detected and warned about rather than silently cropped. Do not build yet: No batch reproducibility or export presets yet. ### Phase 4 · Batch that is reproducible Build: Process a directory as a run. Hash the settings and record that hash with every output. Continue past a failing image and report it. Make a rerun with the same settings produce byte-identical outputs. Trap: A pipeline with any nondeterminism, such as a model running with a random seed or a resize using a nondeterministic path, cannot be reproduced, so a catalogue cannot be corrected image by image later. Pin the seed, pin the resampling filter, and verify by running twice and comparing bytes. Done when: A folder of a hundred images processes unattended. Rerunning with the same settings produces byte-identical outputs. A corrupt image is reported and does not stop the run. Every output's settings hash matches the run that produced it. Do not build yet: No export presets or review pass yet. ### Phase 5 · Export presets and a review pass Build: Named export presets with exact pixel dimensions and format. A review view sorting outputs by a confidence signal such as alpha coverage or edge complexity, so the likely failures are at the top rather than scattered through two hundred files. Trap: Reviewing two hundred cutouts in file order means the five bad ones are found by someone else after publication. Sort by a signal that correlates with difficulty, because the failures cluster at one end and reviewing the top twenty catches most of them. Done when: Each preset produces exactly the pixel dimensions it names. The review view puts genuinely problematic cutouts near the top, verified against a set you have marked by hand. Marking an image as needing rework excludes it from the export until it is redone. Do not build yet: Model licensing and distribution come last. ### Phase 6 · Model licences and running it for others Build: Record the licence of every model and every background asset. If you expose the pipeline as a service, add authentication, limits and a queue, because segmentation is compute and an open endpoint is a free processor. Trap: Model licences in this area vary and some permit research use only, which is not the same as commercial product photography. Check each one specifically rather than assuming that open source means unrestricted, and record what you found. Done when: Every bundled model and background asset has a licence recorded with what it permits. An exposed endpoint requires authentication and enforces a per-user job limit. A queue prevents concurrent jobs from exhausting memory, verified by submitting more jobs than the limit. Do not build yet: Nothing after this. ### Out of scope (and why) - The specialised vision models behind the commercial product, which are trained on product photography specifically and beat general segmentation on exactly the cases that matter. - Mobile capture with live preview, which is most of how the paid application is used. - The template library and the generated backgrounds, which are content and model access. - High volume hosted processing. You have one machine, and segmentation is not cheap. ### README must contain - Which model is used and why, with the subject types it was chosen for - That compositing happens in linear light, and what a gamma-space composite looks like - That framing anchors to a baseline rather than centring, and why a grid makes that obvious - That every output carries a settings hash and reruns are byte-identical - Every bundled model and asset licence, and whether commercial use is permitted ===== AGENTS.md ===== # Agent instructions · PhotoRoom indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Python 3.12, rembg for speed, BiRefNet for edge quality, behind one interface, Pillow with alpha compositing in linear light, Subject bounding box from the alpha mask, then padding rules, Sources untouched, outputs plus a settings hash per run. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Comparing composites on a white background hides almost every edge failure, because a soft or wrong edge against white looks fine. Look at the alpha mask itself, and composite onto a saturated colour where a bad edge is obvious. - Alpha compositing in gamma-encoded space produces a dark fringe around every soft edge, which is subtle on white and glaring on a coloured background. Convert to linear light first, composite, then convert back. This one step is the difference between output that looks composited and output that looks photographed. - Centring the bounding box vertically makes tall and short products look like they are floating at different heights when placed side by side in a grid. Anchor to a baseline instead, so everything sits on the same line, which is what makes a catalogue look deliberate. - A pipeline with any nondeterminism, such as a model running with a random seed or a resize using a nondeterministic path, cannot be reproduced, so a catalogue cannot be corrected image by image later. Pin the seed, pin the resampling filter, and verify by running twice and comparing bytes. - Reviewing two hundred cutouts in file order means the five bad ones are found by someone else after publication. Sort by a signal that correlates with difficulty, because the failures cluster at one end and reviewing the top twenty catches most of them. ===== BUILD_PLAN.md ===== # Build plan · PhotoRoom A pipeline that takes a folder of product photographs, cuts out the subject with a segmentation model, composites onto a chosen background, applies consistent framing and padding, and exports at the exact sizes each destination needs, with every output reproducible from the source and a settings file. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · One cutout, and an honest look at the edges A single image is cut out, and you can see exactly where the model struggles. ### Steps 1. Run both models on one image Save the alpha mask as its own file. The mask is what you are actually evaluating. ```sh pip install rembg pillow rembg i input.jpg output.png ``` 2. Composite onto a hostile background A saturated colour, not white. Every edge problem becomes visible immediately. Files: `src/compare.py` 3. Write down the model choice With the reason and the subject type. Hard-edged products and hair are genuinely different problems. Files: `docs/model-choice.md` ### Done when - [ ] Both models produce a cutout and a separate alpha mask - [ ] Edge differences are visible on a saturated background - [ ] The model choice is recorded with its reasoning - [ ] Fine detail such as hair is inspected at full resolution ### Watch out - Comparing composites on a white background hides almost every edge failure, because a soft or wrong edge against white looks fine. Look at the alpha mask itself, and composite onto a saturated colour where a bad edge is obvious. ## Phase 2 · Compositing without fringes The cutout sits on a new background with no dark halo around its edges. ### Steps 1. Convert to linear before compositing Undo the transfer function, composite, reapply. Do it once in a helper so no code path can skip it. Files: `src/composite.py` 2. Handle partial alpha correctly Premultiplied or straight alpha, chosen deliberately and applied consistently. Mixing the two is how fringes reappear. Files: `src/composite.py` 3. Test on light and dark The same subject on both. A fringe visible on one and not the other means the conversion is half applied. ### Done when - [ ] No dark fringe on a mid-grey background at full resolution - [ ] Light and dark backgrounds both look correct - [ ] Two-step compositing matches direct compositing - [ ] Alpha convention is consistent across every code path ### Watch out - Alpha compositing in gamma-encoded space produces a dark fringe around every soft edge, which is subtle on white and glaring on a coloured background. Convert to linear light first, composite, then convert back. This one step is the difference between output that looks composited and output that looks photographed. ## Phase 3 · Consistent framing across a catalogue Fifty products photographed at different distances end up looking like one set. ### Steps 1. Derive the bounding box from alpha Threshold the mask and find the tight bounds. Ignore isolated specks, which are usually mask noise rather than subject. Files: `src/frame.py` 2. Scale to a target subject proportion The subject occupies a chosen fraction of the frame. This is what makes different photographs match. Files: `src/frame.py` 3. Anchor to a baseline Horizontally centred, vertically sitting on a line. Compare a grid of outputs before and after to see why. Files: `src/frame.py` ### Done when - [ ] Subjects occupy the same frame proportion across fifty images - [ ] A grid of outputs shows subjects on a common baseline - [ ] A subject touching the source edge produces a warning - [ ] Mask specks do not distort the bounding box ### Watch out - Centring the bounding box vertically makes tall and short products look like they are floating at different heights when placed side by side in a grid. Anchor to a baseline instead, so everything sits on the same line, which is what makes a catalogue look deliberate. ## Phase 4 · Batch that is reproducible A whole folder processes unattended, and any output can be regenerated exactly. ### Steps 1. Hash the settings Every setting that affects output goes into the hash. If changing something does not change the hash, the hash is wrong. Files: `src/settings.py` 2. Make the pipeline deterministic Pin seeds and resampling filters. Run twice and compare bytes as a test. Files: `tests/test_determinism.py` 3. Isolate failures One unreadable file must not stop a run of two hundred. Report it and continue. Files: `src/batch.py` ### Done when - [ ] A hundred images process unattended - [ ] Rerunning produces byte-identical outputs - [ ] A corrupt image is reported without stopping the run - [ ] Every output records its settings hash ### Watch out - A pipeline with any nondeterminism, such as a model running with a random seed or a resize using a nondeterministic path, cannot be reproduced, so a catalogue cannot be corrected image by image later. Pin the seed, pin the resampling filter, and verify by running twice and comparing bytes. ## Phase 5 · Export presets and a review pass Each destination gets the size it needs, and the ones the model got wrong are easy to find. ### Steps 1. Add presets with asserted dimensions Named sizes per destination, with a test asserting output dimensions rather than trusting arithmetic. Files: `src/presets.py` 2. Compute a difficulty signal Alpha coverage, edge perimeter relative to area, and the count of partially transparent pixels. Sort by it. Files: `src/review.py` 3. Validate the sort against hand marking Mark twenty by hand, then check whether the sort surfaces them. If it does not, the signal is wrong. ### Done when - [ ] Every preset produces exactly the dimensions it names - [ ] The review sort surfaces hand-marked problem images near the top - [ ] Marking for rework excludes an image from export - [ ] The review view shows the alpha mask, not only the composite ### Watch out - Reviewing two hundred cutouts in file order means the five bad ones are found by someone else after publication. Sort by a signal that correlates with difficulty, because the failures cluster at one end and reviewing the top twenty catches most of them. ## Not in this build - Product-specific vision models trained for this exact task. - Mobile capture with live preview. - Template and generated background libraries. - High volume hosted processing. ## After v1, if you want it - Shadow generation, which is what makes a composite look photographed rather than pasted - Automatic colour consistency across a catalogue shot under different lighting - A manual mask touch-up tool for the few images the review pass surfaces ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Which model to use. Chosen by the comparison in Phase 1, not by reputation. SEGMENTATION_MODEL=rembg # Required. Input directory. Never written to. SOURCE_DIR=./photos/source # Required. Where composites and masks are written. OUTPUT_DIR=./photos/out # Required. What proportion of the frame the subject fills. The single most important setting for a consistent catalogue. SUBJECT_FRAME_FRACTION=0.78 # Required. Where the subject's bottom edge sits vertically. Anchoring here rather than centring is what aligns a grid. BASELINE_FRACTION=0.88 # Required. Concurrent images. Too high exhausts memory on a GPU faster than you expect. BATCH_CONCURRENCY=2
You are building a lean indie version of PhotoRoom. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # PhotoRoom · indie build A pipeline that takes a folder of product photographs, cuts out the subject with a segmentation model, composites onto a chosen background, applies consistent framing and padding, and exports at the exact sizes each destination needs, with every output reproducible from the source and a settings file. Estimated effort: **weekend**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | Python 3.12 | the image and model ecosystem lives here and fighting that is pointless | | Segmentation | rembg for speed, BiRefNet for edge quality, behind one interface | the right choice depends on your subjects, so make it a setting rather than a decision | | Compositing | Pillow with alpha compositing in linear light | compositing in the usual gamma-encoded space produces dark fringes around every edge | | Framing | Subject bounding box from the alpha mask, then padding rules | consistent framing across a catalogue is what makes a set look professional | | Storage | Sources untouched, outputs plus a settings hash per run | a catalogue you cannot regenerate is a catalogue you cannot correct | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Python 3.12 or newer** · free - Why: Runs the pipeline and hosts the model runtimes. - Get it: python.org or your package manager, then a virtual environment. - Verify: python3 --version prints 3.12 or higher - [ ] **A machine that can run segmentation, ideally with a GPU** · free if you have suitable hardware; renting GPU time is the alternative and has a per-hour cost - Why: rembg runs acceptably on a processor; higher quality models are much slower without a GPU, which changes whether batch processing is practical. - Get it: Check what you have first. Time one image through both models before committing to a model for a two hundred image catalogue. - Verify: One image through the chosen model completes in a time you can multiply by your catalogue size - [ ] **Segmentation model files with licences checked** · free to download; check commercial use terms individually - Why: They are the engine, and their licences differ in ways that matter for commercial product photography. - Get it: rembg downloads its models on first use. Higher quality models are separate downloads. Read each licence before using it commercially rather than after. - Verify: Each model runs and its licence is recorded - [ ] **Twenty real product photographs** · free - Why: Including at least one with hair or fur, one on a busy background and one where the subject touches the frame edge. Clean studio shots hide every problem this pipeline exists to solve. - Get it: Use your own catalogue. If you do not have one, this build has no use case yet and the pipeline decisions cannot be made well. - Verify: Twenty images are present with the three hard cases included - [ ] **Background assets you are licensed to use** · free for generated backgrounds - Why: A composited background is redistributed with every product image you publish. - Get it: Solid colours and gradients you generate are safest. If you use photographs, check the licence permits commercial redistribution as part of a composite. - Verify: Every background asset's licence is recorded ## Quick start ```sh pip install rembg pillow rembg i input.jpg output.png ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Product-specific vision models trained for this exact task. - Mobile capture with live preview. - Template and generated background libraries. - High volume hosted processing. - specialized vision models, mobile capture, templates, and high-volume APIs - frontier proprietary models - hosted GPU capacity - licensed training data - moderation and fast global delivery If one of those is essential to you, that is the reason to keep paying for PhotoRoom, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · PhotoRoom The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a batch background remover and product image pipeline running on your own machine to replace PhotoRoom. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Do not train a segmentation model. Two strong open ones exist and the work worth doing is the pipeline around them: consistent framing, reproducible output, and a batch that does not need supervision. Choose the model deliberately. rembg is the fast, dependency-light option that installs and runs, and its ceiling is edge quality. BiRefNet is a high-resolution model with sub-pixel alpha matting that handles hair, fur and lace far better, at a real compute cost. If your products have hard edges, the cheap option is genuinely enough. Checked September 2026. ### Stack (fixed, do not substitute) - Runtime: Python 3.12 · the image and model ecosystem lives here and fighting that is pointless - Segmentation: rembg for speed, BiRefNet for edge quality, behind one interface · the right choice depends on your subjects, so make it a setting rather than a decision - Compositing: Pillow with alpha compositing in linear light · compositing in the usual gamma-encoded space produces dark fringes around every edge - Framing: Subject bounding box from the alpha mask, then padding rules · consistent framing across a catalogue is what makes a set look professional - Storage: Sources untouched, outputs plus a settings hash per run · a catalogue you cannot regenerate is a catalogue you cannot correct ### Data model (create this before Phase 1) - runs(id, at, settings_hash, model, source_dir, output_dir) - images(id, run_id, source_path, output_path, subject_bbox_json, alpha_coverage, warnings_json) - settings(id, hash, model, background_json, padding_json, output_presets_json) · hashed so an output can always be traced to how it was made - The settings hash on every output is what makes a catalogue reproducible: the same sources and the same hash produce the same images. ### Phase 1 · One cutout, and an honest look at the edges Build: Run one image through rembg and save the alpha mask separately from the composite. Then run the same image through BiRefNet. Compare the masks at the edges, zoomed in, rather than comparing the composites at normal size. Trap: Comparing composites on a white background hides almost every edge failure, because a soft or wrong edge against white looks fine. Look at the alpha mask itself, and composite onto a saturated colour where a bad edge is obvious. Done when: Both models produce a cutout of the same image and their alpha masks are saved separately. Compositing each onto a saturated background shows the edge quality difference clearly. The choice of model for your subjects is written down with the reason. Do not build yet: No batch, no framing, no export presets. ### Phase 2 · Compositing without fringes Build: Composite in linear light: convert to linear, composite with the alpha mask, convert back. Handle partially transparent pixels correctly. Test against a subject with fine detail on both a light and a dark background. Trap: Alpha compositing in gamma-encoded space produces a dark fringe around every soft edge, which is subtle on white and glaring on a coloured background. Convert to linear light first, composite, then convert back. This one step is the difference between output that looks composited and output that looks photographed. Done when: A cutout with fine edge detail composited onto a mid-grey background shows no dark fringe, checked at full resolution. The same cutout on a light and a dark background both look correct. Compositing onto a transparent background and then onto a colour gives the same result as compositing directly. Do not build yet: No framing or batch yet. ### Phase 3 · Consistent framing across a catalogue Build: Find the subject bounding box from the alpha mask. Scale so the subject occupies a chosen proportion of the frame, and pad to the target aspect ratio. Anchor consistently, usually centred horizontally and sitting on a baseline vertically. Trap: Centring the bounding box vertically makes tall and short products look like they are floating at different heights when placed side by side in a grid. Anchor to a baseline instead, so everything sits on the same line, which is what makes a catalogue look deliberate. Done when: Fifty images of different subject sizes produce outputs where the subject occupies the same proportion of the frame. Placed in a grid, subjects sit on a common baseline rather than floating. A subject touching the edge of its source frame is detected and warned about rather than silently cropped. Do not build yet: No batch reproducibility or export presets yet. ### Phase 4 · Batch that is reproducible Build: Process a directory as a run. Hash the settings and record that hash with every output. Continue past a failing image and report it. Make a rerun with the same settings produce byte-identical outputs. Trap: A pipeline with any nondeterminism, such as a model running with a random seed or a resize using a nondeterministic path, cannot be reproduced, so a catalogue cannot be corrected image by image later. Pin the seed, pin the resampling filter, and verify by running twice and comparing bytes. Done when: A folder of a hundred images processes unattended. Rerunning with the same settings produces byte-identical outputs. A corrupt image is reported and does not stop the run. Every output's settings hash matches the run that produced it. Do not build yet: No export presets or review pass yet. ### Phase 5 · Export presets and a review pass Build: Named export presets with exact pixel dimensions and format. A review view sorting outputs by a confidence signal such as alpha coverage or edge complexity, so the likely failures are at the top rather than scattered through two hundred files. Trap: Reviewing two hundred cutouts in file order means the five bad ones are found by someone else after publication. Sort by a signal that correlates with difficulty, because the failures cluster at one end and reviewing the top twenty catches most of them. Done when: Each preset produces exactly the pixel dimensions it names. The review view puts genuinely problematic cutouts near the top, verified against a set you have marked by hand. Marking an image as needing rework excludes it from the export until it is redone. Do not build yet: Model licensing and distribution come last. ### Phase 6 · Model licences and running it for others Build: Record the licence of every model and every background asset. If you expose the pipeline as a service, add authentication, limits and a queue, because segmentation is compute and an open endpoint is a free processor. Trap: Model licences in this area vary and some permit research use only, which is not the same as commercial product photography. Check each one specifically rather than assuming that open source means unrestricted, and record what you found. Done when: Every bundled model and background asset has a licence recorded with what it permits. An exposed endpoint requires authentication and enforces a per-user job limit. A queue prevents concurrent jobs from exhausting memory, verified by submitting more jobs than the limit. Do not build yet: Nothing after this. ### Out of scope (and why) - The specialised vision models behind the commercial product, which are trained on product photography specifically and beat general segmentation on exactly the cases that matter. - Mobile capture with live preview, which is most of how the paid application is used. - The template library and the generated backgrounds, which are content and model access. - High volume hosted processing. You have one machine, and segmentation is not cheap. ### README must contain - Which model is used and why, with the subject types it was chosen for - That compositing happens in linear light, and what a gamma-space composite looks like - That framing anchors to a baseline rather than centring, and why a grid makes that obvious - That every output carries a settings hash and reruns are byte-identical - Every bundled model and asset licence, and whether commercial use is permitted ===== AGENTS.md ===== # Agent instructions · PhotoRoom indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Python 3.12, rembg for speed, BiRefNet for edge quality, behind one interface, Pillow with alpha compositing in linear light, Subject bounding box from the alpha mask, then padding rules, Sources untouched, outputs plus a settings hash per run. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Comparing composites on a white background hides almost every edge failure, because a soft or wrong edge against white looks fine. Look at the alpha mask itself, and composite onto a saturated colour where a bad edge is obvious. - Alpha compositing in gamma-encoded space produces a dark fringe around every soft edge, which is subtle on white and glaring on a coloured background. Convert to linear light first, composite, then convert back. This one step is the difference between output that looks composited and output that looks photographed. - Centring the bounding box vertically makes tall and short products look like they are floating at different heights when placed side by side in a grid. Anchor to a baseline instead, so everything sits on the same line, which is what makes a catalogue look deliberate. - A pipeline with any nondeterminism, such as a model running with a random seed or a resize using a nondeterministic path, cannot be reproduced, so a catalogue cannot be corrected image by image later. Pin the seed, pin the resampling filter, and verify by running twice and comparing bytes. - Reviewing two hundred cutouts in file order means the five bad ones are found by someone else after publication. Sort by a signal that correlates with difficulty, because the failures cluster at one end and reviewing the top twenty catches most of them. ===== BUILD_PLAN.md ===== # Build plan · PhotoRoom A pipeline that takes a folder of product photographs, cuts out the subject with a segmentation model, composites onto a chosen background, applies consistent framing and padding, and exports at the exact sizes each destination needs, with every output reproducible from the source and a settings file. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · One cutout, and an honest look at the edges A single image is cut out, and you can see exactly where the model struggles. ### Steps 1. Run both models on one image Save the alpha mask as its own file. The mask is what you are actually evaluating. ```sh pip install rembg pillow rembg i input.jpg output.png ``` 2. Composite onto a hostile background A saturated colour, not white. Every edge problem becomes visible immediately. Files: `src/compare.py` 3. Write down the model choice With the reason and the subject type. Hard-edged products and hair are genuinely different problems. Files: `docs/model-choice.md` ### Done when - [ ] Both models produce a cutout and a separate alpha mask - [ ] Edge differences are visible on a saturated background - [ ] The model choice is recorded with its reasoning - [ ] Fine detail such as hair is inspected at full resolution ### Watch out - Comparing composites on a white background hides almost every edge failure, because a soft or wrong edge against white looks fine. Look at the alpha mask itself, and composite onto a saturated colour where a bad edge is obvious. ## Phase 2 · Compositing without fringes The cutout sits on a new background with no dark halo around its edges. ### Steps 1. Convert to linear before compositing Undo the transfer function, composite, reapply. Do it once in a helper so no code path can skip it. Files: `src/composite.py` 2. Handle partial alpha correctly Premultiplied or straight alpha, chosen deliberately and applied consistently. Mixing the two is how fringes reappear. Files: `src/composite.py` 3. Test on light and dark The same subject on both. A fringe visible on one and not the other means the conversion is half applied. ### Done when - [ ] No dark fringe on a mid-grey background at full resolution - [ ] Light and dark backgrounds both look correct - [ ] Two-step compositing matches direct compositing - [ ] Alpha convention is consistent across every code path ### Watch out - Alpha compositing in gamma-encoded space produces a dark fringe around every soft edge, which is subtle on white and glaring on a coloured background. Convert to linear light first, composite, then convert back. This one step is the difference between output that looks composited and output that looks photographed. ## Phase 3 · Consistent framing across a catalogue Fifty products photographed at different distances end up looking like one set. ### Steps 1. Derive the bounding box from alpha Threshold the mask and find the tight bounds. Ignore isolated specks, which are usually mask noise rather than subject. Files: `src/frame.py` 2. Scale to a target subject proportion The subject occupies a chosen fraction of the frame. This is what makes different photographs match. Files: `src/frame.py` 3. Anchor to a baseline Horizontally centred, vertically sitting on a line. Compare a grid of outputs before and after to see why. Files: `src/frame.py` ### Done when - [ ] Subjects occupy the same frame proportion across fifty images - [ ] A grid of outputs shows subjects on a common baseline - [ ] A subject touching the source edge produces a warning - [ ] Mask specks do not distort the bounding box ### Watch out - Centring the bounding box vertically makes tall and short products look like they are floating at different heights when placed side by side in a grid. Anchor to a baseline instead, so everything sits on the same line, which is what makes a catalogue look deliberate. ## Phase 4 · Batch that is reproducible A whole folder processes unattended, and any output can be regenerated exactly. ### Steps 1. Hash the settings Every setting that affects output goes into the hash. If changing something does not change the hash, the hash is wrong. Files: `src/settings.py` 2. Make the pipeline deterministic Pin seeds and resampling filters. Run twice and compare bytes as a test. Files: `tests/test_determinism.py` 3. Isolate failures One unreadable file must not stop a run of two hundred. Report it and continue. Files: `src/batch.py` ### Done when - [ ] A hundred images process unattended - [ ] Rerunning produces byte-identical outputs - [ ] A corrupt image is reported without stopping the run - [ ] Every output records its settings hash ### Watch out - A pipeline with any nondeterminism, such as a model running with a random seed or a resize using a nondeterministic path, cannot be reproduced, so a catalogue cannot be corrected image by image later. Pin the seed, pin the resampling filter, and verify by running twice and comparing bytes. ## Phase 5 · Export presets and a review pass Each destination gets the size it needs, and the ones the model got wrong are easy to find. ### Steps 1. Add presets with asserted dimensions Named sizes per destination, with a test asserting output dimensions rather than trusting arithmetic. Files: `src/presets.py` 2. Compute a difficulty signal Alpha coverage, edge perimeter relative to area, and the count of partially transparent pixels. Sort by it. Files: `src/review.py` 3. Validate the sort against hand marking Mark twenty by hand, then check whether the sort surfaces them. If it does not, the signal is wrong. ### Done when - [ ] Every preset produces exactly the dimensions it names - [ ] The review sort surfaces hand-marked problem images near the top - [ ] Marking for rework excludes an image from export - [ ] The review view shows the alpha mask, not only the composite ### Watch out - Reviewing two hundred cutouts in file order means the five bad ones are found by someone else after publication. Sort by a signal that correlates with difficulty, because the failures cluster at one end and reviewing the top twenty catches most of them. ## Not in this build - Product-specific vision models trained for this exact task. - Mobile capture with live preview. - Template and generated background libraries. - High volume hosted processing. ## After v1, if you want it - Shadow generation, which is what makes a composite look photographed rather than pasted - Automatic colour consistency across a catalogue shot under different lighting - A manual mask touch-up tool for the few images the review pass surfaces ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Which model to use. Chosen by the comparison in Phase 1, not by reputation. SEGMENTATION_MODEL=rembg # Required. Input directory. Never written to. SOURCE_DIR=./photos/source # Required. Where composites and masks are written. OUTPUT_DIR=./photos/out # Required. What proportion of the frame the subject fills. The single most important setting for a consistent catalogue. SUBJECT_FRAME_FRACTION=0.78 # Required. Where the subject's bottom edge sits vertically. Anchoring here rather than centring is what aligns a grid. BASELINE_FRACTION=0.88 # Required. Concurrent images. Too high exhausts memory on a GPU faster than you expect. BATCH_CONCURRENCY=2
You are building a production product version of PhotoRoom. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== PRODUCT.md ===== # PhotoRoom · product brief ## Problem The core loop is buildable, but a dependable replacement becomes a real weekend or multi-day project. For PhotoRoom, remove or replace backgrounds and batch-export consistent product images. The hard boundary is specialized vision models, mobile capture, templates, and high-volume apis, plus frontier models, compute, and data. ## Product outcome A pipeline that produces a consistent catalogue from inconsistent photographs, composites without fringes, reruns byte-identically, surfaces its own likely failures for review, and ships only models you have checked you may use. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - GPU-capable machine or user-supplied generation API - ComfyUI - model files with appropriate licenses - local storage ## Explicit non-goals for v1 - Product-specific vision models trained for this exact task. - Mobile capture with live preview. - Template and generated background libraries. - High volume hosted processing. - specialized vision models, mobile capture, templates, and high-volume APIs - frontier proprietary models - hosted GPU capacity - licensed training data - moderation and fast global delivery ## Success criteria - No fringe on the fine-detail fixture at full resolution - Reruns are byte-identical - The review sort surfaces the hand-marked problem images - Every preset's dimensions are asserted by test - Every model and background licence permits the intended use ===== BRIEF.md ===== # Build brief · PhotoRoom The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a batch background remover and product image pipeline running on your own machine to replace PhotoRoom. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Do not train a segmentation model. Two strong open ones exist and the work worth doing is the pipeline around them: consistent framing, reproducible output, and a batch that does not need supervision. Choose the model deliberately. rembg is the fast, dependency-light option that installs and runs, and its ceiling is edge quality. BiRefNet is a high-resolution model with sub-pixel alpha matting that handles hair, fur and lace far better, at a real compute cost. If your products have hard edges, the cheap option is genuinely enough. Checked September 2026. ### Stack (fixed, do not substitute) - Runtime: Python 3.12 · the image and model ecosystem lives here and fighting that is pointless - Segmentation: rembg for speed, BiRefNet for edge quality, behind one interface · the right choice depends on your subjects, so make it a setting rather than a decision - Compositing: Pillow with alpha compositing in linear light · compositing in the usual gamma-encoded space produces dark fringes around every edge - Framing: Subject bounding box from the alpha mask, then padding rules · consistent framing across a catalogue is what makes a set look professional - Storage: Sources untouched, outputs plus a settings hash per run · a catalogue you cannot regenerate is a catalogue you cannot correct ### Data model (create this before Phase 1) - runs(id, at, settings_hash, model, source_dir, output_dir) - images(id, run_id, source_path, output_path, subject_bbox_json, alpha_coverage, warnings_json) - settings(id, hash, model, background_json, padding_json, output_presets_json) · hashed so an output can always be traced to how it was made - The settings hash on every output is what makes a catalogue reproducible: the same sources and the same hash produce the same images. ### Phase 1 · One cutout, and an honest look at the edges Build: Run one image through rembg and save the alpha mask separately from the composite. Then run the same image through BiRefNet. Compare the masks at the edges, zoomed in, rather than comparing the composites at normal size. Trap: Comparing composites on a white background hides almost every edge failure, because a soft or wrong edge against white looks fine. Look at the alpha mask itself, and composite onto a saturated colour where a bad edge is obvious. Done when: Both models produce a cutout of the same image and their alpha masks are saved separately. Compositing each onto a saturated background shows the edge quality difference clearly. The choice of model for your subjects is written down with the reason. Do not build yet: No batch, no framing, no export presets. ### Phase 2 · Compositing without fringes Build: Composite in linear light: convert to linear, composite with the alpha mask, convert back. Handle partially transparent pixels correctly. Test against a subject with fine detail on both a light and a dark background. Trap: Alpha compositing in gamma-encoded space produces a dark fringe around every soft edge, which is subtle on white and glaring on a coloured background. Convert to linear light first, composite, then convert back. This one step is the difference between output that looks composited and output that looks photographed. Done when: A cutout with fine edge detail composited onto a mid-grey background shows no dark fringe, checked at full resolution. The same cutout on a light and a dark background both look correct. Compositing onto a transparent background and then onto a colour gives the same result as compositing directly. Do not build yet: No framing or batch yet. ### Phase 3 · Consistent framing across a catalogue Build: Find the subject bounding box from the alpha mask. Scale so the subject occupies a chosen proportion of the frame, and pad to the target aspect ratio. Anchor consistently, usually centred horizontally and sitting on a baseline vertically. Trap: Centring the bounding box vertically makes tall and short products look like they are floating at different heights when placed side by side in a grid. Anchor to a baseline instead, so everything sits on the same line, which is what makes a catalogue look deliberate. Done when: Fifty images of different subject sizes produce outputs where the subject occupies the same proportion of the frame. Placed in a grid, subjects sit on a common baseline rather than floating. A subject touching the edge of its source frame is detected and warned about rather than silently cropped. Do not build yet: No batch reproducibility or export presets yet. ### Phase 4 · Batch that is reproducible Build: Process a directory as a run. Hash the settings and record that hash with every output. Continue past a failing image and report it. Make a rerun with the same settings produce byte-identical outputs. Trap: A pipeline with any nondeterminism, such as a model running with a random seed or a resize using a nondeterministic path, cannot be reproduced, so a catalogue cannot be corrected image by image later. Pin the seed, pin the resampling filter, and verify by running twice and comparing bytes. Done when: A folder of a hundred images processes unattended. Rerunning with the same settings produces byte-identical outputs. A corrupt image is reported and does not stop the run. Every output's settings hash matches the run that produced it. Do not build yet: No export presets or review pass yet. ### Phase 5 · Export presets and a review pass Build: Named export presets with exact pixel dimensions and format. A review view sorting outputs by a confidence signal such as alpha coverage or edge complexity, so the likely failures are at the top rather than scattered through two hundred files. Trap: Reviewing two hundred cutouts in file order means the five bad ones are found by someone else after publication. Sort by a signal that correlates with difficulty, because the failures cluster at one end and reviewing the top twenty catches most of them. Done when: Each preset produces exactly the pixel dimensions it names. The review view puts genuinely problematic cutouts near the top, verified against a set you have marked by hand. Marking an image as needing rework excludes it from the export until it is redone. Do not build yet: Model licensing and distribution come last. ### Phase 6 · Model licences and running it for others Build: Record the licence of every model and every background asset. If you expose the pipeline as a service, add authentication, limits and a queue, because segmentation is compute and an open endpoint is a free processor. Trap: Model licences in this area vary and some permit research use only, which is not the same as commercial product photography. Check each one specifically rather than assuming that open source means unrestricted, and record what you found. Done when: Every bundled model and background asset has a licence recorded with what it permits. An exposed endpoint requires authentication and enforces a per-user job limit. A queue prevents concurrent jobs from exhausting memory, verified by submitting more jobs than the limit. Do not build yet: Nothing after this. ### Out of scope (and why) - The specialised vision models behind the commercial product, which are trained on product photography specifically and beat general segmentation on exactly the cases that matter. - Mobile capture with live preview, which is most of how the paid application is used. - The template library and the generated backgrounds, which are content and model access. - High volume hosted processing. You have one machine, and segmentation is not cheap. ### README must contain - Which model is used and why, with the subject types it was chosen for - That compositing happens in linear light, and what a gamma-space composite looks like - That framing anchors to a baseline rather than centring, and why a grid makes that obvious - That every output carries a settings hash and reruns are byte-identical - Every bundled model and asset licence, and whether commercial use is permitted ===== ARCHITECTURE.md ===== # Architecture · PhotoRoom ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | Python 3.12 | the image and model ecosystem lives here and fighting that is pointless | | Segmentation | rembg for speed, BiRefNet for edge quality, behind one interface | the right choice depends on your subjects, so make it a setting rather than a decision | | Compositing | Pillow with alpha compositing in linear light | compositing in the usual gamma-encoded space produces dark fringes around every edge | | Framing | Subject bounding box from the alpha mask, then padding rules | consistent framing across a catalogue is what makes a set look professional | | Storage | Sources untouched, outputs plus a settings hash per run | a catalogue you cannot regenerate is a catalogue you cannot correct | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Segmentation | Producing an alpha mask. | Both models sit behind one interface, so swapping is a setting. The mask is the contract. | | Composite | Combining subject and background correctly. | The linear light rule is a correctness property and must survive any change. | | Frame | Consistent scaling and anchoring. | Pure geometry from the bounding box, testable without any model at all. | | Batch | Runs, determinism and failure isolation. | The settings hash is what makes the catalogue correctable later, so it stays regardless of implementation. | | Review | Surfacing likely failures. | The difficulty signal can improve; the principle that failures are sorted to the top rather than found by a customer should not. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `SEGMENTATION_MODEL` · required · Which model to use. Chosen by the comparison in Phase 1, not by reputation. - `SOURCE_DIR` · required · Input directory. Never written to. - `OUTPUT_DIR` · required · Where composites and masks are written. - `SUBJECT_FRAME_FRACTION` · required · What proportion of the frame the subject fills. The single most important setting for a consistent catalogue. - `BASELINE_FRACTION` · required · Where the subject's bottom edge sits vertically. Anchoring here rather than centring is what aligns a grid. - `BATCH_CONCURRENCY` · required · Concurrent images. Too high exhausts memory on a GPU faster than you expect. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it. ===== AGENTS.md ===== # Agent instructions · PhotoRoom product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: Python 3.12, rembg for speed, BiRefNet for edge quality, behind one interface, Pillow with alpha compositing in linear light, Subject bounding box from the alpha mask, then padding rules, Sources untouched, outputs plus a settings hash per run. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - Comparing composites on a white background hides almost every edge failure, because a soft or wrong edge against white looks fine. Look at the alpha mask itself, and composite onto a saturated colour where a bad edge is obvious. - Alpha compositing in gamma-encoded space produces a dark fringe around every soft edge, which is subtle on white and glaring on a coloured background. Convert to linear light first, composite, then convert back. This one step is the difference between output that looks composited and output that looks photographed. - Centring the bounding box vertically makes tall and short products look like they are floating at different heights when placed side by side in a grid. Anchor to a baseline instead, so everything sits on the same line, which is what makes a catalogue look deliberate. - A pipeline with any nondeterminism, such as a model running with a random seed or a resize using a nondeterministic path, cannot be reproduced, so a catalogue cannot be corrected image by image later. Pin the seed, pin the resampling filter, and verify by running twice and comparing bytes. - Reviewing two hundred cutouts in file order means the five bad ones are found by someone else after publication. Sort by a signal that correlates with difficulty, because the failures cluster at one end and reviewing the top twenty catches most of them. - Model licences in this area vary and some permit research use only, which is not the same as commercial product photography. Check each one specifically rather than assuming that open source means unrestricted, and record what you found. ===== MILESTONES.md ===== # Delivery milestones · PhotoRoom Estimated effort: **weekend** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · One cutout, and an honest look at the edges A single image is cut out, and you can see exactly where the model struggles. ### Steps 1. Run both models on one image Save the alpha mask as its own file. The mask is what you are actually evaluating. ```sh pip install rembg pillow rembg i input.jpg output.png ``` 2. Composite onto a hostile background A saturated colour, not white. Every edge problem becomes visible immediately. Files: `src/compare.py` 3. Write down the model choice With the reason and the subject type. Hard-edged products and hair are genuinely different problems. Files: `docs/model-choice.md` ### Done when - [ ] Both models produce a cutout and a separate alpha mask - [ ] Edge differences are visible on a saturated background - [ ] The model choice is recorded with its reasoning - [ ] Fine detail such as hair is inspected at full resolution ### Watch out - Comparing composites on a white background hides almost every edge failure, because a soft or wrong edge against white looks fine. Look at the alpha mask itself, and composite onto a saturated colour where a bad edge is obvious. ## M2 · Compositing without fringes The cutout sits on a new background with no dark halo around its edges. ### Steps 1. Convert to linear before compositing Undo the transfer function, composite, reapply. Do it once in a helper so no code path can skip it. Files: `src/composite.py` 2. Handle partial alpha correctly Premultiplied or straight alpha, chosen deliberately and applied consistently. Mixing the two is how fringes reappear. Files: `src/composite.py` 3. Test on light and dark The same subject on both. A fringe visible on one and not the other means the conversion is half applied. ### Done when - [ ] No dark fringe on a mid-grey background at full resolution - [ ] Light and dark backgrounds both look correct - [ ] Two-step compositing matches direct compositing - [ ] Alpha convention is consistent across every code path ### Watch out - Alpha compositing in gamma-encoded space produces a dark fringe around every soft edge, which is subtle on white and glaring on a coloured background. Convert to linear light first, composite, then convert back. This one step is the difference between output that looks composited and output that looks photographed. ## M3 · Consistent framing across a catalogue Fifty products photographed at different distances end up looking like one set. ### Steps 1. Derive the bounding box from alpha Threshold the mask and find the tight bounds. Ignore isolated specks, which are usually mask noise rather than subject. Files: `src/frame.py` 2. Scale to a target subject proportion The subject occupies a chosen fraction of the frame. This is what makes different photographs match. Files: `src/frame.py` 3. Anchor to a baseline Horizontally centred, vertically sitting on a line. Compare a grid of outputs before and after to see why. Files: `src/frame.py` ### Done when - [ ] Subjects occupy the same frame proportion across fifty images - [ ] A grid of outputs shows subjects on a common baseline - [ ] A subject touching the source edge produces a warning - [ ] Mask specks do not distort the bounding box ### Watch out - Centring the bounding box vertically makes tall and short products look like they are floating at different heights when placed side by side in a grid. Anchor to a baseline instead, so everything sits on the same line, which is what makes a catalogue look deliberate. ## M4 · Batch that is reproducible A whole folder processes unattended, and any output can be regenerated exactly. ### Steps 1. Hash the settings Every setting that affects output goes into the hash. If changing something does not change the hash, the hash is wrong. Files: `src/settings.py` 2. Make the pipeline deterministic Pin seeds and resampling filters. Run twice and compare bytes as a test. Files: `tests/test_determinism.py` 3. Isolate failures One unreadable file must not stop a run of two hundred. Report it and continue. Files: `src/batch.py` ### Done when - [ ] A hundred images process unattended - [ ] Rerunning produces byte-identical outputs - [ ] A corrupt image is reported without stopping the run - [ ] Every output records its settings hash ### Watch out - A pipeline with any nondeterminism, such as a model running with a random seed or a resize using a nondeterministic path, cannot be reproduced, so a catalogue cannot be corrected image by image later. Pin the seed, pin the resampling filter, and verify by running twice and comparing bytes. ## M5 · Export presets and a review pass Each destination gets the size it needs, and the ones the model got wrong are easy to find. ### Steps 1. Add presets with asserted dimensions Named sizes per destination, with a test asserting output dimensions rather than trusting arithmetic. Files: `src/presets.py` 2. Compute a difficulty signal Alpha coverage, edge perimeter relative to area, and the count of partially transparent pixels. Sort by it. Files: `src/review.py` 3. Validate the sort against hand marking Mark twenty by hand, then check whether the sort surfaces them. If it does not, the signal is wrong. ### Done when - [ ] Every preset produces exactly the dimensions it names - [ ] The review sort surfaces hand-marked problem images near the top - [ ] Marking for rework excludes an image from export - [ ] The review view shows the alpha mask, not only the composite ### Watch out - Reviewing two hundred cutouts in file order means the five bad ones are found by someone else after publication. Sort by a signal that correlates with difficulty, because the failures cluster at one end and reviewing the top twenty catches most of them. ## M6 · Model licences and running it for others (production only) Everything you ship or serve is something you are allowed to ship or serve. ### Steps 1. Record every licence Model, source, licence and whether commercial use is permitted. One table, checked rather than assumed. Files: `docs/licences.md` 2. Authenticate and queue Authentication before any job is accepted, and a queue with a concurrency limit sized to your memory. Files: `src/server.py` 3. Test the limit by exceeding it Submit more jobs than the limit and confirm queuing rather than memory exhaustion. ### Done when - [ ] Every model and asset licence is recorded and permits your use - [ ] An exposed endpoint requires authentication - [ ] Exceeding the concurrency limit queues rather than exhausting memory - [ ] Per-user job limits are enforced ### Watch out - Model licences in this area vary and some permit research use only, which is not the same as commercial product photography. Check each one specifically rather than assuming that open source means unrestricted, and record what you found. ===== OPERATIONS.md ===== # Operations · PhotoRoom ## Backup Source photographs are the irreplaceable data. Outputs are regenerable from sources plus a settings hash, which is the practical payoff of determinism. ## Restore Restore sources and settings, then rerun. Byte-identical output is what makes this a restore rather than a reprocessing with different results. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring For batch use, watch memory during processing and the per-run failure count. If exposed as a service, queue depth is the number that matters, because segmentation jobs are long enough to pile up. ## Incident checklist A catalogue processed with wrong settings is fully recoverable because sources are untouched and outputs are reproducible. This is the reason for both rules and it will justify itself the first time a framing decision changes. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] No fringe on the fine-detail fixture at full resolution - [ ] Reruns are byte-identical - [ ] The review sort surfaces the hand-marked problem images - [ ] Every preset's dimensions are asserted by test - [ ] Every model and background licence permits the intended use ## Launch constraint Do not market omitted PhotoRoom capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered. ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Which model to use. Chosen by the comparison in Phase 1, not by reputation. SEGMENTATION_MODEL=rembg # Required. Input directory. Never written to. SOURCE_DIR=./photos/source # Required. Where composites and masks are written. OUTPUT_DIR=./photos/out # Required. What proportion of the frame the subject fills. The single most important setting for a consistent catalogue. SUBJECT_FRAME_FRACTION=0.78 # Required. Where the subject's bottom edge sits vertically. Anchoring here rather than centring is what aligns a grid. BASELINE_FRACTION=0.88 # Required. Concurrent images. Too high exhausts memory on a GPU faster than you expect. BATCH_CONCURRENCY=2
# PhotoRoom · indie build A pipeline that takes a folder of product photographs, cuts out the subject with a segmentation model, composites onto a chosen background, applies consistent framing and padding, and exports at the exact sizes each destination needs, with every output reproducible from the source and a settings file. Estimated effort: **weekend**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | Python 3.12 | the image and model ecosystem lives here and fighting that is pointless | | Segmentation | rembg for speed, BiRefNet for edge quality, behind one interface | the right choice depends on your subjects, so make it a setting rather than a decision | | Compositing | Pillow with alpha compositing in linear light | compositing in the usual gamma-encoded space produces dark fringes around every edge | | Framing | Subject bounding box from the alpha mask, then padding rules | consistent framing across a catalogue is what makes a set look professional | | Storage | Sources untouched, outputs plus a settings hash per run | a catalogue you cannot regenerate is a catalogue you cannot correct | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Python 3.12 or newer** · free - Why: Runs the pipeline and hosts the model runtimes. - Get it: python.org or your package manager, then a virtual environment. - Verify: python3 --version prints 3.12 or higher - [ ] **A machine that can run segmentation, ideally with a GPU** · free if you have suitable hardware; renting GPU time is the alternative and has a per-hour cost - Why: rembg runs acceptably on a processor; higher quality models are much slower without a GPU, which changes whether batch processing is practical. - Get it: Check what you have first. Time one image through both models before committing to a model for a two hundred image catalogue. - Verify: One image through the chosen model completes in a time you can multiply by your catalogue size - [ ] **Segmentation model files with licences checked** · free to download; check commercial use terms individually - Why: They are the engine, and their licences differ in ways that matter for commercial product photography. - Get it: rembg downloads its models on first use. Higher quality models are separate downloads. Read each licence before using it commercially rather than after. - Verify: Each model runs and its licence is recorded - [ ] **Twenty real product photographs** · free - Why: Including at least one with hair or fur, one on a busy background and one where the subject touches the frame edge. Clean studio shots hide every problem this pipeline exists to solve. - Get it: Use your own catalogue. If you do not have one, this build has no use case yet and the pipeline decisions cannot be made well. - Verify: Twenty images are present with the three hard cases included - [ ] **Background assets you are licensed to use** · free for generated backgrounds - Why: A composited background is redistributed with every product image you publish. - Get it: Solid colours and gradients you generate are safest. If you use photographs, check the licence permits commercial redistribution as part of a composite. - Verify: Every background asset's licence is recorded ## Quick start ```sh pip install rembg pillow rembg i input.jpg output.png ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Product-specific vision models trained for this exact task. - Mobile capture with live preview. - Template and generated background libraries. - High volume hosted processing. - specialized vision models, mobile capture, templates, and high-volume APIs - frontier proprietary models - hosted GPU capacity - licensed training data - moderation and fast global delivery If one of those is essential to you, that is the reason to keep paying for PhotoRoom, and the README should say so rather than pretend.
# Build brief · PhotoRoom The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a batch background remover and product image pipeline running on your own machine to replace PhotoRoom. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Do not train a segmentation model. Two strong open ones exist and the work worth doing is the pipeline around them: consistent framing, reproducible output, and a batch that does not need supervision. Choose the model deliberately. rembg is the fast, dependency-light option that installs and runs, and its ceiling is edge quality. BiRefNet is a high-resolution model with sub-pixel alpha matting that handles hair, fur and lace far better, at a real compute cost. If your products have hard edges, the cheap option is genuinely enough. Checked September 2026. ### Stack (fixed, do not substitute) - Runtime: Python 3.12 · the image and model ecosystem lives here and fighting that is pointless - Segmentation: rembg for speed, BiRefNet for edge quality, behind one interface · the right choice depends on your subjects, so make it a setting rather than a decision - Compositing: Pillow with alpha compositing in linear light · compositing in the usual gamma-encoded space produces dark fringes around every edge - Framing: Subject bounding box from the alpha mask, then padding rules · consistent framing across a catalogue is what makes a set look professional - Storage: Sources untouched, outputs plus a settings hash per run · a catalogue you cannot regenerate is a catalogue you cannot correct ### Data model (create this before Phase 1) - runs(id, at, settings_hash, model, source_dir, output_dir) - images(id, run_id, source_path, output_path, subject_bbox_json, alpha_coverage, warnings_json) - settings(id, hash, model, background_json, padding_json, output_presets_json) · hashed so an output can always be traced to how it was made - The settings hash on every output is what makes a catalogue reproducible: the same sources and the same hash produce the same images. ### Phase 1 · One cutout, and an honest look at the edges Build: Run one image through rembg and save the alpha mask separately from the composite. Then run the same image through BiRefNet. Compare the masks at the edges, zoomed in, rather than comparing the composites at normal size. Trap: Comparing composites on a white background hides almost every edge failure, because a soft or wrong edge against white looks fine. Look at the alpha mask itself, and composite onto a saturated colour where a bad edge is obvious. Done when: Both models produce a cutout of the same image and their alpha masks are saved separately. Compositing each onto a saturated background shows the edge quality difference clearly. The choice of model for your subjects is written down with the reason. Do not build yet: No batch, no framing, no export presets. ### Phase 2 · Compositing without fringes Build: Composite in linear light: convert to linear, composite with the alpha mask, convert back. Handle partially transparent pixels correctly. Test against a subject with fine detail on both a light and a dark background. Trap: Alpha compositing in gamma-encoded space produces a dark fringe around every soft edge, which is subtle on white and glaring on a coloured background. Convert to linear light first, composite, then convert back. This one step is the difference between output that looks composited and output that looks photographed. Done when: A cutout with fine edge detail composited onto a mid-grey background shows no dark fringe, checked at full resolution. The same cutout on a light and a dark background both look correct. Compositing onto a transparent background and then onto a colour gives the same result as compositing directly. Do not build yet: No framing or batch yet. ### Phase 3 · Consistent framing across a catalogue Build: Find the subject bounding box from the alpha mask. Scale so the subject occupies a chosen proportion of the frame, and pad to the target aspect ratio. Anchor consistently, usually centred horizontally and sitting on a baseline vertically. Trap: Centring the bounding box vertically makes tall and short products look like they are floating at different heights when placed side by side in a grid. Anchor to a baseline instead, so everything sits on the same line, which is what makes a catalogue look deliberate. Done when: Fifty images of different subject sizes produce outputs where the subject occupies the same proportion of the frame. Placed in a grid, subjects sit on a common baseline rather than floating. A subject touching the edge of its source frame is detected and warned about rather than silently cropped. Do not build yet: No batch reproducibility or export presets yet. ### Phase 4 · Batch that is reproducible Build: Process a directory as a run. Hash the settings and record that hash with every output. Continue past a failing image and report it. Make a rerun with the same settings produce byte-identical outputs. Trap: A pipeline with any nondeterminism, such as a model running with a random seed or a resize using a nondeterministic path, cannot be reproduced, so a catalogue cannot be corrected image by image later. Pin the seed, pin the resampling filter, and verify by running twice and comparing bytes. Done when: A folder of a hundred images processes unattended. Rerunning with the same settings produces byte-identical outputs. A corrupt image is reported and does not stop the run. Every output's settings hash matches the run that produced it. Do not build yet: No export presets or review pass yet. ### Phase 5 · Export presets and a review pass Build: Named export presets with exact pixel dimensions and format. A review view sorting outputs by a confidence signal such as alpha coverage or edge complexity, so the likely failures are at the top rather than scattered through two hundred files. Trap: Reviewing two hundred cutouts in file order means the five bad ones are found by someone else after publication. Sort by a signal that correlates with difficulty, because the failures cluster at one end and reviewing the top twenty catches most of them. Done when: Each preset produces exactly the pixel dimensions it names. The review view puts genuinely problematic cutouts near the top, verified against a set you have marked by hand. Marking an image as needing rework excludes it from the export until it is redone. Do not build yet: Model licensing and distribution come last. ### Phase 6 · Model licences and running it for others Build: Record the licence of every model and every background asset. If you expose the pipeline as a service, add authentication, limits and a queue, because segmentation is compute and an open endpoint is a free processor. Trap: Model licences in this area vary and some permit research use only, which is not the same as commercial product photography. Check each one specifically rather than assuming that open source means unrestricted, and record what you found. Done when: Every bundled model and background asset has a licence recorded with what it permits. An exposed endpoint requires authentication and enforces a per-user job limit. A queue prevents concurrent jobs from exhausting memory, verified by submitting more jobs than the limit. Do not build yet: Nothing after this. ### Out of scope (and why) - The specialised vision models behind the commercial product, which are trained on product photography specifically and beat general segmentation on exactly the cases that matter. - Mobile capture with live preview, which is most of how the paid application is used. - The template library and the generated backgrounds, which are content and model access. - High volume hosted processing. You have one machine, and segmentation is not cheap. ### README must contain - Which model is used and why, with the subject types it was chosen for - That compositing happens in linear light, and what a gamma-space composite looks like - That framing anchors to a baseline rather than centring, and why a grid makes that obvious - That every output carries a settings hash and reruns are byte-identical - Every bundled model and asset licence, and whether commercial use is permitted
# Agent instructions · PhotoRoom indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Python 3.12, rembg for speed, BiRefNet for edge quality, behind one interface, Pillow with alpha compositing in linear light, Subject bounding box from the alpha mask, then padding rules, Sources untouched, outputs plus a settings hash per run. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Comparing composites on a white background hides almost every edge failure, because a soft or wrong edge against white looks fine. Look at the alpha mask itself, and composite onto a saturated colour where a bad edge is obvious. - Alpha compositing in gamma-encoded space produces a dark fringe around every soft edge, which is subtle on white and glaring on a coloured background. Convert to linear light first, composite, then convert back. This one step is the difference between output that looks composited and output that looks photographed. - Centring the bounding box vertically makes tall and short products look like they are floating at different heights when placed side by side in a grid. Anchor to a baseline instead, so everything sits on the same line, which is what makes a catalogue look deliberate. - A pipeline with any nondeterminism, such as a model running with a random seed or a resize using a nondeterministic path, cannot be reproduced, so a catalogue cannot be corrected image by image later. Pin the seed, pin the resampling filter, and verify by running twice and comparing bytes. - Reviewing two hundred cutouts in file order means the five bad ones are found by someone else after publication. Sort by a signal that correlates with difficulty, because the failures cluster at one end and reviewing the top twenty catches most of them.
# Build plan · PhotoRoom A pipeline that takes a folder of product photographs, cuts out the subject with a segmentation model, composites onto a chosen background, applies consistent framing and padding, and exports at the exact sizes each destination needs, with every output reproducible from the source and a settings file. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · One cutout, and an honest look at the edges A single image is cut out, and you can see exactly where the model struggles. ### Steps 1. Run both models on one image Save the alpha mask as its own file. The mask is what you are actually evaluating. ```sh pip install rembg pillow rembg i input.jpg output.png ``` 2. Composite onto a hostile background A saturated colour, not white. Every edge problem becomes visible immediately. Files: `src/compare.py` 3. Write down the model choice With the reason and the subject type. Hard-edged products and hair are genuinely different problems. Files: `docs/model-choice.md` ### Done when - [ ] Both models produce a cutout and a separate alpha mask - [ ] Edge differences are visible on a saturated background - [ ] The model choice is recorded with its reasoning - [ ] Fine detail such as hair is inspected at full resolution ### Watch out - Comparing composites on a white background hides almost every edge failure, because a soft or wrong edge against white looks fine. Look at the alpha mask itself, and composite onto a saturated colour where a bad edge is obvious. ## Phase 2 · Compositing without fringes The cutout sits on a new background with no dark halo around its edges. ### Steps 1. Convert to linear before compositing Undo the transfer function, composite, reapply. Do it once in a helper so no code path can skip it. Files: `src/composite.py` 2. Handle partial alpha correctly Premultiplied or straight alpha, chosen deliberately and applied consistently. Mixing the two is how fringes reappear. Files: `src/composite.py` 3. Test on light and dark The same subject on both. A fringe visible on one and not the other means the conversion is half applied. ### Done when - [ ] No dark fringe on a mid-grey background at full resolution - [ ] Light and dark backgrounds both look correct - [ ] Two-step compositing matches direct compositing - [ ] Alpha convention is consistent across every code path ### Watch out - Alpha compositing in gamma-encoded space produces a dark fringe around every soft edge, which is subtle on white and glaring on a coloured background. Convert to linear light first, composite, then convert back. This one step is the difference between output that looks composited and output that looks photographed. ## Phase 3 · Consistent framing across a catalogue Fifty products photographed at different distances end up looking like one set. ### Steps 1. Derive the bounding box from alpha Threshold the mask and find the tight bounds. Ignore isolated specks, which are usually mask noise rather than subject. Files: `src/frame.py` 2. Scale to a target subject proportion The subject occupies a chosen fraction of the frame. This is what makes different photographs match. Files: `src/frame.py` 3. Anchor to a baseline Horizontally centred, vertically sitting on a line. Compare a grid of outputs before and after to see why. Files: `src/frame.py` ### Done when - [ ] Subjects occupy the same frame proportion across fifty images - [ ] A grid of outputs shows subjects on a common baseline - [ ] A subject touching the source edge produces a warning - [ ] Mask specks do not distort the bounding box ### Watch out - Centring the bounding box vertically makes tall and short products look like they are floating at different heights when placed side by side in a grid. Anchor to a baseline instead, so everything sits on the same line, which is what makes a catalogue look deliberate. ## Phase 4 · Batch that is reproducible A whole folder processes unattended, and any output can be regenerated exactly. ### Steps 1. Hash the settings Every setting that affects output goes into the hash. If changing something does not change the hash, the hash is wrong. Files: `src/settings.py` 2. Make the pipeline deterministic Pin seeds and resampling filters. Run twice and compare bytes as a test. Files: `tests/test_determinism.py` 3. Isolate failures One unreadable file must not stop a run of two hundred. Report it and continue. Files: `src/batch.py` ### Done when - [ ] A hundred images process unattended - [ ] Rerunning produces byte-identical outputs - [ ] A corrupt image is reported without stopping the run - [ ] Every output records its settings hash ### Watch out - A pipeline with any nondeterminism, such as a model running with a random seed or a resize using a nondeterministic path, cannot be reproduced, so a catalogue cannot be corrected image by image later. Pin the seed, pin the resampling filter, and verify by running twice and comparing bytes. ## Phase 5 · Export presets and a review pass Each destination gets the size it needs, and the ones the model got wrong are easy to find. ### Steps 1. Add presets with asserted dimensions Named sizes per destination, with a test asserting output dimensions rather than trusting arithmetic. Files: `src/presets.py` 2. Compute a difficulty signal Alpha coverage, edge perimeter relative to area, and the count of partially transparent pixels. Sort by it. Files: `src/review.py` 3. Validate the sort against hand marking Mark twenty by hand, then check whether the sort surfaces them. If it does not, the signal is wrong. ### Done when - [ ] Every preset produces exactly the dimensions it names - [ ] The review sort surfaces hand-marked problem images near the top - [ ] Marking for rework excludes an image from export - [ ] The review view shows the alpha mask, not only the composite ### Watch out - Reviewing two hundred cutouts in file order means the five bad ones are found by someone else after publication. Sort by a signal that correlates with difficulty, because the failures cluster at one end and reviewing the top twenty catches most of them. ## Not in this build - Product-specific vision models trained for this exact task. - Mobile capture with live preview. - Template and generated background libraries. - High volume hosted processing. ## After v1, if you want it - Shadow generation, which is what makes a composite look photographed rather than pasted - Automatic colour consistency across a catalogue shot under different lighting - A manual mask touch-up tool for the few images the review pass surfaces
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. Which model to use. Chosen by the comparison in Phase 1, not by reputation. SEGMENTATION_MODEL=rembg # Required. Input directory. Never written to. SOURCE_DIR=./photos/source # Required. Where composites and masks are written. OUTPUT_DIR=./photos/out # Required. What proportion of the frame the subject fills. The single most important setting for a consistent catalogue. SUBJECT_FRAME_FRACTION=0.78 # Required. Where the subject's bottom edge sits vertically. Anchoring here rather than centring is what aligns a grid. BASELINE_FRACTION=0.88 # Required. Concurrent images. Too high exhausts memory on a GPU faster than you expect. BATCH_CONCURRENCY=2
# PhotoRoom · product brief ## Problem The core loop is buildable, but a dependable replacement becomes a real weekend or multi-day project. For PhotoRoom, remove or replace backgrounds and batch-export consistent product images. The hard boundary is specialized vision models, mobile capture, templates, and high-volume apis, plus frontier models, compute, and data. ## Product outcome A pipeline that produces a consistent catalogue from inconsistent photographs, composites without fringes, reruns byte-identically, surfaces its own likely failures for review, and ships only models you have checked you may use. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - GPU-capable machine or user-supplied generation API - ComfyUI - model files with appropriate licenses - local storage ## Explicit non-goals for v1 - Product-specific vision models trained for this exact task. - Mobile capture with live preview. - Template and generated background libraries. - High volume hosted processing. - specialized vision models, mobile capture, templates, and high-volume APIs - frontier proprietary models - hosted GPU capacity - licensed training data - moderation and fast global delivery ## Success criteria - No fringe on the fine-detail fixture at full resolution - Reruns are byte-identical - The review sort surfaces the hand-marked problem images - Every preset's dimensions are asserted by test - Every model and background licence permits the intended use
# Build brief · PhotoRoom The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a batch background remover and product image pipeline running on your own machine to replace PhotoRoom. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Do not train a segmentation model. Two strong open ones exist and the work worth doing is the pipeline around them: consistent framing, reproducible output, and a batch that does not need supervision. Choose the model deliberately. rembg is the fast, dependency-light option that installs and runs, and its ceiling is edge quality. BiRefNet is a high-resolution model with sub-pixel alpha matting that handles hair, fur and lace far better, at a real compute cost. If your products have hard edges, the cheap option is genuinely enough. Checked September 2026. ### Stack (fixed, do not substitute) - Runtime: Python 3.12 · the image and model ecosystem lives here and fighting that is pointless - Segmentation: rembg for speed, BiRefNet for edge quality, behind one interface · the right choice depends on your subjects, so make it a setting rather than a decision - Compositing: Pillow with alpha compositing in linear light · compositing in the usual gamma-encoded space produces dark fringes around every edge - Framing: Subject bounding box from the alpha mask, then padding rules · consistent framing across a catalogue is what makes a set look professional - Storage: Sources untouched, outputs plus a settings hash per run · a catalogue you cannot regenerate is a catalogue you cannot correct ### Data model (create this before Phase 1) - runs(id, at, settings_hash, model, source_dir, output_dir) - images(id, run_id, source_path, output_path, subject_bbox_json, alpha_coverage, warnings_json) - settings(id, hash, model, background_json, padding_json, output_presets_json) · hashed so an output can always be traced to how it was made - The settings hash on every output is what makes a catalogue reproducible: the same sources and the same hash produce the same images. ### Phase 1 · One cutout, and an honest look at the edges Build: Run one image through rembg and save the alpha mask separately from the composite. Then run the same image through BiRefNet. Compare the masks at the edges, zoomed in, rather than comparing the composites at normal size. Trap: Comparing composites on a white background hides almost every edge failure, because a soft or wrong edge against white looks fine. Look at the alpha mask itself, and composite onto a saturated colour where a bad edge is obvious. Done when: Both models produce a cutout of the same image and their alpha masks are saved separately. Compositing each onto a saturated background shows the edge quality difference clearly. The choice of model for your subjects is written down with the reason. Do not build yet: No batch, no framing, no export presets. ### Phase 2 · Compositing without fringes Build: Composite in linear light: convert to linear, composite with the alpha mask, convert back. Handle partially transparent pixels correctly. Test against a subject with fine detail on both a light and a dark background. Trap: Alpha compositing in gamma-encoded space produces a dark fringe around every soft edge, which is subtle on white and glaring on a coloured background. Convert to linear light first, composite, then convert back. This one step is the difference between output that looks composited and output that looks photographed. Done when: A cutout with fine edge detail composited onto a mid-grey background shows no dark fringe, checked at full resolution. The same cutout on a light and a dark background both look correct. Compositing onto a transparent background and then onto a colour gives the same result as compositing directly. Do not build yet: No framing or batch yet. ### Phase 3 · Consistent framing across a catalogue Build: Find the subject bounding box from the alpha mask. Scale so the subject occupies a chosen proportion of the frame, and pad to the target aspect ratio. Anchor consistently, usually centred horizontally and sitting on a baseline vertically. Trap: Centring the bounding box vertically makes tall and short products look like they are floating at different heights when placed side by side in a grid. Anchor to a baseline instead, so everything sits on the same line, which is what makes a catalogue look deliberate. Done when: Fifty images of different subject sizes produce outputs where the subject occupies the same proportion of the frame. Placed in a grid, subjects sit on a common baseline rather than floating. A subject touching the edge of its source frame is detected and warned about rather than silently cropped. Do not build yet: No batch reproducibility or export presets yet. ### Phase 4 · Batch that is reproducible Build: Process a directory as a run. Hash the settings and record that hash with every output. Continue past a failing image and report it. Make a rerun with the same settings produce byte-identical outputs. Trap: A pipeline with any nondeterminism, such as a model running with a random seed or a resize using a nondeterministic path, cannot be reproduced, so a catalogue cannot be corrected image by image later. Pin the seed, pin the resampling filter, and verify by running twice and comparing bytes. Done when: A folder of a hundred images processes unattended. Rerunning with the same settings produces byte-identical outputs. A corrupt image is reported and does not stop the run. Every output's settings hash matches the run that produced it. Do not build yet: No export presets or review pass yet. ### Phase 5 · Export presets and a review pass Build: Named export presets with exact pixel dimensions and format. A review view sorting outputs by a confidence signal such as alpha coverage or edge complexity, so the likely failures are at the top rather than scattered through two hundred files. Trap: Reviewing two hundred cutouts in file order means the five bad ones are found by someone else after publication. Sort by a signal that correlates with difficulty, because the failures cluster at one end and reviewing the top twenty catches most of them. Done when: Each preset produces exactly the pixel dimensions it names. The review view puts genuinely problematic cutouts near the top, verified against a set you have marked by hand. Marking an image as needing rework excludes it from the export until it is redone. Do not build yet: Model licensing and distribution come last. ### Phase 6 · Model licences and running it for others Build: Record the licence of every model and every background asset. If you expose the pipeline as a service, add authentication, limits and a queue, because segmentation is compute and an open endpoint is a free processor. Trap: Model licences in this area vary and some permit research use only, which is not the same as commercial product photography. Check each one specifically rather than assuming that open source means unrestricted, and record what you found. Done when: Every bundled model and background asset has a licence recorded with what it permits. An exposed endpoint requires authentication and enforces a per-user job limit. A queue prevents concurrent jobs from exhausting memory, verified by submitting more jobs than the limit. Do not build yet: Nothing after this. ### Out of scope (and why) - The specialised vision models behind the commercial product, which are trained on product photography specifically and beat general segmentation on exactly the cases that matter. - Mobile capture with live preview, which is most of how the paid application is used. - The template library and the generated backgrounds, which are content and model access. - High volume hosted processing. You have one machine, and segmentation is not cheap. ### README must contain - Which model is used and why, with the subject types it was chosen for - That compositing happens in linear light, and what a gamma-space composite looks like - That framing anchors to a baseline rather than centring, and why a grid makes that obvious - That every output carries a settings hash and reruns are byte-identical - Every bundled model and asset licence, and whether commercial use is permitted
# Architecture · PhotoRoom ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | Python 3.12 | the image and model ecosystem lives here and fighting that is pointless | | Segmentation | rembg for speed, BiRefNet for edge quality, behind one interface | the right choice depends on your subjects, so make it a setting rather than a decision | | Compositing | Pillow with alpha compositing in linear light | compositing in the usual gamma-encoded space produces dark fringes around every edge | | Framing | Subject bounding box from the alpha mask, then padding rules | consistent framing across a catalogue is what makes a set look professional | | Storage | Sources untouched, outputs plus a settings hash per run | a catalogue you cannot regenerate is a catalogue you cannot correct | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Segmentation | Producing an alpha mask. | Both models sit behind one interface, so swapping is a setting. The mask is the contract. | | Composite | Combining subject and background correctly. | The linear light rule is a correctness property and must survive any change. | | Frame | Consistent scaling and anchoring. | Pure geometry from the bounding box, testable without any model at all. | | Batch | Runs, determinism and failure isolation. | The settings hash is what makes the catalogue correctable later, so it stays regardless of implementation. | | Review | Surfacing likely failures. | The difficulty signal can improve; the principle that failures are sorted to the top rather than found by a customer should not. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `SEGMENTATION_MODEL` · required · Which model to use. Chosen by the comparison in Phase 1, not by reputation. - `SOURCE_DIR` · required · Input directory. Never written to. - `OUTPUT_DIR` · required · Where composites and masks are written. - `SUBJECT_FRAME_FRACTION` · required · What proportion of the frame the subject fills. The single most important setting for a consistent catalogue. - `BASELINE_FRACTION` · required · Where the subject's bottom edge sits vertically. Anchoring here rather than centring is what aligns a grid. - `BATCH_CONCURRENCY` · required · Concurrent images. Too high exhausts memory on a GPU faster than you expect. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it.
# Agent instructions · PhotoRoom product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: Python 3.12, rembg for speed, BiRefNet for edge quality, behind one interface, Pillow with alpha compositing in linear light, Subject bounding box from the alpha mask, then padding rules, Sources untouched, outputs plus a settings hash per run. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - Comparing composites on a white background hides almost every edge failure, because a soft or wrong edge against white looks fine. Look at the alpha mask itself, and composite onto a saturated colour where a bad edge is obvious. - Alpha compositing in gamma-encoded space produces a dark fringe around every soft edge, which is subtle on white and glaring on a coloured background. Convert to linear light first, composite, then convert back. This one step is the difference between output that looks composited and output that looks photographed. - Centring the bounding box vertically makes tall and short products look like they are floating at different heights when placed side by side in a grid. Anchor to a baseline instead, so everything sits on the same line, which is what makes a catalogue look deliberate. - A pipeline with any nondeterminism, such as a model running with a random seed or a resize using a nondeterministic path, cannot be reproduced, so a catalogue cannot be corrected image by image later. Pin the seed, pin the resampling filter, and verify by running twice and comparing bytes. - Reviewing two hundred cutouts in file order means the five bad ones are found by someone else after publication. Sort by a signal that correlates with difficulty, because the failures cluster at one end and reviewing the top twenty catches most of them. - Model licences in this area vary and some permit research use only, which is not the same as commercial product photography. Check each one specifically rather than assuming that open source means unrestricted, and record what you found.
# Delivery milestones · PhotoRoom Estimated effort: **weekend** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · One cutout, and an honest look at the edges A single image is cut out, and you can see exactly where the model struggles. ### Steps 1. Run both models on one image Save the alpha mask as its own file. The mask is what you are actually evaluating. ```sh pip install rembg pillow rembg i input.jpg output.png ``` 2. Composite onto a hostile background A saturated colour, not white. Every edge problem becomes visible immediately. Files: `src/compare.py` 3. Write down the model choice With the reason and the subject type. Hard-edged products and hair are genuinely different problems. Files: `docs/model-choice.md` ### Done when - [ ] Both models produce a cutout and a separate alpha mask - [ ] Edge differences are visible on a saturated background - [ ] The model choice is recorded with its reasoning - [ ] Fine detail such as hair is inspected at full resolution ### Watch out - Comparing composites on a white background hides almost every edge failure, because a soft or wrong edge against white looks fine. Look at the alpha mask itself, and composite onto a saturated colour where a bad edge is obvious. ## M2 · Compositing without fringes The cutout sits on a new background with no dark halo around its edges. ### Steps 1. Convert to linear before compositing Undo the transfer function, composite, reapply. Do it once in a helper so no code path can skip it. Files: `src/composite.py` 2. Handle partial alpha correctly Premultiplied or straight alpha, chosen deliberately and applied consistently. Mixing the two is how fringes reappear. Files: `src/composite.py` 3. Test on light and dark The same subject on both. A fringe visible on one and not the other means the conversion is half applied. ### Done when - [ ] No dark fringe on a mid-grey background at full resolution - [ ] Light and dark backgrounds both look correct - [ ] Two-step compositing matches direct compositing - [ ] Alpha convention is consistent across every code path ### Watch out - Alpha compositing in gamma-encoded space produces a dark fringe around every soft edge, which is subtle on white and glaring on a coloured background. Convert to linear light first, composite, then convert back. This one step is the difference between output that looks composited and output that looks photographed. ## M3 · Consistent framing across a catalogue Fifty products photographed at different distances end up looking like one set. ### Steps 1. Derive the bounding box from alpha Threshold the mask and find the tight bounds. Ignore isolated specks, which are usually mask noise rather than subject. Files: `src/frame.py` 2. Scale to a target subject proportion The subject occupies a chosen fraction of the frame. This is what makes different photographs match. Files: `src/frame.py` 3. Anchor to a baseline Horizontally centred, vertically sitting on a line. Compare a grid of outputs before and after to see why. Files: `src/frame.py` ### Done when - [ ] Subjects occupy the same frame proportion across fifty images - [ ] A grid of outputs shows subjects on a common baseline - [ ] A subject touching the source edge produces a warning - [ ] Mask specks do not distort the bounding box ### Watch out - Centring the bounding box vertically makes tall and short products look like they are floating at different heights when placed side by side in a grid. Anchor to a baseline instead, so everything sits on the same line, which is what makes a catalogue look deliberate. ## M4 · Batch that is reproducible A whole folder processes unattended, and any output can be regenerated exactly. ### Steps 1. Hash the settings Every setting that affects output goes into the hash. If changing something does not change the hash, the hash is wrong. Files: `src/settings.py` 2. Make the pipeline deterministic Pin seeds and resampling filters. Run twice and compare bytes as a test. Files: `tests/test_determinism.py` 3. Isolate failures One unreadable file must not stop a run of two hundred. Report it and continue. Files: `src/batch.py` ### Done when - [ ] A hundred images process unattended - [ ] Rerunning produces byte-identical outputs - [ ] A corrupt image is reported without stopping the run - [ ] Every output records its settings hash ### Watch out - A pipeline with any nondeterminism, such as a model running with a random seed or a resize using a nondeterministic path, cannot be reproduced, so a catalogue cannot be corrected image by image later. Pin the seed, pin the resampling filter, and verify by running twice and comparing bytes. ## M5 · Export presets and a review pass Each destination gets the size it needs, and the ones the model got wrong are easy to find. ### Steps 1. Add presets with asserted dimensions Named sizes per destination, with a test asserting output dimensions rather than trusting arithmetic. Files: `src/presets.py` 2. Compute a difficulty signal Alpha coverage, edge perimeter relative to area, and the count of partially transparent pixels. Sort by it. Files: `src/review.py` 3. Validate the sort against hand marking Mark twenty by hand, then check whether the sort surfaces them. If it does not, the signal is wrong. ### Done when - [ ] Every preset produces exactly the dimensions it names - [ ] The review sort surfaces hand-marked problem images near the top - [ ] Marking for rework excludes an image from export - [ ] The review view shows the alpha mask, not only the composite ### Watch out - Reviewing two hundred cutouts in file order means the five bad ones are found by someone else after publication. Sort by a signal that correlates with difficulty, because the failures cluster at one end and reviewing the top twenty catches most of them. ## M6 · Model licences and running it for others (production only) Everything you ship or serve is something you are allowed to ship or serve. ### Steps 1. Record every licence Model, source, licence and whether commercial use is permitted. One table, checked rather than assumed. Files: `docs/licences.md` 2. Authenticate and queue Authentication before any job is accepted, and a queue with a concurrency limit sized to your memory. Files: `src/server.py` 3. Test the limit by exceeding it Submit more jobs than the limit and confirm queuing rather than memory exhaustion. ### Done when - [ ] Every model and asset licence is recorded and permits your use - [ ] An exposed endpoint requires authentication - [ ] Exceeding the concurrency limit queues rather than exhausting memory - [ ] Per-user job limits are enforced ### Watch out - Model licences in this area vary and some permit research use only, which is not the same as commercial product photography. Check each one specifically rather than assuming that open source means unrestricted, and record what you found.
# Operations · PhotoRoom ## Backup Source photographs are the irreplaceable data. Outputs are regenerable from sources plus a settings hash, which is the practical payoff of determinism. ## Restore Restore sources and settings, then rerun. Byte-identical output is what makes this a restore rather than a reprocessing with different results. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring For batch use, watch memory during processing and the per-run failure count. If exposed as a service, queue depth is the number that matters, because segmentation jobs are long enough to pile up. ## Incident checklist A catalogue processed with wrong settings is fully recoverable because sources are untouched and outputs are reproducible. This is the reason for both rules and it will justify itself the first time a framing decision changes. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] No fringe on the fine-detail fixture at full resolution - [ ] Reruns are byte-identical - [ ] The review sort surfaces the hand-marked problem images - [ ] Every preset's dimensions are asserted by test - [ ] Every model and background licence permits the intended use ## Launch constraint Do not market omitted PhotoRoom capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered.
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. Which model to use. Chosen by the comparison in Phase 1, not by reputation. SEGMENTATION_MODEL=rembg # Required. Input directory. Never written to. SOURCE_DIR=./photos/source # Required. Where composites and masks are written. OUTPUT_DIR=./photos/out # Required. What proportion of the frame the subject fills. The single most important setting for a consistent catalogue. SUBJECT_FRAME_FRACTION=0.78 # Required. Where the subject's bottom edge sits vertically. Anchoring here rather than centring is what aligns a grid. BASELINE_FRACTION=0.88 # Required. Concurrent images. Too high exhausts memory on a GPU faster than you expect. BATCH_CONCURRENCY=2
$ choose a build depth, inspect the files, then open the complete pack in your agent
People still pay for PhotoRoom because the product value is the model and compute fleet, not the prompt box around it. The recurring cost buys GPU procurement, model licensing, safety filters, queueing, storage, and rapid model replacement, not just the visible interface.
xspecialized vision models, mobile capture, templates, and high-volume APIs
xfrontier proprietary models
xhosted GPU capacity
xlicensed training data
xmoderation and fast global delivery
Don't feel like building it? These folks already made it free.
no votes, no pay-to-list · just what's real
Vibecode PhotoRoom
Kinda. The core of PhotoRoom is buildable in a weekend with the prompt on this page, but there are real gaps: specialized vision models, mobile capture, templates, and high-volume APIs, frontier proprietary models. Read the honest list above before committing.
How much does PhotoRoom cost?
PhotoRoom costs about $12.99/month (Pro, checked 2026-07-31), which is $155.88 per year.
What do I lose by replacing PhotoRoom?
Honestly: specialized vision models, mobile capture, templates, and high-volume APIs; frontier proprietary models; hosted GPU capacity; licensed training data; moderation and fast global delivery. If any of those are load-bearing for you, keep paying.
Is there an open-source alternative to PhotoRoom?
Yes: chaiNNer (Batch background removal and repeatable compositing, without the product-photo hand-holding.) The prompt is for when you want it exactly your way.