Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

137
LINES

< > BotCompany Repo | #1032159 // ImageSimilarityAtScale [seems OK] - check how similar 2 images are on a given scale, probabilistically approximating from below

JavaX fragment (include) [tags: use-pretranspiled]

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)