webarkit / jsartoolkitNFT

jsartolkitNFT is a smaller version of jsartoolkit5 with only NFT support
GNU Lesser General Public License v3.0
130 stars 26 forks source link

Marker detection problem after the first time #392

Open MasoudShah opened 1 month ago

MasoudShah commented 1 month ago

There is a problem with marker detection. For the first time that a marker gets detected, everything is ok. But the second time and subsequent occurrence of detections, it seems that the position of the marker has an initial value and it gets corrected after some seconds. This amount of time is due to using filters for omitting noises, but the problem is that this filter shouldn't be active for initial frame of tracking (as the marker gets detected). It should behave like the first time of detection that probably no value of position is initialized. The video in this link shows the difference between the first and second detection. It seems that for the second detections, there is some animation for showing the threejs object, but I have verified, there is no animation at all. It's just about the position of the target marker at the beginning of detection. I should note that this problem doesn't exist in the threaded version.

MasoudShah commented 1 month ago

I think I found the causing problem. It seems that the matrixGL_RH value (which the marker position depends on it), at the beginning of detection is not precise. Then the updating of position takes time due to one-euro-filter operation. The solution is to turn off the filter for the initial frames after the detection. For example, the first five frames, update without filter affection. I noticed the problem solves with this method.

kalwalt commented 1 month ago

Hi @MasoudShah thank you for this, I have also noticed this issue, have you a patch so i can test it?

MasoudShah commented 1 month ago

Something like this

...
class OneEuroFilter {
  constructor({ minCutOff, beta }) {
    this.minCutOff = minCutOff;
    this.beta = beta;
    this.dCutOff = 0.001; // period in milliseconds, so default to 0.001 = 1Hz
    this.framesToSkip = 10;

    this.xPrev = null;
    this.dxPrev = null;
    this.tPrev = null;
    this.warmup = 0;
  }

  reset() {
    this.warmup = 0;
  }

  filter(t, x) {
    if (this.warmup < this.framesToSkip) {
      this.warmup++;
      this.xPrev = x;
      this.dxPrev = x.map(() => 0);
      this.tPrev = t;
      return x;
    }
    const { xPrev, tPrev, dxPrev } = this;
...

This change only protects the first frames of detection from filter effect. But another problem shows up that for some milliseconds, the threejs object shows in very different position and attitude. To amend this problem, I used 100 milliseconds delay to show the object and playing video, that the user almost doesn't understand at all. Instead of

ARVideos[index].play();
planes[index].visible = true;

in the main script for showing objects, I used this:

setTimeout((idx) => {
      ARVideos[idx].play();
      planes[idx].visible = true;
}, 100, index);

I only noted the solution for you, to do it yourself with your own code structure. If there is still some vagueness, let me know.

kalwalt commented 1 month ago

Something like this

...
class OneEuroFilter {
  constructor({ minCutOff, beta }) {
    this.minCutOff = minCutOff;
    this.beta = beta;
    this.dCutOff = 0.001; // period in milliseconds, so default to 0.001 = 1Hz
    this.framesToSkip = 10;

    this.xPrev = null;
    this.dxPrev = null;
    this.tPrev = null;
    this.warmup = 0;
  }

  reset() {
    this.warmup = 0;
  }

  filter(t, x) {
    if (this.warmup < this.framesToSkip) {
      this.warmup++;
      this.xPrev = x;
      this.dxPrev = x.map(() => 0);
      this.tPrev = t;
      return x;
    }
    const { xPrev, tPrev, dxPrev } = this;
...

This change only protects the first frames of detection from filter effect. But another problem shows up that for some milliseconds, the threejs object shows in very different position and attitude. To amend this problem, I used 100 milliseconds delay to show the object and playing video, that the user almost doesn't understand at all. Instead of

ARVideos[index].play();
planes[index].visible = true;

in the main script for showing objects, I used this:

setTimeout((idx) => {
      ARVideos[idx].play();
      planes[idx].visible = true;
}, 100, index);

I only noted the solution for you, to do it yourself with your own code structure. If there is still some vagueness, let me know.

Thank you @MasoudShah i will try it!