Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/linux-chrome.yml
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,10 @@ jobs:
run: (npm run start-server&)
- name: Test multi pages with video and visual metrics
run: ./bin/browsertime.js -b chrome test/data/navigationscript/multi.cjs -n 3 --chrome.timeline --video --visualMetrics --visualElements --viewPort 1000x600 --xvfb
- name: Test video and visual metrics with noise tolerance
run: |
./bin/browsertime.js -b chrome -n 1 --video --visualMetrics --videoParams.noiseTolerance 0.05 --resultDir /tmp/noise-tolerance --xvfb http://127.0.0.1:3000/simple/
jq -e '.[0].visualMetrics[0].LastVisualChange' /tmp/noise-tolerance/browsertime.json
- name: Test navigation and page complete check inactivity
run: ./bin/browsertime.js -b chrome test/data/navigationscript/navigateAndStartInTwoSteps.cjs -n 1 --pageCompleteCheckInactivity --timeToSettle 1000 --xvfb
- name: Run test with check network idle in Chrome
Expand Down
7 changes: 7 additions & 0 deletions lib/support/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,13 @@ export function parseCommandLine() {
'Convert the original video to a viewable format (for most video players). Turn that off to make a faster run.',
group: 'video'
})
.option('videoParams.noiseTolerance', {
type: 'number',
default: 0,
describe:
'Percentage (0-100) of pixels that are allowed to differ between video frames when Visual Metrics looks for visual changes. Browsers can re-rasterize the page long after it is visually done, leaving invisible sub-pixel noise that inflates Last Visual Change, Speed Index and the visual progress curve. Try 0.05 if you see a too late Last Visual Change with no visible change in the filmstrip. 0 (default) keeps the old behavior of allowing 5 pixels.',
group: 'video'
})
.option('videoParams.threads', {
default: threads,
describe:
Expand Down
6 changes: 6 additions & 0 deletions lib/video/postprocessing/visualmetrics/visualMetrics.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ export async function run(

const thumbsize = getProperty(options, 'videoParams.thumbsize', 400);

const noiseTolerance = getProperty(options, 'videoParams.noiseTolerance', 0);

const scriptArguments = [
'--video',
videoPath,
Expand All @@ -69,6 +71,10 @@ export async function run(
100
];

if (noiseTolerance > 0) {
scriptArguments.push('--noisetolerance', noiseTolerance);
}

if (options.visualMetricsPerceptual) {
scriptArguments.push('--perceptual');
}
Expand Down
38 changes: 38 additions & 0 deletions visualmetrics/visualmetrics-portable.py
Original file line number Diff line number Diff line change
Expand Up @@ -902,6 +902,33 @@ def eliminate_duplicate_frames(directory, cropped, is_mobile):
)
Path(duplicate).unlink()

# With a noise tolerance set, do a last pass over the remaining
# frames and drop every frame that differs from the previously
# kept frame by no more than the allowance. Browsers can
# re-rasterize the whole page long after it is visually done,
# leaving invisible sub-pixel noise on glyph edges spread over
# hundreds of pixels on a page-sized frame; the passes above
# allow only a handful of differing pixels, so that noise counts
# as a visual change and pollutes Last Visual Change, the visual
# progress curve, Speed Index and VisualComplete85/95/99.
# Comparing against the last KEPT frame (not the deleted
# neighbour) keeps slow real changes: once the accumulated
# difference crosses the allowance the frame is kept and becomes
# the new reference.
if options.noisetolerance > 0:
max_differences = max(
5, int(width * height * options.noisetolerance / 100)
)
files = sorted(glob.glob(str(Path(directory) / "ms_*.png")))
if len(files) > 2:
kept = files[0]
for file in files[1:]:
if frames_match(kept, file, 15, max_differences, crop, None):
logging.debug("Removing noise frame {0}".format(file))
Path(file).unlink()
else:
kept = file

except BaseException:
logging.exception("Error processing frames for duplicates")

Expand Down Expand Up @@ -1990,6 +2017,17 @@ def main():
"Diffs metric. Lets report UIs show exact diffs without pixel "
"access.",
)
parser.add_argument(
"--noisetolerance",
type=float,
default=0,
help="Percentage (0-100) of the compared area that is allowed to "
"differ between frames when eliminating duplicate frames. Use it "
"to keep invisible sub-pixel browser rendering noise from "
"inflating Last Visual Change, Speed Index and the visual "
"progress curve. 0 (default) keeps the fixed allowance of 5 "
"pixels.",
)
parser.add_argument(
"-k",
"--perceptual",
Expand Down
Loading