Binarising AI Line Art For Vector Traceability: Threshold Choices That Survive Adobe QC
A batch of 40 line art assets came back from Adobe Stock review with the same rejection note: "issues with technical quality, visible noise or artifacts." I had traced them at whatever threshold the default gave me. The vectors looked fine on my screen. Zoomed to 400%, the strokes were made of tiny

A batch of 40 line art assets came back from Adobe Stock review with the same rejection note: "issues with technical quality, visible noise or artifacts." I had traced them at whatever threshold the default gave me. The vectors looked fine on my screen. Zoomed to 400%, the strokes were made of tiny islands and speckles, and the reviewer saw it in seconds. That rejection cost me a week of rework, so I got systematic about the binarisation step. Here is what I learned, with the numbers I now use.
If you sell vectors from AI generated line art, the pipeline is: generate raster, binarise to pure black and white, trace to paths, export. The trace step is honest. It draws exactly what you give it. If your binarised image has stray pixels along an edge, the trace turns them into microscopic path fragments. Adobe's QC reviewers zoom in, and Adobe's own checks flag files with excessive path fragmentation. Vecteezy's review is a bit more forgiving, but a messy trace gets rejected there too, and Dreamstime asks for clean editable vectors in its 15 column metadata sheet the same way.
The fix is upstream. Get the bitmap clean and the trace takes care of itself.
Most tracing tools apply an automatic threshold when you feed them grayscale or color input. That default is tuned for photos, not for AI line art. AI generators produce anti-aliased edges and faint gray halos around strokes. A mid range threshold can turn those halos into a fog of tiny specks.
What works for me, tested across a few hundred assets:
For anything destined for stickers or print, I binarise harder than feels comfortable. Pure 0 and pure 255, nothing in between. Cutters and printers do not care about your grays.
Two operations, in this order, remove most rejections:
Both run in Pillow plus a little numpy-free logic in my scripts, and on my 1 vCPU, 2 GB RAM server each image takes under a second. I process one file at a time, call gc.collect() after each asset, and measure peak RSS with resource.getrusage(RUSAGE_SELF).ru_maxrss rather than trusting my feel for memory use. That habit came from watching a parallel batch swap itself to death on the small box.
You do not need to open every SVG in Illustrator. Render the vector back to a raster at the same size and diff it against the binarised source. If the pixel difference is small and there are no tiny isolated regions in the render, the trace is clean. I inspect at 60 dpi previews during the batch and only render at full resolution for final output, which keeps the small server happy.
Count your paths too. If one asset traces to ten times the path count of its siblings in the same batch, something went wrong at the threshold step. That single number caught more problems for me than any visual review.
A clean vector with a wrong CSV header is still an instant rejection. Adobe's upload CSV has a fixed 5 column header, Vecteezy uses 4 columns, Dreamstime wants 15. All three reject the file immediately if the header does not match exactly. After hand-editing a spreadsheet wrong twice, I wrote make_metadata.py to emit the exact header per platform from a folder of finished files, and upload_tracker.py so a re-run of the batch never double-posts an asset. Double-posting is the fastest way to get an account flagged, and at 3 am with a batch running, you will not remember what you already uploaded.
Binarisation is not glamorous. It is a histogram, a threshold in the valley, a despeckle, and a path count check. But it is the difference between a vector that survives Adobe QC and one that comes back with the same rejection note four times. Everything above runs on a 1 vCPU, 2 GB box with no GPU, one process at a time.
This whole workflow, the metadata builders, the upload ledger, the packager that zips files and renders a 1280x720 cover and 600x600 thumbnail, is part of a kit I built after packaging 39 products this way, about 856 MB of finished output on that tiny server. If you want the scripts instead of rebuilding them from this post, they are here:
Building digital products? Free guides & kits for digital product sellers.