Libraryless. Click here for Pure Java version (6584L/39K).
// Approximate pixel-by-pixel similarity of 2 images very quickly // (probabilistically) // Note: If my math is correct - and it always is -, floating-point // rounding errors should not occur even for the largest images. // Note 2: The data structures can get pretty big if you go to a // very high detail level. srecord noeq ImageSimilarityAtScale( IIntegralImage img1, IIntegralImage img2, int featureSize // smallest block size to be looked at (w/h in pixels) ) extends Probabilistic implements IF0<Double> { int channels; LookAtClip root; int blocksCalculated; bool verbose; bool updateInnerNodes; double factor; double rootDiffs; // key = area in pixels // Hmm. stil working this out. How to get the proper diff at // different feature sizes? //Map<Int, RatioAccumulator> diffAtScale = autoMap(() -> new RatioAccumulator); /*void updateDiffAtScale(int area, Double change) { addToDoubleValueMap(diffAtScale, area, change, area); }*/ record LookAtClip(LookAtClip parent, Rect r) implements Runnable { L<LookAtClip> children; // diffs are in range [0;sqrt(channels)*pixelsCovered] double initialDiff, diffsFromChildren, areaCoveredByChildren; double latestDiff; double latestDiff() { ret latestDiff; } double calculateLatestDiff() { double area = area(); double areaLeft = 1-areaCoveredByChildren/area; latestDiff = initialDiff*areaLeft+diffsFromChildren; if (verbose) printVars(+r, +latestDiff, +initialDiff, +areaLeft, +diffsFromChildren); ret latestDiff; } run { // make initial guess double sum = 0; for channel to channels: { double sum1 = img1.rectSum(r, channel); double sum2 = img2.rectSum(r, channel); sum += sqr(sum1-sum2); //if (verbose) printVars(+r, +sum1, +sum2, +sum); } setInitialDiff(sqrt(sum)*factor); ++blocksCalculated; if (r.w > r.h) { if (r.w > featureSize) splitHorizontally(); } else { if (r.h > featureSize) splitVertically(); } } void splitHorizontally { split(asList(splitRectInHorizontalHalves(r))); } void splitVertically { split(asList(splitRectInVerticalHalves(r))); } void split(L<Rect> parts) { double p = 0.99; children = map(parts, r -> new LookAtClip(this, r)); scheduleAllRelative(p, children); } void setInitialDiff(double initialDiff) { this.initialDiff = initialDiff; //updateDiffAtScale(area(), initialDiff); calculateLatestDiff(); if (verbose) printVars setInitialDiff(+r, +initialDiff, +latestDiff); if (this == root) rootDiffs = latestDiff; if (parent != null) { parent.areaCoveredByChildren += area(); parent.diffsFromChildren += latestDiff; parent.update(); } } void update() { double oldValue = latestDiff; calculateLatestDiff(); if (verbose) printVars update(+r, +latestDiff); rootDiffs += latestDiff-oldValue; if (updateInnerNodes && parent != null) { parent.diffsFromChildren += latestDiff-oldValue; if (verbose) printVars update2(+r, +oldValue, +latestDiff, dfc := parent.diffsFromChildren); parent.update(); } } simplyCached int area() { ret rectArea(r); } } run { assertEquals("Sizes of compared images have to be equal", img1.getSize(), img2.getSize()); assertEquals("Channel counts of compared images have to be equal", channels = img1.nChannels(), img2.nChannels()); factor = 1/(255.0*sqrt(channels)); root = new LookAtClip(null, imageRect(img1)); root.run(); } // similarity between 0 and 1 // it's a best guess until the calculation is complete public Double similarity aka get() { ret 1-diff(); } // difference (1-similarity) between 0 and 1 // (this can be more precise in floating point then similarity) public double diff() { ret /*root.latestDiff()*/rootDiffs/root.area(); } int blocksCalculated() { ret blocksCalculated; } }
download show line numbers debug dex old transpilations
Travelled to 3 computer(s): bhatertpkbcr, mqqgnosmbjvj, pyentgdyhuwx
No comments. add comment
Snippet ID: | #1032159 |
Snippet name: | ImageSimilarityAtScale [seems OK] - check how similar 2 images are on a given scale, probabilistically approximating from below |
Eternal ID of this version: | #1032159/57 |
Text MD5: | 59d05c7d0a30a27c6b6a09fb0bbdc93c |
Transpilation MD5: | 928b42a9d21b64a12667f8d773a46aab |
Author: | stefan |
Category: | javax |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2021-09-08 03:10:16 |
Source code size: | 4315 bytes / 137 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 297 / 547 |
Version history: | 56 change(s) |
Referenced in: | #1032464 - LookForMotion [dev.] #1034167 - Standard Classes + Interfaces (LIVE, continuation of #1003674) |