tscircuit / tscircuit/image-utils

percentThreshold is accepted and validated but discarded before comparison

Open
#40 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
0
Forks
10
PR merge metrics
No merged PRs in 30d

Description

The public comparison options accept percentThreshold, and prepareOptions rejects negative/non-finite values with a threshold-specific error. However, a valid value is then discarded: PreparedOptions has no threshold field, the returned object omits it, and compare always uses differentPixels === 0 for equal.

For two fully opaque 10x1 RGBA images differing at exactly one pixel, omitted/0/0.1/1/10/50/100 all return { equal: false, differentPixels: 1, totalPixels: 10 }. This occurs in both strict and non-strict mode with caret/antialiasing ignoring disabled. Negative, NaN and Infinity thresholds throw, so this is an actively accepted/validated option that does not affect comparison, rather than an arbitrary unknown JS property.

The option was introduced in #3 alongside pixel counts. Its tests calculate a percentage on the caller side and do not exercise percentThreshold. I found no documented unit or boundary convention, so this report does not assume a particular 0..1 versus 0..100 interpretation. Please either wire the option into a documented threshold policy or remove/deprecate the inert option and direct callers to compare the returned counts themselves. Existing raw differentPixels/totalPixels should remain available.

Reproduced on main 81a79bc3665a4fedab02d3a8e273b9ac5d1af906 (v0.0.10), Bun 1.4.2. Save at the repository root as reproduce-percent-threshold.ts and run bun reproduce-percent-threshold.ts:

import { encode } from "fast-png"
import looksSame from "./lib/looks-same"

const referenceData = new Uint8Array(10 * 4)
for (let i = 3; i < referenceData.length; i += 4) referenceData[i] = 255
const currentData = referenceData.slice()
currentData[0] = 255
const png = (data: Uint8Array) => encode({ width: 10, height: 1, channels: 4, depth: 8, data })
const reference = png(referenceData)
const current = png(currentData)
for (const strict of [false, true]) for (const percentThreshold of [undefined, 0, 0.1, 1, 10, 50, 100]) {
  const result = await looksSame(reference, current, {
    percentThreshold, strict, ignoreCaret: false, ignoreAntialiasing: false,
  })
  console.log(JSON.stringify({ strict, percentThreshold: percentThreshold ?? "omitted", ...result }))
}
for (const percentThreshold of [-1, Number.NaN, Number.POSITIVE_INFINITY]) {
  try {
    await looksSame(reference, current, { percentThreshold })
    throw new Error("invalid threshold unexpectedly accepted")
  } catch (error) {
    if (!(error instanceof TypeError)) throw error
    console.log(JSON.stringify({ invalid: String(percentThreshold), error: error.message }))
  }
}
console.log(JSON.stringify({ identicalControl: await looksSame(reference, reference, { percentThreshold: 0 }) }))

Validation: all 14 changed-image configurations returned the same one-pixel difference; all three invalid-value controls threw the documented TypeError, and the identical-image control returned equal=true. TypeScript check passed. This is independent of #39: all source pixels here have alpha 255.

Prepared with Codex (Astra) assistance and verified locally.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in lib/looks-same and trace prepareOptions, PreparedOptions, and compare to see where percentThreshold is validated and lost. Run reproduce-percent-threshold.ts first, then inspect the existing comparison tests; done means the option has a documented, tested policy or is removed/deprecated while differentPixels and totalPixels remain available.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
computer-vision, testing-qa
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.