Create high res images by scrolling around at low res in video.
============
debug
: show debug comments in the logannotations
: display markers in the canvas to help debuggingvignette
: draw to the canvas in an edge-blurred circle, to improve image compositing and look awesome (reduces field of view of image, though)goodMatchesMin
: default 8; frames must have this many good matches to be displayedkeyframeThreshold
: default 2; keyframes must have 2x number of good matcheskeyframeDistanceThreshold
: default 1/3; keyframes must be this far off of last keyframe originnum_train_levels
: default 4; keypoint finder at different zoom levels; not needed if no resizing happens (as in microscopy)trainingMargin
: default 0.1, // the width of the margin we discard when training a pattern, to improve matching; as a proportion from 0 to 1width
: default 1000; width of canvasheight
: default 1000; width of canvassrcWidth
: default 640; width of video source (bigger may degrade performance)srcHeight
: default 480; height of video source
scrollgraph.train(img1)
scrollgraph.match(img2)
returns aresults
object with the following parameters and functions:results.good_matches
results.num_matches
results.num_corners
results.projected_corners
results.annotate(ctx, offset)
scrollgraph.setOption(key, value)
: change options dynamically, i.e. turn on annotations
- each image placed on the canvas is called a
frame
- each image "good enough" to establish a new local origin point is called a
keyframe
, and is used to train our matcher against for subsequent matches
- from the original image (our first
keyframe
), we train our matcher to look in the incoming video feed for a similar "constellation" of points, regardless of rotation - when enough "good matches" are found in a video frame, we place that frame on the canvas at the estimated position (using the findTransform method from jsfeat)
- we repeat as fast as we can!
- if an image is really good, and ALSO far enough away from the last keyframe, and enough time has passed (500ms), we
A range of tweaks and optimizations have been added to create a more responsive experience (which gets "stuck" less often).
- we relax thresholds for both frames and keyframes if no recent matches have been made (500ms)
- we reject frames that are > ~15 degrees different rotation than the last keyframe
- we reject frames that are > 1.5x displaced from the last keyframe
-
OFFSET FOUND!!! (0 || 0.1) evaluates to 0.1, so we had never ditched the 0.1 margin and were offsetting..
-
fix this....
- try removing keypoint scaling/margin code?? no, it seems critical to getting enough matches
- 0.1 stays stationary but 0.2 does not...???
- 0 works but not as well as 0.1?
-
also our tracking circle is offset to the right
// notice: training does more: resamples, blurs... is it all necessary?
-
add tracking circles
-
add "last keyframe" in grey?
-
and markers/labels
-
break out labeling code?
-
refactor local variables to be part of response
-
optimizations:
- dist is often the limiting factor... but we often get off-track. Could we have a "fallback" that's more lax?
- keyframes must not happen too often - balance with responsiveness...? use time/distance threshold?
- cap points from each image
- can we use only lower res keyframes? 1/2 resolution? options.keyframeScale !
-
we could say, if it's been 2 seconds since last keyframe, try a lower threshold?
-
there's a balance between "good keyframes" and "fast responsiveness"
- try putting non-keyframes behind in blend modes
-
if no matches for X seconds, try other past keyframes?
- we relax thresholds for both frames and keyframes if no recent matches have been made (500ms)
-
SOLVE 10px offset!!
- 0.1 scaling factor?? YES THIS WAS IT-- solve this!
- just forget scaling factor? keyframes seem fine...
- would using the scaling factor improve our keyframe matching when we get off track?
- 0.1 scaling factor?? YES THIS WAS IT-- solve this!
-
still getting some kind of offset maybe...
-
enable flipping; we'd have to change (setting this aside until optimizations):
train_image
handleImage
match()
- AHA! seems this causes the 10px offset and gets us in a loop of creating keyframes
- OK< so flipping seems to suffer from offset issues, but possibly we need to add a flipbit other places...
-
reject blurry keyframes? Tough: https://stackoverflow.com/q/7765810/1116657, https://stackoverflow.com/q/6646371/1116657
- can we assess if it's MOSTLY blurry? run analytics on keyframes like
inspectKeyframe()
?
- can we assess if it's MOSTLY blurry? run analytics on keyframes like
- detect landscape/portrait (done but needs testing)
- doesn't work - video seems OK but canvas is not
- scale images according to points 1 and 2
- scaling works, but keyframes then would need to be scaled too? do we need to track scale?
- turned scaling off, can re-enable as an option to fix it
- try placing perspectivally, using WebGL/glfx.js: https://github.com/jywarren/webgl-distort/blob/main/dist/webgl-distort.js
-
render_pattern_shape
inhandleImage
-
- bump some work to another thread? Web Workers?
- focus knockout - higher sharpness images overwrite lower sharpness images
- refactor
jsfeat
-based resize/resample to use canvas? - make multi-level training toggle-able, and use it in LDI matching if 2 images are different sizes?
- we're now only doing this if you set
num_train_levels
to > 1
- we're now only doing this if you set
- get jsfeat demo working
- refactor into separate modules
- get it working again
- externalize
match()
to be run in scrollgraph? - refactor
train_pattern()
to accept new image/vid input - pass out
good_matches
frommatch()
- can we make the multiscale pyramid toggle-able - yes, set
num_train_levels
to 1 - don't run on each video frame, only when a match is requested?
- maybe we need a working canvas and a display canvas
- place image only if a match happens
- average
shape_pts
corners to place the image (short term solution) - make resolution configurable - not just 640x480
- draw box around key images
- mark points
- crop in/out the image used to train... use only the center for some reason??
- scale up the points too, to account for scale down of image
- use x,y offset + num matches to find new keyframes. Plus high rankings
- add rotation
- tweak cumulative offest settings
- figure out black screen startup error (i think!)
- rotation error - esp when annotations are on (might be 2 issues)
- putImageData doesn't respect ctx.translate!!! SOLVED by transferring to a drawImage call
- [M] now, keyframes don't respect rotation. Do we need to set a global ctx.rotate and leave it running?
- maybe solved! Needs testing...
- figure out ~10px error in keyframe placement, see if this addresses drift
- see if rotation fixes solve this... i think it does... keep testing
- make mask larger
- use intermediate canvas to perform masking?
- do NOT mask on the working canvas... just on pasting onto the display canvas
- figure out canvas scaling to not be a gigantic webpage? div scrolling?
- transform: scale(); in CSS
- keyframe offset limits maybe aren't working?
- pull out filterFrame and filterKeyframe ?