DEV Community

Cover image for Standardizing 250 car photos a week without Photoshop (a 20-line Python job)
Oleg Tagobitsky for API4AI

Posted on Originally published at api4.ai

Standardizing 250 car photos a week without Photoshop (a 20-line Python job)

A dealership photographs twelve cars on the lot. Twelve different backgrounds come back: the service bay, a competitor's banner, half a customer's minivan. Individually fine. On a listing page, it looks like twelve different dealerships.

The fix is boring and mechanical, which makes it a good thing to automate: cut the car out, put it on the same backdrop every time. Here's the whole integration.

The endpoint

POST https://api4ai.cloud/img-bg-removal/v1/cars/results?mode=<mode>
Enter fullscreen mode Exit fullscreen mode

Multipart form data. The photo goes in the image field as a file, or in url as a public link. Your key goes in the X-API-KEY header (api_key or key as a query parameter also works).

mode decides what you get back:

Mode Output
fg-image Car on a transparent background
fg-image-shadow Car with a drop shadow
fg-image-hideclp Car with the license plate hidden
fg-image-shadow-hideclp Both
fg-mask A mask, for compositing yourself

GET /img-bg-removal/v1/modes returns the list at runtime, so you don't have to hardcode it.

There's also an optional backdrop: pass your own image in image-bg (or url-bg) and it's blended under the car, centered. The output keeps the dimensions of the main input image, not the backdrop — worth knowing before you send a 4K backdrop and wonder why the result is 1500 px wide.

The response, and the failure case that isn't a 4xx

{
  "results": [
    {
      "status": { "code": "ok", "message": "Success" },
      "name": "img.jpg",
      "width": 1024,
      "height": 768,
      "entities": [
        {
          "kind": "image",
          "name": "cars-fg-image",
          "image": "iVBORw0KGgoAAAA...YII=",
          "format": "PNG",
          "representation": "base64"
        }
      ]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

The finished PNG is base64 in results[].entities[], alongside an objects entity with bounding boxes — so pick the entity by kind == "image" rather than by index.

The gotcha: an image the service can't read still comes back as HTTP 200. status.code is failure and status.message says why. If you only check raise_for_status(), those files quietly vanish from your output folder. Limits that trigger it: anything that isn't JPEG or PNG, over 16 MB, or larger than 4096 × 4096.

The batch script

import base64
import pathlib
import requests

API_KEY = "a4a-..."                # from portal.api4.ai
URL = "https://api4ai.cloud/img-bg-removal/v1/cars/results"
MODE = "fg-image-shadow"           # or fg-image, fg-mask, fg-image-hideclp, ...

src = pathlib.Path("inbox")        # today's lot photos
dst = pathlib.Path("ready")
dst.mkdir(exist_ok=True)

for photo in sorted(src.glob("*.jpg")):
    with photo.open("rb") as f:
        response = requests.post(
            URL,
            params={"mode": MODE},
            headers={"X-API-KEY": API_KEY},
            files={"image": f},
            timeout=60,
        )
    response.raise_for_status()

    result = response.json()["results"][0]
    if result["status"]["code"] != "ok":
        print(f"skipped {photo.name}: {result['status']['message']}")
        continue

    picture = next(e for e in result["entities"] if e["kind"] == "image")
    out = dst / f"{photo.stem}.png"
    out.write_bytes(base64.b64decode(picture["image"]))
    print(f"{photo.name} -> {out.name}")
Enter fullscreen mode Exit fullscreen mode

Requests are independent, so for a few hundred cars wrap the loop body in a ThreadPoolExecutor and run 8–16 at a time.

One domain detail worth knowing

If the output feeds Google's vehicle ads, their image guidelines ask for at least 500 × 500 (1500 × 1500 recommended) at a 4:3 ratio, with no superimposed logos or text, and the whole vehicle in frame. Cluttered backgrounds and modified license plates are both on the "allowed but hurts performance" list.

So the plate-hiding modes are right for your own site and for marketplaces where you'd rather not publish a plate — and the plain modes are right for that feed. Run the batch twice with different modes; at $0.03 a photo it's not a real decision.

If you don't want to write the loop at all

The same engine runs as a browser tool at carbg.api4.ai: drop up to 60 photos or a .zip, pick transparent / white / brand color, optional shadow and plate blur, get one zip back. Prepaid at 3 cents a photo, $5 of credit on a new account (about 166 photos), no card. Useful for the pilot before you decide the automation is worth building.

Full API docs: api4.ai/docs/car-bg-removal. What do you use for image preprocessing in your listing pipeline?

Top comments (0)