Dev.to AI 🤖 Ai 👁 0 📖 4 min read

Why 'looks white' fails Amazon's image check — and how to test it in code

A seller uploads a product photo on a pure white background. It looks white. Amazon rejects it. The background measured 242, 244, 246 — visually indistinguishable from 255 on a warm monitor, but far enough from the thre

A seller uploads a product photo on a pure white background. It looks white. Amazon rejects it.

The background measured 242, 244, 246 — visually indistinguishable from 255 on a warm monitor, but far enough from the threshold to fail an automated check.

This post walks through the checks we implemented for PicViva’s free compliance checker: background pixel sampling, product coverage, resolution, and overlay detection. The code is simplified for readability; production needs more edge cases.

What the check actually compares

Amazon’s requirements are not a style guide. The automated checks compare measurable properties:

Background pixel values, per channel
Product fill ratio inside the frame
Overlays: text, badges, borders, watermarks
Resolution and aspect ratio
Category-specific rules that override the general policy

The first one causes the most rejections, and it is also the easiest to test locally.

Sampling the background in code

The naive approach is to look at the average colour of the whole image. That fails because the product occupies the centre and drags the average away from white.

Instead, sample the corners and a thin ring around the frame. Those regions should be pure background.


import sharp from "sharp";

const CORNER_SIZE = 24;
const WHITE_MIN = 250;
const WHITE_RATIO = 0.98;

async function checkBackground(file: string) {
  const image = sharp(file).flatten({ background: "#ffffff" });
  const { data, info } = await image
    .raw()
    .toBuffer({ resolveWithObject: true });

  const { width, height, channels } = info;
  const corners = [
    [0, 0],
    [width - CORNER_SIZE, 0],
    [0, height - CORNER_SIZE],
    [width - CORNER_SIZE, height - CORNER_SIZE],
  ];

  let white = 0;
  let total = 0;

  for (const [x0, y0] of corners) {
    for (let y = y0; y < y0 + CORNER_SIZE; y++) {
      for (let x = x0; x < x0 + CORNER_SIZE; x++) {
        const i = (y * width + x) * channels;
        const r = data[i];
        const g = data[i + 1];
        const b = data[i + 2];
        total++;
        if (r >= WHITE_MIN && g >= WHITE_MIN && b >= WHITE_MIN) white++;
      }
    }
  }

  return {
    whiteRatio: white / total,
    pass: white / total >= WHITE_RATIO,
  };
}

Three details matter more than the code itself:

Flatten transparency first. A transparent PNG exported to JPEG gets composited onto some background. If you check the raw alpha channel, you are testing a file the marketplace will never see. Flatten onto white before sampling.

Sample the corners, not the whole image. The product occupies the centre. A whole-image average tells you almost nothing.

Report the actual values. A boolean “pass” is not useful to a seller. Return the minimum and maximum per channel so they can see whether they are at 254 or 242.

Interpreting the result

The official requirement is 255, 255, 255. In practice, JPEG compression makes exact 255 rare across an entire region.

A practical rule set:

Fail if the corner average on any channel is below 250.
Warn if it is between 250 and 254 — close enough to look white, but still not exact.
Pass if all channels are 255 across the sampled region.

Do not use the product’s edge pixels. Anti-aliasing and contact shadows legitimately darken them. Sample a margin inside the frame and a thin band outside the product’s bounding box.

Estimating product coverage

If the image has an alpha channel, coverage is straightforward: count pixels above an alpha threshold.

function coverageFromAlpha(
  data: Buffer,
  width: number,
  height: number,
  channels: number,
) {
  let product = 0;
  for (let i = 0; i < width * height; i++) {
    const alpha = data[i * channels + 3];
    if (alpha > 10) product++;
  }
  return product / (width * height);
}

For JPEG, there is no alpha. The simplest approach is background subtraction: count pixels that differ from white by more than a tolerance. That over-counts shadows and props, which is why coverage should be reported as an estimate, not a verdict.

The working range from our sample of roughly 200 rejection cases was 85% to 92% of the frame, with margin on all sides. Too small and too tightly cropped fail for the same underlying reason: the product does not dominate the image.

Resolution and overlays

Resolution is metadata:

const { width, height } = await sharp(file).metadata();

Overlay detection is harder. OCR catches text, but badges, borders and watermarks are often graphic. A practical first pass combines:

OCR confidence above a threshold
Edge density in regions that should be empty
A border scan along the frame edges

Treat overlay findings as warnings unless the rule is unambiguous. False positives are worse than a missed warning here.

Putting it together

Each check returns a finding rather than a single score:

type Finding = {
  rule: string;
  status: "pass" | "warn" | "fail";
  detail: string;
};

function report(findings: Finding[]) {
  return {
    ok: findings.every((f) => f.status !== "fail"),
    findings,
  };
}

That shape lets the UI explain why an image is at risk, instead of showing a red X with no context.

What code cannot catch

Three rejection causes are procedural, not visual:

“Authoritative image already exists.” On a shared ASIN, Amazon may lock a preferred image. Your upload never takes effect. Re-editing cannot fix a catalog-ownership problem.
Byte-identical resubmission. Amazon fingerprints uploaded images. Exporting the same file again can be rejected without reprocessing.
Category rules that override the general policy. An image can pass every general check and still fail a softlines-specific rule.

A checker can flag the visual risks. It cannot guarantee approval, and it should say so.

Takeaways
Compare pixel values, not perception.
Sample corners and edges, not the whole image.
Flatten transparency before testing.
Report actual values, not just pass/fail.
Treat coverage and overlays as estimates with warnings.
Keep procedural causes out of the visual score.

PicViva’s compliance checker runs these checks for free (0 credits) against Amazon, Walmart and TikTok Shop rules: https://picviva.ai/en/tools/amazon-image-compliance-checker

I work on PicViva. The checker flags potential issues; it does not guarantee marketplace approval.

📰 Read the original article on Dev.to AI

Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.