From 1e154071bd323f756625f8172ef67133a561450a Mon Sep 17 00:00:00 2001 From: Good Guy Date: Sun, 20 Nov 2016 20:46:36 -0700 Subject: [PATCH] add xvid/flv formats, add motion plugin varients + save file, fix build bug --- cinelerra-5.1/cinelerra/ffmpeg.C | 4 +- cinelerra-5.1/ffmpeg/audio/f4v.dfl | 1 + cinelerra-5.1/ffmpeg/audio/flv.dfl | 1 + cinelerra-5.1/ffmpeg/audio/mp3.f4v | 1 + cinelerra-5.1/ffmpeg/audio/mp3.flv | 2 + cinelerra-5.1/ffmpeg/encode.opts | 1 + cinelerra-5.1/ffmpeg/flv.dfl | 1 + cinelerra-5.1/ffmpeg/video/avi.xvid | 2 + cinelerra-5.1/ffmpeg/video/f4v.dfl | 1 + cinelerra-5.1/ffmpeg/video/flv.dfl | 1 + cinelerra-5.1/ffmpeg/video/flv.flv | 1 + cinelerra-5.1/ffmpeg/video/h264.f4v | 5 + cinelerra-5.1/ffmpeg/video/xvid.dfl | 1 + cinelerra-5.1/plugin_defs | 6 +- cinelerra-5.1/plugins/Makefile | 2 + .../plugins/interpolatevideo/Makefile | 6 +- .../interpolatevideo/interpolatevideo.C | 2 +- .../plugins/interpolatevideo/motionscan-hv.C | 1 + .../plugins/interpolatevideo/motionscan-hv.h | 1 + .../interpolatevideo/motionscan-hv.inc | 1 + .../plugins/interpolatevideo/motionscan.C | 1 - .../plugins/interpolatevideo/motionscan.h | 1 - .../plugins/interpolatevideo/motionscan.inc | 1 - .../plugins/interpolatevideo/opticflow.C | 2 +- .../plugins/interpolatevideo/opticflow.h | 2 +- .../plugins/motion-cv/.deps/motion.Plo | 691 +++++ .../plugins/motion-cv/.deps/motionwindow.Plo | 621 ++++ .../plugins/motion-cv/.libs/motion.la | 1 + .../plugins/motion-cv/.libs/motion.lai | 41 + .../plugins/motion-cv/.libs/motion.so | Bin 0 -> 315344 bytes cinelerra-5.1/plugins/motion-cv/Makefile | 13 + cinelerra-5.1/plugins/motion-cv/motion-cv.C | 2655 ++++++++++++++++ .../plugins/motion-cv/motion-cv.C.orig | 2731 +++++++++++++++++ cinelerra-5.1/plugins/motion-cv/motion-cv.h | 494 +++ .../plugins/motion-cv/motionwindow-cv.C | 1023 ++++++ .../plugins/motion-cv/motionwindow-cv.h | 360 +++ cinelerra-5.1/plugins/motion-cv/picon.png | Bin 0 -> 2301 bytes cinelerra-5.1/plugins/motion-hv/Makefile | 14 + .../motion.C => motion-hv/motion-hv.C} | 874 ++---- .../motion.h => motion-hv/motion-hv.h} | 149 +- .../motion.inc => motion-hv/motion-hv.inc} | 8 +- .../plugins/motion-hv/motionscan-hv.C | 1936 ++++++++++++ .../motionscan-hv.h} | 130 +- .../motionscan-hv.inc} | 6 +- .../motionwindow-hv.C} | 511 ++- .../plugins/motion-hv/motionwindow-hv.h | 385 +++ .../motionwindow-hv.inc} | 8 +- cinelerra-5.1/plugins/motion-hv/picon.png | Bin 0 -> 4152 bytes cinelerra-5.1/plugins/motion-hv/theme_suv.png | Bin 0 -> 210 bytes cinelerra-5.1/plugins/motion.new/Makefile | 14 - cinelerra-5.1/plugins/motion.new/motionscan.C | 1090 ------- .../plugins/motion.new/motionwindow.h | 373 --- cinelerra-5.1/plugins/motion/motion.C | 659 +++- cinelerra-5.1/plugins/motion/motion.h | 118 +- cinelerra-5.1/plugins/motion/motion.inc | 8 +- cinelerra-5.1/plugins/motion/motionscan.C | 1746 +++-------- cinelerra-5.1/plugins/motion/motionscan.h | 126 +- cinelerra-5.1/plugins/motion/motionscan.inc | 8 +- cinelerra-5.1/plugins/motion/motionwindow.C | 253 +- cinelerra-5.1/plugins/motion/motionwindow.h | 158 +- cinelerra-5.1/plugins/motion/motionwindow.inc | 8 +- cinelerra-5.1/plugins/motion/opencvwrapper.C | 132 +- cinelerra-5.1/plugins/motion/opencvwrapper.h | 22 +- cinelerra-5.1/plugins/motion/rotatescan.C | 471 --- cinelerra-5.1/plugins/motion/rotatescan.h | 136 - cinelerra-5.1/plugins/motion/rotatescan.inc | 12 - cinelerra-5.1/plugins/motion2point/Makefile | 6 +- cinelerra-5.1/plugins/motion2point/motion.C | 2 +- cinelerra-5.1/plugins/motion2point/motion.h | 2 +- .../plugins/motion2point/motionscan-hv.C | 1 + .../plugins/motion2point/motionscan-hv.h | 1 + .../plugins/motion2point/motionscan-hv.inc | 1 + .../plugins/motion2point/motionscan.C | 1 - .../plugins/motion2point/motionscan.h | 1 - .../plugins/motion2point/motionscan.inc | 1 - .../plugins/motion2point/motionwindow.C | 2 +- cinelerra-5.1/thirdparty/Makefile | 2 +- 77 files changed, 13075 insertions(+), 4978 deletions(-) create mode 100644 cinelerra-5.1/ffmpeg/audio/f4v.dfl create mode 100644 cinelerra-5.1/ffmpeg/audio/flv.dfl create mode 100644 cinelerra-5.1/ffmpeg/audio/mp3.f4v create mode 100644 cinelerra-5.1/ffmpeg/audio/mp3.flv create mode 100644 cinelerra-5.1/ffmpeg/flv.dfl create mode 100644 cinelerra-5.1/ffmpeg/video/avi.xvid create mode 100644 cinelerra-5.1/ffmpeg/video/f4v.dfl create mode 100644 cinelerra-5.1/ffmpeg/video/flv.dfl create mode 100644 cinelerra-5.1/ffmpeg/video/flv.flv create mode 100644 cinelerra-5.1/ffmpeg/video/h264.f4v create mode 100644 cinelerra-5.1/ffmpeg/video/xvid.dfl create mode 120000 cinelerra-5.1/plugins/interpolatevideo/motionscan-hv.C create mode 120000 cinelerra-5.1/plugins/interpolatevideo/motionscan-hv.h create mode 120000 cinelerra-5.1/plugins/interpolatevideo/motionscan-hv.inc delete mode 120000 cinelerra-5.1/plugins/interpolatevideo/motionscan.C delete mode 120000 cinelerra-5.1/plugins/interpolatevideo/motionscan.h delete mode 120000 cinelerra-5.1/plugins/interpolatevideo/motionscan.inc create mode 100644 cinelerra-5.1/plugins/motion-cv/.deps/motion.Plo create mode 100644 cinelerra-5.1/plugins/motion-cv/.deps/motionwindow.Plo create mode 120000 cinelerra-5.1/plugins/motion-cv/.libs/motion.la create mode 100644 cinelerra-5.1/plugins/motion-cv/.libs/motion.lai create mode 100755 cinelerra-5.1/plugins/motion-cv/.libs/motion.so create mode 100644 cinelerra-5.1/plugins/motion-cv/Makefile create mode 100644 cinelerra-5.1/plugins/motion-cv/motion-cv.C create mode 100644 cinelerra-5.1/plugins/motion-cv/motion-cv.C.orig create mode 100644 cinelerra-5.1/plugins/motion-cv/motion-cv.h create mode 100644 cinelerra-5.1/plugins/motion-cv/motionwindow-cv.C create mode 100644 cinelerra-5.1/plugins/motion-cv/motionwindow-cv.h create mode 100644 cinelerra-5.1/plugins/motion-cv/picon.png create mode 100644 cinelerra-5.1/plugins/motion-hv/Makefile rename cinelerra-5.1/plugins/{motion.new/motion.C => motion-hv/motion-hv.C} (60%) rename cinelerra-5.1/plugins/{motion.new/motion.h => motion-hv/motion-hv.h} (69%) rename cinelerra-5.1/plugins/{motion.new/motion.inc => motion-hv/motion-hv.inc} (90%) create mode 100644 cinelerra-5.1/plugins/motion-hv/motionscan-hv.C rename cinelerra-5.1/plugins/{motion.new/motionscan.h => motion-hv/motionscan-hv.h} (62%) rename cinelerra-5.1/plugins/{motion.new/motionscan.inc => motion-hv/motionscan-hv.inc} (92%) rename cinelerra-5.1/plugins/{motion.new/motionwindow.C => motion-hv/motionwindow-hv.C} (60%) create mode 100644 cinelerra-5.1/plugins/motion-hv/motionwindow-hv.h rename cinelerra-5.1/plugins/{motion.new/motionwindow.inc => motion-hv/motionwindow-hv.inc} (89%) create mode 100644 cinelerra-5.1/plugins/motion-hv/picon.png create mode 100644 cinelerra-5.1/plugins/motion-hv/theme_suv.png delete mode 100644 cinelerra-5.1/plugins/motion.new/Makefile delete mode 100644 cinelerra-5.1/plugins/motion.new/motionscan.C delete mode 100644 cinelerra-5.1/plugins/motion.new/motionwindow.h delete mode 100644 cinelerra-5.1/plugins/motion/rotatescan.C delete mode 100644 cinelerra-5.1/plugins/motion/rotatescan.h delete mode 100644 cinelerra-5.1/plugins/motion/rotatescan.inc create mode 120000 cinelerra-5.1/plugins/motion2point/motionscan-hv.C create mode 120000 cinelerra-5.1/plugins/motion2point/motionscan-hv.h create mode 120000 cinelerra-5.1/plugins/motion2point/motionscan-hv.inc delete mode 120000 cinelerra-5.1/plugins/motion2point/motionscan.C delete mode 120000 cinelerra-5.1/plugins/motion2point/motionscan.h delete mode 120000 cinelerra-5.1/plugins/motion2point/motionscan.inc diff --git a/cinelerra-5.1/cinelerra/ffmpeg.C b/cinelerra-5.1/cinelerra/ffmpeg.C index 16c6409b..9b468bf4 100644 --- a/cinelerra-5.1/cinelerra/ffmpeg.C +++ b/cinelerra-5.1/cinelerra/ffmpeg.C @@ -1949,7 +1949,7 @@ int FFMPEG::encode_activate() (ret=avio_open(&fmt_ctx->pb, fmt_ctx->filename, AVIO_FLAG_WRITE)) < 0 ) { ff_err(ret, "FFMPEG::encode_activate: err opening : %s\n", fmt_ctx->filename); - return 1; + return -1; } AVDictionary *fopts = 0; @@ -1961,7 +1961,7 @@ int FFMPEG::encode_activate() if( ret < 0 ) { ff_err(ret, "FFMPEG::encode_activate: write header failed %s\n", fmt_ctx->filename); - return 1; + return -1; } encoding = 1; } diff --git a/cinelerra-5.1/ffmpeg/audio/f4v.dfl b/cinelerra-5.1/ffmpeg/audio/f4v.dfl new file mode 100644 index 00000000..8b64dbf5 --- /dev/null +++ b/cinelerra-5.1/ffmpeg/audio/f4v.dfl @@ -0,0 +1 @@ +mp3.f4v diff --git a/cinelerra-5.1/ffmpeg/audio/flv.dfl b/cinelerra-5.1/ffmpeg/audio/flv.dfl new file mode 100644 index 00000000..da751b7c --- /dev/null +++ b/cinelerra-5.1/ffmpeg/audio/flv.dfl @@ -0,0 +1 @@ +mp3.flv diff --git a/cinelerra-5.1/ffmpeg/audio/mp3.f4v b/cinelerra-5.1/ffmpeg/audio/mp3.f4v new file mode 100644 index 00000000..f524cf0f --- /dev/null +++ b/cinelerra-5.1/ffmpeg/audio/mp3.f4v @@ -0,0 +1 @@ +f4v libmp3lame diff --git a/cinelerra-5.1/ffmpeg/audio/mp3.flv b/cinelerra-5.1/ffmpeg/audio/mp3.flv new file mode 100644 index 00000000..05d193c4 --- /dev/null +++ b/cinelerra-5.1/ffmpeg/audio/mp3.flv @@ -0,0 +1,2 @@ +flv libmp3lame +# sample rate must be: 44100 or 22050 or 11025 diff --git a/cinelerra-5.1/ffmpeg/encode.opts b/cinelerra-5.1/ffmpeg/encode.opts index 73021c58..79301da3 100644 --- a/cinelerra-5.1/ffmpeg/encode.opts +++ b/cinelerra-5.1/ffmpeg/encode.opts @@ -1,3 +1,4 @@ # apply at init encode +loglevel=error threads=auto side_data_only_packets=1 diff --git a/cinelerra-5.1/ffmpeg/flv.dfl b/cinelerra-5.1/ffmpeg/flv.dfl new file mode 100644 index 00000000..da751b7c --- /dev/null +++ b/cinelerra-5.1/ffmpeg/flv.dfl @@ -0,0 +1 @@ +mp3.flv diff --git a/cinelerra-5.1/ffmpeg/video/avi.xvid b/cinelerra-5.1/ffmpeg/video/avi.xvid new file mode 100644 index 00000000..acb78bf7 --- /dev/null +++ b/cinelerra-5.1/ffmpeg/video/avi.xvid @@ -0,0 +1,2 @@ +avi mpeg4 +vtag xvid diff --git a/cinelerra-5.1/ffmpeg/video/f4v.dfl b/cinelerra-5.1/ffmpeg/video/f4v.dfl new file mode 100644 index 00000000..c1d2d642 --- /dev/null +++ b/cinelerra-5.1/ffmpeg/video/f4v.dfl @@ -0,0 +1 @@ +h264.f4v diff --git a/cinelerra-5.1/ffmpeg/video/flv.dfl b/cinelerra-5.1/ffmpeg/video/flv.dfl new file mode 100644 index 00000000..85a67a71 --- /dev/null +++ b/cinelerra-5.1/ffmpeg/video/flv.dfl @@ -0,0 +1 @@ +flv.flv diff --git a/cinelerra-5.1/ffmpeg/video/flv.flv b/cinelerra-5.1/ffmpeg/video/flv.flv new file mode 100644 index 00000000..46af192a --- /dev/null +++ b/cinelerra-5.1/ffmpeg/video/flv.flv @@ -0,0 +1 @@ +flv flv diff --git a/cinelerra-5.1/ffmpeg/video/h264.f4v b/cinelerra-5.1/ffmpeg/video/h264.f4v new file mode 100644 index 00000000..664351d7 --- /dev/null +++ b/cinelerra-5.1/ffmpeg/video/h264.f4v @@ -0,0 +1,5 @@ +f4v libx264 +profile=baseline +level=3.0 +preset=medium +x264opts keyint=25:min-keyint=4:qpmin=3:qpmax=33:qp_step=4:merange=8 diff --git a/cinelerra-5.1/ffmpeg/video/xvid.dfl b/cinelerra-5.1/ffmpeg/video/xvid.dfl new file mode 100644 index 00000000..da30cb20 --- /dev/null +++ b/cinelerra-5.1/ffmpeg/video/xvid.dfl @@ -0,0 +1 @@ +avi.xvid diff --git a/cinelerra-5.1/plugin_defs b/cinelerra-5.1/plugin_defs index 3dbe03f1..d8fdbbd0 100644 --- a/cinelerra-5.1/plugin_defs +++ b/cinelerra-5.1/plugin_defs @@ -37,9 +37,9 @@ plugin_dirs += video_tools video_tools := blur decimate delayvideo denoisemjpeg denoisevideo downsample \ fieldframe flash framefield freezeframe greycstoration interpolatepixels \ interpolatevideo invertvideo linearblur loopvideo motion2 motionblur \ - motion overlay radialblur reframe reframert reroute reversevideo \ - seltempavg sharpen spectrogram svg titler timeavg timefront unsharp \ - videoscope wave zoomblur + motion motion-cv motion-hv overlay radialblur reframe reframert reroute \ + reversevideo seltempavg sharpen spectrogram svg titler timeavg timefront \ + unsharp videoscope wave zoomblur plugin_dirs += blending blending := chromakeyhsv chromakey diffkey diff --git a/cinelerra-5.1/plugins/Makefile b/cinelerra-5.1/plugins/Makefile index c233f06c..5bac9134 100644 --- a/cinelerra-5.1/plugins/Makefile +++ b/cinelerra-5.1/plugins/Makefile @@ -78,6 +78,8 @@ DIRS = \ loopaudio \ loopvideo \ motion \ + motion-cv \ + motion-hv \ motion2point \ motionblur \ normalize \ diff --git a/cinelerra-5.1/plugins/interpolatevideo/Makefile b/cinelerra-5.1/plugins/interpolatevideo/Makefile index 98baa20f..3b50feb2 100644 --- a/cinelerra-5.1/plugins/interpolatevideo/Makefile +++ b/cinelerra-5.1/plugins/interpolatevideo/Makefile @@ -3,7 +3,9 @@ include ../../plugin_defs OBJS = $(OBJDIR)/interpolatevideo.o \ $(OBJDIR)/interpolatewindow.o \ $(OBJDIR)/opticflow.o \ - $(OBJDIR)/motionscan.o + $(OBJDIR)/motionscan-hv.o + +CFLAGS += -DMotionScan=MotionHVScan PLUGIN = interpolatevideo @@ -13,4 +15,4 @@ include ../../plugin_config $(OBJDIR)/interpolatevideo.o: interpolatevideo.C $(OBJDIR)/interpolatewindow.o: interpolatewindow.C $(OBJDIR)/opticflow.o: opticflow.C -$(OBJDIR)/motionscan.o: motionscan.C motionscan.h +$(OBJDIR)/motionscan-hv.o: motionscan-hv.C motionscan-hv.h diff --git a/cinelerra-5.1/plugins/interpolatevideo/interpolatevideo.C b/cinelerra-5.1/plugins/interpolatevideo/interpolatevideo.C index 017be8d5..9e553ac3 100644 --- a/cinelerra-5.1/plugins/interpolatevideo/interpolatevideo.C +++ b/cinelerra-5.1/plugins/interpolatevideo/interpolatevideo.C @@ -24,7 +24,7 @@ #include "interpolatevideo.h" #include "interpolatewindow.h" #include "language.h" -#include "motionscan.h" +#include "motionscan-hv.h" #include "opticflow.h" #include "transportque.inc" #include diff --git a/cinelerra-5.1/plugins/interpolatevideo/motionscan-hv.C b/cinelerra-5.1/plugins/interpolatevideo/motionscan-hv.C new file mode 120000 index 00000000..898c188d --- /dev/null +++ b/cinelerra-5.1/plugins/interpolatevideo/motionscan-hv.C @@ -0,0 +1 @@ +../motion-hv/motionscan-hv.C \ No newline at end of file diff --git a/cinelerra-5.1/plugins/interpolatevideo/motionscan-hv.h b/cinelerra-5.1/plugins/interpolatevideo/motionscan-hv.h new file mode 120000 index 00000000..d5eb987c --- /dev/null +++ b/cinelerra-5.1/plugins/interpolatevideo/motionscan-hv.h @@ -0,0 +1 @@ +../motion-hv/motionscan-hv.h \ No newline at end of file diff --git a/cinelerra-5.1/plugins/interpolatevideo/motionscan-hv.inc b/cinelerra-5.1/plugins/interpolatevideo/motionscan-hv.inc new file mode 120000 index 00000000..4c59354a --- /dev/null +++ b/cinelerra-5.1/plugins/interpolatevideo/motionscan-hv.inc @@ -0,0 +1 @@ +../motion-hv/motionscan-hv.inc \ No newline at end of file diff --git a/cinelerra-5.1/plugins/interpolatevideo/motionscan.C b/cinelerra-5.1/plugins/interpolatevideo/motionscan.C deleted file mode 120000 index 4ac14722..00000000 --- a/cinelerra-5.1/plugins/interpolatevideo/motionscan.C +++ /dev/null @@ -1 +0,0 @@ -../motion/motionscan.C \ No newline at end of file diff --git a/cinelerra-5.1/plugins/interpolatevideo/motionscan.h b/cinelerra-5.1/plugins/interpolatevideo/motionscan.h deleted file mode 120000 index 9461913f..00000000 --- a/cinelerra-5.1/plugins/interpolatevideo/motionscan.h +++ /dev/null @@ -1 +0,0 @@ -../motion/motionscan.h \ No newline at end of file diff --git a/cinelerra-5.1/plugins/interpolatevideo/motionscan.inc b/cinelerra-5.1/plugins/interpolatevideo/motionscan.inc deleted file mode 120000 index e4b8d557..00000000 --- a/cinelerra-5.1/plugins/interpolatevideo/motionscan.inc +++ /dev/null @@ -1 +0,0 @@ -../motion/motionscan.inc \ No newline at end of file diff --git a/cinelerra-5.1/plugins/interpolatevideo/opticflow.C b/cinelerra-5.1/plugins/interpolatevideo/opticflow.C index bce991b0..9f94d6be 100644 --- a/cinelerra-5.1/plugins/interpolatevideo/opticflow.C +++ b/cinelerra-5.1/plugins/interpolatevideo/opticflow.C @@ -24,7 +24,7 @@ #include "clip.h" #include "interpolatevideo.h" -#include "motionscan.h" +#include "motionscan-hv.h" #include "opticflow.h" #include diff --git a/cinelerra-5.1/plugins/interpolatevideo/opticflow.h b/cinelerra-5.1/plugins/interpolatevideo/opticflow.h index bbea19df..6e611fc5 100644 --- a/cinelerra-5.1/plugins/interpolatevideo/opticflow.h +++ b/cinelerra-5.1/plugins/interpolatevideo/opticflow.h @@ -24,7 +24,7 @@ #include "interpolatevideo.inc" #include "loadbalance.h" -#include "motionscan.inc" +#include "motionscan-hv.inc" #include "opticflow.inc" // Need a 2nd table if a large number of packages diff --git a/cinelerra-5.1/plugins/motion-cv/.deps/motion.Plo b/cinelerra-5.1/plugins/motion-cv/.deps/motion.Plo new file mode 100644 index 00000000..22e9e747 --- /dev/null +++ b/cinelerra-5.1/plugins/motion-cv/.deps/motion.Plo @@ -0,0 +1,691 @@ +motion.lo: motion.C /usr/include/stdc-predef.h ../../cinelerra/affine.h \ + ../../cinelerra/affine.inc ../../cinelerra/loadbalance.h \ + ../../guicast/condition.inc ../../guicast/mutex.inc \ + ../../guicast/thread.h /usr/include/pthread.h /usr/include/features.h \ + /usr/include/sys/cdefs.h /usr/include/bits/wordsize.h \ + /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \ + /usr/include/endian.h /usr/include/bits/endian.h \ + /usr/include/bits/byteswap.h /usr/include/bits/types.h \ + /usr/include/bits/typesizes.h /usr/include/bits/byteswap-16.h \ + /usr/include/sched.h \ + /usr/lib/gcc/x86_64-redhat-linux/5.3.1/include/stddef.h \ + /usr/include/time.h /usr/include/bits/sched.h /usr/include/bits/time.h \ + /usr/include/bits/timex.h /usr/include/xlocale.h \ + /usr/include/bits/pthreadtypes.h /usr/include/bits/setjmp.h \ + ../../guicast/vframe.inc ../../guicast/bcdisplayinfo.h \ + /usr/include/stdio.h /usr/include/libio.h /usr/include/_G_config.h \ + /usr/include/wchar.h \ + /usr/lib/gcc/x86_64-redhat-linux/5.3.1/include/stdarg.h \ + /usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \ + /usr/include/stdlib.h /usr/include/bits/waitflags.h \ + /usr/include/bits/waitstatus.h /usr/include/sys/types.h \ + /usr/include/sys/select.h /usr/include/bits/select.h \ + /usr/include/bits/sigset.h /usr/include/sys/sysmacros.h \ + /usr/include/alloca.h /usr/include/bits/stdlib-float.h \ + /usr/include/X11/Xlib.h /usr/include/X11/X.h \ + /usr/include/X11/Xfuncproto.h /usr/include/X11/Xosdefs.h \ + ../../guicast/clip.h ../../guicast/bchash.h \ + ../../guicast/bcwindowbase.inc ../../guicast/hashcache.inc \ + ../../guicast/units.h ../../guicast/sizes.h /usr/include/math.h \ + /usr/include/bits/math-vector.h /usr/include/bits/libm-simd-decl-stubs.h \ + /usr/include/bits/huge_val.h /usr/include/bits/huge_valf.h \ + /usr/include/bits/huge_vall.h /usr/include/bits/inf.h \ + /usr/include/bits/nan.h /usr/include/bits/mathdef.h \ + /usr/include/bits/mathcalls.h \ + /usr/lib/gcc/x86_64-redhat-linux/5.3.1/include/stdint.h \ + /usr/include/stdint.h /usr/include/bits/wchar.h \ + ../../cinelerra/filexml.h ../../guicast/sizes.h \ + ../../cinelerra/keyframe.h ../../cinelerra/auto.h \ + ../../cinelerra/auto.inc ../../cinelerra/edl.inc ../../guicast/guicast.h \ + ../../guicast/bcbar.h ../../guicast/bcsubwindow.h \ + ../../guicast/arraylist.h ../../guicast/bcwindowbase.h ../../config.h \ + ../../guicast/bcbar.inc ../../guicast/bcbitmap.inc \ + ../../guicast/bcbutton.inc ../../guicast/bccapture.inc \ + ../../guicast/bcclipboard.inc ../../guicast/bccmodels.inc \ + ../../guicast/bcdragwindow.inc ../../guicast/bcfilebox.inc \ + ../../guicast/bclistbox.inc ../../guicast/bcmenubar.inc \ + ../../guicast/bcmeter.inc ../../guicast/bcpan.inc \ + ../../guicast/bcpbuffer.inc ../../guicast/bcpixmap.inc \ + ../../guicast/bcpopup.inc ../../guicast/bcpopupmenu.inc \ + ../../guicast/bcpot.inc ../../guicast/bcprogress.inc \ + ../../guicast/bcrelocatablewidget.h ../../guicast/bcrepeater.inc \ + ../../guicast/bcresources.inc ../../guicast/bcscrollbar.inc \ + ../../guicast/bcslider.inc ../../guicast/bcsubwindow.inc \ + ../../guicast/bcsynchronous.inc ../../guicast/bctextbox.inc \ + ../../guicast/bctimer.inc ../../guicast/bctitle.inc \ + ../../guicast/bctoggle.inc ../../guicast/bctumble.inc \ + ../../guicast/bcwidgetgrid.inc ../../guicast/bcwindow.inc \ + ../../guicast/bcwindowevents.inc ../../guicast/condition.inc \ + ../../guicast/bchash.inc ../../guicast/linklist.h \ + ../../guicast/mutex.inc ../../guicast/vframe.inc \ + /usr/include/X11/Xatom.h /usr/include/X11/Xft/Xft.h \ + /usr/include/freetype2/ft2build.h \ + /usr/include/freetype2/config/ftheader.h \ + /usr/include/freetype2/freetype.h \ + /usr/include/freetype2/config/ftconfig.h \ + /usr/include/freetype2/config/ftconfig-64.h \ + /usr/include/freetype2/config/ftoption.h \ + /usr/include/freetype2/config/ftstdlib.h \ + /usr/lib/gcc/x86_64-redhat-linux/5.3.1/include/limits.h \ + /usr/lib/gcc/x86_64-redhat-linux/5.3.1/include/syslimits.h \ + /usr/include/limits.h /usr/include/bits/posix1_lim.h \ + /usr/include/bits/local_lim.h /usr/include/linux/limits.h \ + /usr/include/bits/posix2_lim.h /usr/include/bits/xopen_lim.h \ + /usr/include/string.h /usr/include/setjmp.h \ + /usr/include/freetype2/fttypes.h /usr/include/freetype2/ftsystem.h \ + /usr/include/freetype2/ftimage.h /usr/include/freetype2/fterrors.h \ + /usr/include/freetype2/ftmoderr.h /usr/include/freetype2/fterrdef.h \ + /usr/include/fontconfig/fontconfig.h /usr/include/sys/stat.h \ + /usr/include/bits/stat.h /usr/include/X11/extensions/Xrender.h \ + /usr/include/X11/Xutil.h /usr/include/X11/keysym.h \ + /usr/include/X11/keysymdef.h /usr/include/X11/extensions/render.h \ + /usr/include/X11/Xdefs.h /usr/include/X11/Xft/XftCompat.h \ + /usr/include/X11/cursorfont.h /usr/include/X11/extensions/xf86vmode.h \ + /usr/include/X11/Xmd.h /usr/include/X11/extensions/xf86vm.h \ + /usr/include/GL/glx.h /usr/include/GL/gl.h /usr/include/GL/glext.h \ + /usr/include/inttypes.h /usr/include/GL/glxext.h \ + ../../guicast/bcbitmap.h /usr/include/sys/ipc.h \ + /usr/include/bits/ipctypes.h /usr/include/bits/ipc.h \ + /usr/include/sys/shm.h /usr/include/bits/shm.h \ + /usr/include/X11/extensions/XShm.h /usr/include/X11/extensions/shm.h \ + /usr/include/X11/extensions/Xvlib.h /usr/include/X11/extensions/Xv.h \ + ../../guicast/colors.h ../../guicast/bcbutton.h \ + ../../guicast/bcclipboard.h ../../guicast/thread.h \ + ../../guicast/bcdragwindow.h ../../guicast/bcpopup.h \ + ../../guicast/bclistboxitem.h ../../guicast/vframe.h \ + ../../guicast/bctexture.inc ../../quicktime/colormodels.h \ + ../../guicast/bccmodels.h ../../guicast/bcpan.h \ + ../../guicast/rotateframe.inc ../../guicast/bcfilebox.h \ + ../../guicast/bcdelete.inc ../../guicast/bclistboxitem.inc \ + ../../guicast/bcnewfolder.inc ../../guicast/bctextbox.h \ + ../../guicast/bclistbox.h ../../guicast/bcscrollbar.h \ + ../../guicast/bctoggle.h ../../guicast/fonts.h ../../guicast/bctumble.h \ + ../../guicast/bcwindow.h ../../guicast/filesystem.inc \ + ../../guicast/bcmenu.h ../../guicast/bcmenuitem.inc \ + ../../guicast/bcmenupopup.inc ../../guicast/bcmenubar.h \ + ../../guicast/bcmenu.inc ../../guicast/bcmenuitem.h \ + ../../guicast/bcmenupopup.h ../../guicast/bcmeter.h \ + ../../guicast/bcpopupmenu.h ../../guicast/bcpot.h \ + ../../guicast/bcpixmap.h ../../guicast/bcprogress.h \ + ../../guicast/bcrecentlist.h ../../guicast/bcresources.h \ + ../../guicast/bcdisplayinfo.inc ../../guicast/bcfontentry.inc \ + ../../guicast/bcsignals.inc ../../guicast/bcslider.h \ + ../../guicast/bcsynchronous.h ../../guicast/bctexture.h \ + ../../guicast/bctheme.h ../../guicast/bctitle.h ../../guicast/bctimer.h \ + /usr/include/sys/time.h ../../guicast/error.h ../../guicast/debug.h \ + ../../cinelerra/filexml.inc ../../cinelerra/autos.inc \ + ../../cinelerra/keyframes.inc ../../cinelerra/messages.inc \ + ../../guicast/language.h /usr/include/libintl.h motion.h \ + ../../cinelerra/affine.inc ../../guicast/bchash.inc \ + ../../cinelerra/filexml.inc ../../cinelerra/keyframe.inc \ + ../../cinelerra/loadbalance.h motionwindow.inc \ + ../../cinelerra/overlayframe.inc ../../cinelerra/pluginvclient.h \ + ../../cinelerra/maxbuffers.h ../../cinelerra/pluginclient.h \ + ../../guicast/arraylist.h ../../guicast/condition.h \ + ../../cinelerra/edlsession.inc ../../cinelerra/keyframe.h \ + ../../cinelerra/mainprogress.inc ../../cinelerra/plugincommands.h \ + ../../cinelerra/pluginserver.inc ../../cinelerra/theme.inc \ + ../../guicast/vframe.h ../../guicast/rotateframe.inc motionwindow.h \ + motion.inc ../../cinelerra/pluginwindow.h ../../guicast/bcwindow.h \ + ../../guicast/mutex.h ../../cinelerra/overlayframe.h \ + ../../cinelerra/overlayframe.inc picon_png.h ../../guicast/rotateframe.h \ + ../../cinelerra/transportque.h ../../cinelerra/canvas.inc \ + ../../cinelerra/preferences.inc ../../cinelerra/transportque.inc \ + /usr/include/errno.h /usr/include/bits/errno.h \ + /usr/include/linux/errno.h /usr/include/asm/errno.h \ + /usr/include/asm-generic/errno.h /usr/include/asm-generic/errno-base.h \ + /usr/include/unistd.h /usr/include/bits/posix_opt.h \ + /usr/include/bits/environments.h /usr/include/bits/confname.h \ + /usr/include/getopt.h + +/usr/include/stdc-predef.h: + +../../cinelerra/affine.h: + +../../cinelerra/affine.inc: + +../../cinelerra/loadbalance.h: + +../../guicast/condition.inc: + +../../guicast/mutex.inc: + +../../guicast/thread.h: + +/usr/include/pthread.h: + +/usr/include/features.h: + +/usr/include/sys/cdefs.h: + +/usr/include/bits/wordsize.h: + +/usr/include/gnu/stubs.h: + +/usr/include/gnu/stubs-64.h: + +/usr/include/endian.h: + +/usr/include/bits/endian.h: + +/usr/include/bits/byteswap.h: + +/usr/include/bits/types.h: + +/usr/include/bits/typesizes.h: + +/usr/include/bits/byteswap-16.h: + +/usr/include/sched.h: + +/usr/lib/gcc/x86_64-redhat-linux/5.3.1/include/stddef.h: + +/usr/include/time.h: + +/usr/include/bits/sched.h: + +/usr/include/bits/time.h: + +/usr/include/bits/timex.h: + +/usr/include/xlocale.h: + +/usr/include/bits/pthreadtypes.h: + +/usr/include/bits/setjmp.h: + +../../guicast/vframe.inc: + +../../guicast/bcdisplayinfo.h: + +/usr/include/stdio.h: + +/usr/include/libio.h: + +/usr/include/_G_config.h: + +/usr/include/wchar.h: + +/usr/lib/gcc/x86_64-redhat-linux/5.3.1/include/stdarg.h: + +/usr/include/bits/stdio_lim.h: + +/usr/include/bits/sys_errlist.h: + +/usr/include/stdlib.h: + +/usr/include/bits/waitflags.h: + +/usr/include/bits/waitstatus.h: + +/usr/include/sys/types.h: + +/usr/include/sys/select.h: + +/usr/include/bits/select.h: + +/usr/include/bits/sigset.h: + +/usr/include/sys/sysmacros.h: + +/usr/include/alloca.h: + +/usr/include/bits/stdlib-float.h: + +/usr/include/X11/Xlib.h: + +/usr/include/X11/X.h: + +/usr/include/X11/Xfuncproto.h: + +/usr/include/X11/Xosdefs.h: + +../../guicast/clip.h: + +../../guicast/bchash.h: + +../../guicast/bcwindowbase.inc: + +../../guicast/hashcache.inc: + +../../guicast/units.h: + +../../guicast/sizes.h: + +/usr/include/math.h: + +/usr/include/bits/math-vector.h: + +/usr/include/bits/libm-simd-decl-stubs.h: + +/usr/include/bits/huge_val.h: + +/usr/include/bits/huge_valf.h: + +/usr/include/bits/huge_vall.h: + +/usr/include/bits/inf.h: + +/usr/include/bits/nan.h: + +/usr/include/bits/mathdef.h: + +/usr/include/bits/mathcalls.h: + +/usr/lib/gcc/x86_64-redhat-linux/5.3.1/include/stdint.h: + +/usr/include/stdint.h: + +/usr/include/bits/wchar.h: + +../../cinelerra/filexml.h: + +../../guicast/sizes.h: + +../../cinelerra/keyframe.h: + +../../cinelerra/auto.h: + +../../cinelerra/auto.inc: + +../../cinelerra/edl.inc: + +../../guicast/guicast.h: + +../../guicast/bcbar.h: + +../../guicast/bcsubwindow.h: + +../../guicast/arraylist.h: + +../../guicast/bcwindowbase.h: + +../../config.h: + +../../guicast/bcbar.inc: + +../../guicast/bcbitmap.inc: + +../../guicast/bcbutton.inc: + +../../guicast/bccapture.inc: + +../../guicast/bcclipboard.inc: + +../../guicast/bccmodels.inc: + +../../guicast/bcdragwindow.inc: + +../../guicast/bcfilebox.inc: + +../../guicast/bclistbox.inc: + +../../guicast/bcmenubar.inc: + +../../guicast/bcmeter.inc: + +../../guicast/bcpan.inc: + +../../guicast/bcpbuffer.inc: + +../../guicast/bcpixmap.inc: + +../../guicast/bcpopup.inc: + +../../guicast/bcpopupmenu.inc: + +../../guicast/bcpot.inc: + +../../guicast/bcprogress.inc: + +../../guicast/bcrelocatablewidget.h: + +../../guicast/bcrepeater.inc: + +../../guicast/bcresources.inc: + +../../guicast/bcscrollbar.inc: + +../../guicast/bcslider.inc: + +../../guicast/bcsubwindow.inc: + +../../guicast/bcsynchronous.inc: + +../../guicast/bctextbox.inc: + +../../guicast/bctimer.inc: + +../../guicast/bctitle.inc: + +../../guicast/bctoggle.inc: + +../../guicast/bctumble.inc: + +../../guicast/bcwidgetgrid.inc: + +../../guicast/bcwindow.inc: + +../../guicast/bcwindowevents.inc: + +../../guicast/condition.inc: + +../../guicast/bchash.inc: + +../../guicast/linklist.h: + +../../guicast/mutex.inc: + +../../guicast/vframe.inc: + +/usr/include/X11/Xatom.h: + +/usr/include/X11/Xft/Xft.h: + +/usr/include/freetype2/ft2build.h: + +/usr/include/freetype2/config/ftheader.h: + +/usr/include/freetype2/freetype.h: + +/usr/include/freetype2/config/ftconfig.h: + +/usr/include/freetype2/config/ftconfig-64.h: + +/usr/include/freetype2/config/ftoption.h: + +/usr/include/freetype2/config/ftstdlib.h: + +/usr/lib/gcc/x86_64-redhat-linux/5.3.1/include/limits.h: + +/usr/lib/gcc/x86_64-redhat-linux/5.3.1/include/syslimits.h: + +/usr/include/limits.h: + +/usr/include/bits/posix1_lim.h: + +/usr/include/bits/local_lim.h: + +/usr/include/linux/limits.h: + +/usr/include/bits/posix2_lim.h: + +/usr/include/bits/xopen_lim.h: + +/usr/include/string.h: + +/usr/include/setjmp.h: + +/usr/include/freetype2/fttypes.h: + +/usr/include/freetype2/ftsystem.h: + +/usr/include/freetype2/ftimage.h: + +/usr/include/freetype2/fterrors.h: + +/usr/include/freetype2/ftmoderr.h: + +/usr/include/freetype2/fterrdef.h: + +/usr/include/fontconfig/fontconfig.h: + +/usr/include/sys/stat.h: + +/usr/include/bits/stat.h: + +/usr/include/X11/extensions/Xrender.h: + +/usr/include/X11/Xutil.h: + +/usr/include/X11/keysym.h: + +/usr/include/X11/keysymdef.h: + +/usr/include/X11/extensions/render.h: + +/usr/include/X11/Xdefs.h: + +/usr/include/X11/Xft/XftCompat.h: + +/usr/include/X11/cursorfont.h: + +/usr/include/X11/extensions/xf86vmode.h: + +/usr/include/X11/Xmd.h: + +/usr/include/X11/extensions/xf86vm.h: + +/usr/include/GL/glx.h: + +/usr/include/GL/gl.h: + +/usr/include/GL/glext.h: + +/usr/include/inttypes.h: + +/usr/include/GL/glxext.h: + +../../guicast/bcbitmap.h: + +/usr/include/sys/ipc.h: + +/usr/include/bits/ipctypes.h: + +/usr/include/bits/ipc.h: + +/usr/include/sys/shm.h: + +/usr/include/bits/shm.h: + +/usr/include/X11/extensions/XShm.h: + +/usr/include/X11/extensions/shm.h: + +/usr/include/X11/extensions/Xvlib.h: + +/usr/include/X11/extensions/Xv.h: + +../../guicast/colors.h: + +../../guicast/bcbutton.h: + +../../guicast/bcclipboard.h: + +../../guicast/thread.h: + +../../guicast/bcdragwindow.h: + +../../guicast/bcpopup.h: + +../../guicast/bclistboxitem.h: + +../../guicast/vframe.h: + +../../guicast/bctexture.inc: + +../../quicktime/colormodels.h: + +../../guicast/bccmodels.h: + +../../guicast/bcpan.h: + +../../guicast/rotateframe.inc: + +../../guicast/bcfilebox.h: + +../../guicast/bcdelete.inc: + +../../guicast/bclistboxitem.inc: + +../../guicast/bcnewfolder.inc: + +../../guicast/bctextbox.h: + +../../guicast/bclistbox.h: + +../../guicast/bcscrollbar.h: + +../../guicast/bctoggle.h: + +../../guicast/fonts.h: + +../../guicast/bctumble.h: + +../../guicast/bcwindow.h: + +../../guicast/filesystem.inc: + +../../guicast/bcmenu.h: + +../../guicast/bcmenuitem.inc: + +../../guicast/bcmenupopup.inc: + +../../guicast/bcmenubar.h: + +../../guicast/bcmenu.inc: + +../../guicast/bcmenuitem.h: + +../../guicast/bcmenupopup.h: + +../../guicast/bcmeter.h: + +../../guicast/bcpopupmenu.h: + +../../guicast/bcpot.h: + +../../guicast/bcpixmap.h: + +../../guicast/bcprogress.h: + +../../guicast/bcrecentlist.h: + +../../guicast/bcresources.h: + +../../guicast/bcdisplayinfo.inc: + +../../guicast/bcfontentry.inc: + +../../guicast/bcsignals.inc: + +../../guicast/bcslider.h: + +../../guicast/bcsynchronous.h: + +../../guicast/bctexture.h: + +../../guicast/bctheme.h: + +../../guicast/bctitle.h: + +../../guicast/bctimer.h: + +/usr/include/sys/time.h: + +../../guicast/error.h: + +../../guicast/debug.h: + +../../cinelerra/filexml.inc: + +../../cinelerra/autos.inc: + +../../cinelerra/keyframes.inc: + +../../cinelerra/messages.inc: + +../../guicast/language.h: + +/usr/include/libintl.h: + +motion.h: + +../../cinelerra/affine.inc: + +../../guicast/bchash.inc: + +../../cinelerra/filexml.inc: + +../../cinelerra/keyframe.inc: + +../../cinelerra/loadbalance.h: + +motionwindow.inc: + +../../cinelerra/overlayframe.inc: + +../../cinelerra/pluginvclient.h: + +../../cinelerra/maxbuffers.h: + +../../cinelerra/pluginclient.h: + +../../guicast/arraylist.h: + +../../guicast/condition.h: + +../../cinelerra/edlsession.inc: + +../../cinelerra/keyframe.h: + +../../cinelerra/mainprogress.inc: + +../../cinelerra/plugincommands.h: + +../../cinelerra/pluginserver.inc: + +../../cinelerra/theme.inc: + +../../guicast/vframe.h: + +../../guicast/rotateframe.inc: + +motionwindow.h: + +motion.inc: + +../../cinelerra/pluginwindow.h: + +../../guicast/bcwindow.h: + +../../guicast/mutex.h: + +../../cinelerra/overlayframe.h: + +../../cinelerra/overlayframe.inc: + +picon_png.h: + +../../guicast/rotateframe.h: + +../../cinelerra/transportque.h: + +../../cinelerra/canvas.inc: + +../../cinelerra/preferences.inc: + +../../cinelerra/transportque.inc: + +/usr/include/errno.h: + +/usr/include/bits/errno.h: + +/usr/include/linux/errno.h: + +/usr/include/asm/errno.h: + +/usr/include/asm-generic/errno.h: + +/usr/include/asm-generic/errno-base.h: + +/usr/include/unistd.h: + +/usr/include/bits/posix_opt.h: + +/usr/include/bits/environments.h: + +/usr/include/bits/confname.h: + +/usr/include/getopt.h: diff --git a/cinelerra-5.1/plugins/motion-cv/.deps/motionwindow.Plo b/cinelerra-5.1/plugins/motion-cv/.deps/motionwindow.Plo new file mode 100644 index 00000000..08a9f0a6 --- /dev/null +++ b/cinelerra-5.1/plugins/motion-cv/.deps/motionwindow.Plo @@ -0,0 +1,621 @@ +motionwindow.lo: motionwindow.C /usr/include/stdc-predef.h \ + ../../guicast/bcdisplayinfo.h /usr/include/stdio.h \ + /usr/include/features.h /usr/include/sys/cdefs.h \ + /usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \ + /usr/include/gnu/stubs-64.h \ + /usr/lib/gcc/x86_64-redhat-linux/5.3.1/include/stddef.h \ + /usr/include/bits/types.h /usr/include/bits/typesizes.h \ + /usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \ + /usr/lib/gcc/x86_64-redhat-linux/5.3.1/include/stdarg.h \ + /usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \ + /usr/include/stdlib.h /usr/include/bits/waitflags.h \ + /usr/include/bits/waitstatus.h /usr/include/endian.h \ + /usr/include/bits/endian.h /usr/include/bits/byteswap.h \ + /usr/include/bits/byteswap-16.h /usr/include/xlocale.h \ + /usr/include/sys/types.h /usr/include/time.h /usr/include/sys/select.h \ + /usr/include/bits/select.h /usr/include/bits/sigset.h \ + /usr/include/bits/time.h /usr/include/sys/sysmacros.h \ + /usr/include/bits/pthreadtypes.h /usr/include/alloca.h \ + /usr/include/bits/stdlib-float.h /usr/include/X11/Xlib.h \ + /usr/include/X11/X.h /usr/include/X11/Xfuncproto.h \ + /usr/include/X11/Xosdefs.h ../../guicast/clip.h ../../guicast/language.h \ + /usr/include/libintl.h motion.h /usr/include/math.h \ + /usr/include/bits/math-vector.h /usr/include/bits/libm-simd-decl-stubs.h \ + /usr/include/bits/huge_val.h /usr/include/bits/huge_valf.h \ + /usr/include/bits/huge_vall.h /usr/include/bits/inf.h \ + /usr/include/bits/nan.h /usr/include/bits/mathdef.h \ + /usr/include/bits/mathcalls.h \ + /usr/lib/gcc/x86_64-redhat-linux/5.3.1/include/stdint.h \ + /usr/include/stdint.h /usr/include/bits/wchar.h /usr/include/string.h \ + ../../cinelerra/affine.inc ../../guicast/bchash.inc \ + ../../cinelerra/filexml.inc ../../cinelerra/keyframe.inc \ + ../../cinelerra/loadbalance.h ../../guicast/condition.inc \ + ../../guicast/mutex.inc ../../guicast/thread.h /usr/include/pthread.h \ + /usr/include/sched.h /usr/include/bits/sched.h /usr/include/bits/timex.h \ + /usr/include/bits/setjmp.h motionwindow.inc \ + ../../cinelerra/overlayframe.inc ../../cinelerra/pluginvclient.h \ + ../../cinelerra/maxbuffers.h ../../cinelerra/pluginclient.h \ + ../../guicast/arraylist.h ../../guicast/condition.h \ + ../../cinelerra/edlsession.inc ../../cinelerra/keyframe.h \ + ../../cinelerra/auto.h ../../cinelerra/auto.inc ../../cinelerra/edl.inc \ + ../../guicast/guicast.h ../../guicast/bcbar.h \ + ../../guicast/bcsubwindow.h ../../guicast/arraylist.h \ + ../../guicast/bcwindowbase.h ../../config.h ../../guicast/bcbar.inc \ + ../../guicast/bcbitmap.inc ../../guicast/bcbutton.inc \ + ../../guicast/bccapture.inc ../../guicast/bcclipboard.inc \ + ../../guicast/bccmodels.inc ../../guicast/bcdragwindow.inc \ + ../../guicast/bcfilebox.inc ../../guicast/bclistbox.inc \ + ../../guicast/bcmenubar.inc ../../guicast/bcmeter.inc \ + ../../guicast/bcpan.inc ../../guicast/bcpbuffer.inc \ + ../../guicast/bcpixmap.inc ../../guicast/bcpopup.inc \ + ../../guicast/bcpopupmenu.inc ../../guicast/bcpot.inc \ + ../../guicast/bcprogress.inc ../../guicast/bcrelocatablewidget.h \ + ../../guicast/bcrepeater.inc ../../guicast/bcresources.inc \ + ../../guicast/bcscrollbar.inc ../../guicast/bcslider.inc \ + ../../guicast/bcsubwindow.inc ../../guicast/bcsynchronous.inc \ + ../../guicast/bctextbox.inc ../../guicast/bctimer.inc \ + ../../guicast/bctitle.inc ../../guicast/bctoggle.inc \ + ../../guicast/bctumble.inc ../../guicast/bcwidgetgrid.inc \ + ../../guicast/bcwindow.inc ../../guicast/bcwindowbase.inc \ + ../../guicast/bcwindowevents.inc ../../guicast/condition.inc \ + ../../guicast/bchash.inc ../../guicast/linklist.h \ + ../../guicast/mutex.inc ../../guicast/vframe.inc \ + /usr/include/X11/Xatom.h /usr/include/X11/Xft/Xft.h \ + /usr/include/freetype2/ft2build.h \ + /usr/include/freetype2/config/ftheader.h \ + /usr/include/freetype2/freetype.h \ + /usr/include/freetype2/config/ftconfig.h \ + /usr/include/freetype2/config/ftconfig-64.h \ + /usr/include/freetype2/config/ftoption.h \ + /usr/include/freetype2/config/ftstdlib.h \ + /usr/lib/gcc/x86_64-redhat-linux/5.3.1/include/limits.h \ + /usr/lib/gcc/x86_64-redhat-linux/5.3.1/include/syslimits.h \ + /usr/include/limits.h /usr/include/bits/posix1_lim.h \ + /usr/include/bits/local_lim.h /usr/include/linux/limits.h \ + /usr/include/bits/posix2_lim.h /usr/include/bits/xopen_lim.h \ + /usr/include/setjmp.h /usr/include/freetype2/fttypes.h \ + /usr/include/freetype2/ftsystem.h /usr/include/freetype2/ftimage.h \ + /usr/include/freetype2/fterrors.h /usr/include/freetype2/ftmoderr.h \ + /usr/include/freetype2/fterrdef.h /usr/include/fontconfig/fontconfig.h \ + /usr/include/sys/stat.h /usr/include/bits/stat.h \ + /usr/include/X11/extensions/Xrender.h /usr/include/X11/Xutil.h \ + /usr/include/X11/keysym.h /usr/include/X11/keysymdef.h \ + /usr/include/X11/extensions/render.h /usr/include/X11/Xdefs.h \ + /usr/include/X11/Xft/XftCompat.h /usr/include/X11/cursorfont.h \ + /usr/include/X11/extensions/xf86vmode.h /usr/include/X11/Xmd.h \ + /usr/include/X11/extensions/xf86vm.h /usr/include/GL/glx.h \ + /usr/include/GL/gl.h /usr/include/GL/glext.h /usr/include/inttypes.h \ + /usr/include/GL/glxext.h ../../guicast/bcbitmap.h /usr/include/sys/ipc.h \ + /usr/include/bits/ipctypes.h /usr/include/bits/ipc.h \ + /usr/include/sys/shm.h /usr/include/bits/shm.h \ + /usr/include/X11/extensions/XShm.h /usr/include/X11/extensions/shm.h \ + /usr/include/X11/extensions/Xvlib.h /usr/include/X11/extensions/Xv.h \ + ../../guicast/colors.h ../../guicast/sizes.h ../../guicast/bcbutton.h \ + ../../guicast/bcclipboard.h ../../guicast/thread.h \ + ../../guicast/bcdragwindow.h ../../guicast/bcpopup.h \ + ../../guicast/bclistboxitem.h ../../guicast/vframe.h \ + ../../guicast/bctexture.inc ../../quicktime/colormodels.h \ + ../../guicast/bccmodels.h ../../guicast/bcpan.h \ + ../../guicast/rotateframe.inc ../../guicast/bcfilebox.h \ + ../../guicast/bcdelete.inc ../../guicast/bclistboxitem.inc \ + ../../guicast/bcnewfolder.inc ../../guicast/bctextbox.h \ + ../../guicast/bclistbox.h ../../guicast/bcscrollbar.h \ + ../../guicast/bctoggle.h ../../guicast/fonts.h ../../guicast/bctumble.h \ + ../../guicast/bcwindow.h ../../guicast/filesystem.inc \ + ../../guicast/bcmenu.h ../../guicast/bcmenuitem.inc \ + ../../guicast/bcmenupopup.inc ../../guicast/bcmenubar.h \ + ../../guicast/bcmenu.inc ../../guicast/bcmenuitem.h \ + ../../guicast/bcmenupopup.h ../../guicast/bcmeter.h \ + ../../guicast/units.h ../../guicast/bcpopupmenu.h ../../guicast/bcpot.h \ + ../../guicast/bcpixmap.h ../../guicast/bcprogress.h \ + ../../guicast/bcrecentlist.h ../../guicast/bcresources.h \ + ../../guicast/bcdisplayinfo.inc ../../guicast/bcfontentry.inc \ + ../../guicast/bcsignals.inc ../../guicast/hashcache.inc \ + ../../guicast/bcslider.h ../../guicast/bcsynchronous.h \ + ../../guicast/bctexture.h ../../guicast/bctheme.h \ + ../../guicast/bctitle.h ../../guicast/bctimer.h /usr/include/sys/time.h \ + ../../guicast/error.h ../../guicast/debug.h ../../cinelerra/filexml.inc \ + ../../cinelerra/autos.inc ../../cinelerra/keyframes.inc \ + ../../cinelerra/messages.inc ../../cinelerra/mainprogress.inc \ + ../../cinelerra/plugincommands.h ../../cinelerra/pluginserver.inc \ + ../../cinelerra/theme.inc ../../guicast/vframe.h \ + ../../guicast/vframe.inc ../../guicast/rotateframe.inc motionwindow.h \ + motion.inc ../../cinelerra/pluginwindow.h ../../guicast/bcwindow.h + +/usr/include/stdc-predef.h: + +../../guicast/bcdisplayinfo.h: + +/usr/include/stdio.h: + +/usr/include/features.h: + +/usr/include/sys/cdefs.h: + +/usr/include/bits/wordsize.h: + +/usr/include/gnu/stubs.h: + +/usr/include/gnu/stubs-64.h: + +/usr/lib/gcc/x86_64-redhat-linux/5.3.1/include/stddef.h: + +/usr/include/bits/types.h: + +/usr/include/bits/typesizes.h: + +/usr/include/libio.h: + +/usr/include/_G_config.h: + +/usr/include/wchar.h: + +/usr/lib/gcc/x86_64-redhat-linux/5.3.1/include/stdarg.h: + +/usr/include/bits/stdio_lim.h: + +/usr/include/bits/sys_errlist.h: + +/usr/include/stdlib.h: + +/usr/include/bits/waitflags.h: + +/usr/include/bits/waitstatus.h: + +/usr/include/endian.h: + +/usr/include/bits/endian.h: + +/usr/include/bits/byteswap.h: + +/usr/include/bits/byteswap-16.h: + +/usr/include/xlocale.h: + +/usr/include/sys/types.h: + +/usr/include/time.h: + +/usr/include/sys/select.h: + +/usr/include/bits/select.h: + +/usr/include/bits/sigset.h: + +/usr/include/bits/time.h: + +/usr/include/sys/sysmacros.h: + +/usr/include/bits/pthreadtypes.h: + +/usr/include/alloca.h: + +/usr/include/bits/stdlib-float.h: + +/usr/include/X11/Xlib.h: + +/usr/include/X11/X.h: + +/usr/include/X11/Xfuncproto.h: + +/usr/include/X11/Xosdefs.h: + +../../guicast/clip.h: + +../../guicast/language.h: + +/usr/include/libintl.h: + +motion.h: + +/usr/include/math.h: + +/usr/include/bits/math-vector.h: + +/usr/include/bits/libm-simd-decl-stubs.h: + +/usr/include/bits/huge_val.h: + +/usr/include/bits/huge_valf.h: + +/usr/include/bits/huge_vall.h: + +/usr/include/bits/inf.h: + +/usr/include/bits/nan.h: + +/usr/include/bits/mathdef.h: + +/usr/include/bits/mathcalls.h: + +/usr/lib/gcc/x86_64-redhat-linux/5.3.1/include/stdint.h: + +/usr/include/stdint.h: + +/usr/include/bits/wchar.h: + +/usr/include/string.h: + +../../cinelerra/affine.inc: + +../../guicast/bchash.inc: + +../../cinelerra/filexml.inc: + +../../cinelerra/keyframe.inc: + +../../cinelerra/loadbalance.h: + +../../guicast/condition.inc: + +../../guicast/mutex.inc: + +../../guicast/thread.h: + +/usr/include/pthread.h: + +/usr/include/sched.h: + +/usr/include/bits/sched.h: + +/usr/include/bits/timex.h: + +/usr/include/bits/setjmp.h: + +motionwindow.inc: + +../../cinelerra/overlayframe.inc: + +../../cinelerra/pluginvclient.h: + +../../cinelerra/maxbuffers.h: + +../../cinelerra/pluginclient.h: + +../../guicast/arraylist.h: + +../../guicast/condition.h: + +../../cinelerra/edlsession.inc: + +../../cinelerra/keyframe.h: + +../../cinelerra/auto.h: + +../../cinelerra/auto.inc: + +../../cinelerra/edl.inc: + +../../guicast/guicast.h: + +../../guicast/bcbar.h: + +../../guicast/bcsubwindow.h: + +../../guicast/arraylist.h: + +../../guicast/bcwindowbase.h: + +../../config.h: + +../../guicast/bcbar.inc: + +../../guicast/bcbitmap.inc: + +../../guicast/bcbutton.inc: + +../../guicast/bccapture.inc: + +../../guicast/bcclipboard.inc: + +../../guicast/bccmodels.inc: + +../../guicast/bcdragwindow.inc: + +../../guicast/bcfilebox.inc: + +../../guicast/bclistbox.inc: + +../../guicast/bcmenubar.inc: + +../../guicast/bcmeter.inc: + +../../guicast/bcpan.inc: + +../../guicast/bcpbuffer.inc: + +../../guicast/bcpixmap.inc: + +../../guicast/bcpopup.inc: + +../../guicast/bcpopupmenu.inc: + +../../guicast/bcpot.inc: + +../../guicast/bcprogress.inc: + +../../guicast/bcrelocatablewidget.h: + +../../guicast/bcrepeater.inc: + +../../guicast/bcresources.inc: + +../../guicast/bcscrollbar.inc: + +../../guicast/bcslider.inc: + +../../guicast/bcsubwindow.inc: + +../../guicast/bcsynchronous.inc: + +../../guicast/bctextbox.inc: + +../../guicast/bctimer.inc: + +../../guicast/bctitle.inc: + +../../guicast/bctoggle.inc: + +../../guicast/bctumble.inc: + +../../guicast/bcwidgetgrid.inc: + +../../guicast/bcwindow.inc: + +../../guicast/bcwindowbase.inc: + +../../guicast/bcwindowevents.inc: + +../../guicast/condition.inc: + +../../guicast/bchash.inc: + +../../guicast/linklist.h: + +../../guicast/mutex.inc: + +../../guicast/vframe.inc: + +/usr/include/X11/Xatom.h: + +/usr/include/X11/Xft/Xft.h: + +/usr/include/freetype2/ft2build.h: + +/usr/include/freetype2/config/ftheader.h: + +/usr/include/freetype2/freetype.h: + +/usr/include/freetype2/config/ftconfig.h: + +/usr/include/freetype2/config/ftconfig-64.h: + +/usr/include/freetype2/config/ftoption.h: + +/usr/include/freetype2/config/ftstdlib.h: + +/usr/lib/gcc/x86_64-redhat-linux/5.3.1/include/limits.h: + +/usr/lib/gcc/x86_64-redhat-linux/5.3.1/include/syslimits.h: + +/usr/include/limits.h: + +/usr/include/bits/posix1_lim.h: + +/usr/include/bits/local_lim.h: + +/usr/include/linux/limits.h: + +/usr/include/bits/posix2_lim.h: + +/usr/include/bits/xopen_lim.h: + +/usr/include/setjmp.h: + +/usr/include/freetype2/fttypes.h: + +/usr/include/freetype2/ftsystem.h: + +/usr/include/freetype2/ftimage.h: + +/usr/include/freetype2/fterrors.h: + +/usr/include/freetype2/ftmoderr.h: + +/usr/include/freetype2/fterrdef.h: + +/usr/include/fontconfig/fontconfig.h: + +/usr/include/sys/stat.h: + +/usr/include/bits/stat.h: + +/usr/include/X11/extensions/Xrender.h: + +/usr/include/X11/Xutil.h: + +/usr/include/X11/keysym.h: + +/usr/include/X11/keysymdef.h: + +/usr/include/X11/extensions/render.h: + +/usr/include/X11/Xdefs.h: + +/usr/include/X11/Xft/XftCompat.h: + +/usr/include/X11/cursorfont.h: + +/usr/include/X11/extensions/xf86vmode.h: + +/usr/include/X11/Xmd.h: + +/usr/include/X11/extensions/xf86vm.h: + +/usr/include/GL/glx.h: + +/usr/include/GL/gl.h: + +/usr/include/GL/glext.h: + +/usr/include/inttypes.h: + +/usr/include/GL/glxext.h: + +../../guicast/bcbitmap.h: + +/usr/include/sys/ipc.h: + +/usr/include/bits/ipctypes.h: + +/usr/include/bits/ipc.h: + +/usr/include/sys/shm.h: + +/usr/include/bits/shm.h: + +/usr/include/X11/extensions/XShm.h: + +/usr/include/X11/extensions/shm.h: + +/usr/include/X11/extensions/Xvlib.h: + +/usr/include/X11/extensions/Xv.h: + +../../guicast/colors.h: + +../../guicast/sizes.h: + +../../guicast/bcbutton.h: + +../../guicast/bcclipboard.h: + +../../guicast/thread.h: + +../../guicast/bcdragwindow.h: + +../../guicast/bcpopup.h: + +../../guicast/bclistboxitem.h: + +../../guicast/vframe.h: + +../../guicast/bctexture.inc: + +../../quicktime/colormodels.h: + +../../guicast/bccmodels.h: + +../../guicast/bcpan.h: + +../../guicast/rotateframe.inc: + +../../guicast/bcfilebox.h: + +../../guicast/bcdelete.inc: + +../../guicast/bclistboxitem.inc: + +../../guicast/bcnewfolder.inc: + +../../guicast/bctextbox.h: + +../../guicast/bclistbox.h: + +../../guicast/bcscrollbar.h: + +../../guicast/bctoggle.h: + +../../guicast/fonts.h: + +../../guicast/bctumble.h: + +../../guicast/bcwindow.h: + +../../guicast/filesystem.inc: + +../../guicast/bcmenu.h: + +../../guicast/bcmenuitem.inc: + +../../guicast/bcmenupopup.inc: + +../../guicast/bcmenubar.h: + +../../guicast/bcmenu.inc: + +../../guicast/bcmenuitem.h: + +../../guicast/bcmenupopup.h: + +../../guicast/bcmeter.h: + +../../guicast/units.h: + +../../guicast/bcpopupmenu.h: + +../../guicast/bcpot.h: + +../../guicast/bcpixmap.h: + +../../guicast/bcprogress.h: + +../../guicast/bcrecentlist.h: + +../../guicast/bcresources.h: + +../../guicast/bcdisplayinfo.inc: + +../../guicast/bcfontentry.inc: + +../../guicast/bcsignals.inc: + +../../guicast/hashcache.inc: + +../../guicast/bcslider.h: + +../../guicast/bcsynchronous.h: + +../../guicast/bctexture.h: + +../../guicast/bctheme.h: + +../../guicast/bctitle.h: + +../../guicast/bctimer.h: + +/usr/include/sys/time.h: + +../../guicast/error.h: + +../../guicast/debug.h: + +../../cinelerra/filexml.inc: + +../../cinelerra/autos.inc: + +../../cinelerra/keyframes.inc: + +../../cinelerra/messages.inc: + +../../cinelerra/mainprogress.inc: + +../../cinelerra/plugincommands.h: + +../../cinelerra/pluginserver.inc: + +../../cinelerra/theme.inc: + +../../guicast/vframe.h: + +../../guicast/vframe.inc: + +../../guicast/rotateframe.inc: + +motionwindow.h: + +motion.inc: + +../../cinelerra/pluginwindow.h: + +../../guicast/bcwindow.h: diff --git a/cinelerra-5.1/plugins/motion-cv/.libs/motion.la b/cinelerra-5.1/plugins/motion-cv/.libs/motion.la new file mode 120000 index 00000000..3b77efb6 --- /dev/null +++ b/cinelerra-5.1/plugins/motion-cv/.libs/motion.la @@ -0,0 +1 @@ +../motion.la \ No newline at end of file diff --git a/cinelerra-5.1/plugins/motion-cv/.libs/motion.lai b/cinelerra-5.1/plugins/motion-cv/.libs/motion.lai new file mode 100644 index 00000000..c21dbc47 --- /dev/null +++ b/cinelerra-5.1/plugins/motion-cv/.libs/motion.lai @@ -0,0 +1,41 @@ +# motion.la - a libtool library file +# Generated by libtool (GNU libtool) 2.4.6 +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='motion.so' + +# Names of this library. +library_names='motion.so motion.so motion.so' + +# The name of the static archive. +old_library='' + +# Linker flags that cannot go in dependency_libs. +inherited_linker_flags='' + +# Libraries that this one depends upon. +dependency_libs=' -L/mnt0/build5/cinelerra-cv/thirdparty/../thirdparty/a52dec-0.7.4/liba52/.libs -L/mnt0/build5/cinelerra-cv/thirdparty/../thirdparty/faac-1.28/common/mp4v2 -L/mnt0/build5/cinelerra-cv/thirdparty/../thirdparty/faac-1.28/libfaac/.libs -L/mnt0/build5/cinelerra-cv/thirdparty/../thirdparty/faad2-2.7/common/mp4ff -L/mnt0/build5/cinelerra-cv/thirdparty/../thirdparty/faad2-2.7/libfaad/.libs -L/mnt0/build5/cinelerra-cv/thirdparty/../thirdparty/lame-3.99.5/libmp3lame/.libs -L/mnt0/build5/cinelerra-cv/thirdparty/../thirdparty/lame-3.99.5/mpglib/.libs -L/mnt0/build5/cinelerra-cv/thirdparty/../thirdparty/libuuid-1.0.3/.libs -L/mnt0/build5/cinelerra-cv/thirdparty/../thirdparty/mjpegtools-2.1.0/lavtools/.libs -L/mnt0/build5/cinelerra-cv/thirdparty/../thirdparty/mjpegtools-2.1.0/mpeg2enc/.libs -L/mnt0/build5/cinelerra-cv/thirdparty/../thirdparty/mjpegtools-2.1.0/mplex/.libs -L/mnt0/build5/cinelerra-cv/thirdparty/../thirdparty/mjpegtools-2.1.0/utils/.libs -L/mnt0/build5/cinelerra-cv/thirdparty/../thirdparty/mjpegtools-2.1.0/utils/mmxsse/.libs -L/mnt0/build5/cinelerra-cv/thirdparty/../thirdparty/mjpegtools-2.1.0/yuvfilters/.libs -L/mnt0/build5/cinelerra-cv/thirdparty/../thirdparty/x264-snapshot-20160220-2245-stable -ldl -lpthread' + +# Names of additional weak libraries provided by this library +weak_library_names='' + +# Version information for motion. +current=0 +age=0 +revision=0 + +# Is this an already installed library? +installed=yes + +# Should we warn about portability when linking against -modules? +shouldnotlink=yes + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='/usr/local/lib/cinelerra' diff --git a/cinelerra-5.1/plugins/motion-cv/.libs/motion.so b/cinelerra-5.1/plugins/motion-cv/.libs/motion.so new file mode 100755 index 0000000000000000000000000000000000000000..2829316e352be9f510d87a37ce774ff494bd620e GIT binary patch literal 315344 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 u^)f`86N%-8hs7^a>9qmq4ang|C4_F^#*^N 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 literal 0 HcmV?d00001 diff --git a/cinelerra-5.1/plugins/motion-cv/Makefile b/cinelerra-5.1/plugins/motion-cv/Makefile new file mode 100644 index 00000000..a0900168 --- /dev/null +++ b/cinelerra-5.1/plugins/motion-cv/Makefile @@ -0,0 +1,13 @@ +include ../../plugin_defs + +OBJS := \ + $(OBJDIR)/motion-cv.o \ + $(OBJDIR)/motionwindow-cv.o + +PLUGIN = motion-cv + +include ../../plugin_config + +$(OBJDIR)/motion-cv.o: motion-cv.C +$(OBJDIR)/motionwindow-cv.o: motionwindow-cv.C + diff --git a/cinelerra-5.1/plugins/motion-cv/motion-cv.C b/cinelerra-5.1/plugins/motion-cv/motion-cv.C new file mode 100644 index 00000000..9a702503 --- /dev/null +++ b/cinelerra-5.1/plugins/motion-cv/motion-cv.C @@ -0,0 +1,2655 @@ + +/* + * CINELERRA + * Copyright (C) 2008 Adam Williams + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "affine.h" +#include "bcdisplayinfo.h" +#include "clip.h" +#include "bchash.h" +#include "bcsignals.h" +#include "filexml.h" +#include "keyframe.h" +#include "language.h" +#include "motion-cv.h" +#include "motionwindow-cv.h" +#include "mutex.h" +#include "overlayframe.h" +#include "rotateframe.h" +#include "transportque.h" + + +#include +#include + +REGISTER_PLUGIN(MotionCVMain) + +//#undef DEBUG + +#ifndef DEBUG +#define DEBUG +#endif + + +MotionCVConfig::MotionCVConfig() +{ + global_range_w = 5; + global_range_h = 5; + rotation_range = 5; + block_count = 1; + global_block_w = MIN_BLOCK; + global_block_h = MIN_BLOCK; + rotation_block_w = MIN_BLOCK; + rotation_block_h = MIN_BLOCK; + block_x = 50; + block_y = 50; + global_positions = 256; + rotate_positions = 4; + magnitude = 100; + return_speed = 0; + mode1 = STABILIZE; + global = 1; + rotate = 1; + addtrackedframeoffset = 0; + strcpy(tracking_file, TRACKING_FILE); + mode2 = RECALCULATE; + draw_vectors = 1; + mode3 = MotionCVConfig::TRACK_SINGLE; + track_frame = 0; + bottom_is_master = 1; + horizontal_only = 0; + vertical_only = 0; +} + +void MotionCVConfig::boundaries() +{ + CLAMP(global_range_w, MIN_RADIUS, MAX_RADIUS); + CLAMP(global_range_h, MIN_RADIUS, MAX_RADIUS); + CLAMP(rotation_range, MIN_ROTATION, MAX_ROTATION); + CLAMP(block_count, MIN_BLOCKS, MAX_BLOCKS); + CLAMP(global_block_w, MIN_BLOCK, MAX_BLOCK); + CLAMP(global_block_h, MIN_BLOCK, MAX_BLOCK); + CLAMP(rotation_block_w, MIN_BLOCK, MAX_BLOCK); + CLAMP(rotation_block_h, MIN_BLOCK, MAX_BLOCK); +} + +int MotionCVConfig::equivalent(MotionCVConfig &that) +{ + return global_range_w == that.global_range_w && + global_range_h == that.global_range_h && + rotation_range == that.rotation_range && + mode1 == that.mode1 && + global == that.global && + rotate == that.rotate && + addtrackedframeoffset == that.addtrackedframeoffset && + !strcmp(tracking_file, that.tracking_file) && + draw_vectors == that.draw_vectors && + block_count == that.block_count && + global_block_w == that.global_block_w && + global_block_h == that.global_block_h && + rotation_block_w == that.rotation_block_w && + rotation_block_h == that.rotation_block_h && + EQUIV(block_x, that.block_x) && + EQUIV(block_y, that.block_y) && + global_positions == that.global_positions && + rotate_positions == that.rotate_positions && + magnitude == that.magnitude && + return_speed == that.return_speed && + mode3 == that.mode3 && + track_frame == that.track_frame && + bottom_is_master == that.bottom_is_master && + horizontal_only == that.horizontal_only && + vertical_only == that.vertical_only; +} + +void MotionCVConfig::copy_from(MotionCVConfig &that) +{ + global_range_w = that.global_range_w; + global_range_h = that.global_range_h; + rotation_range = that.rotation_range; + mode1 = that.mode1; + global = that.global; + rotate = that.rotate; + addtrackedframeoffset = that.addtrackedframeoffset; + strcpy(tracking_file, that.tracking_file); + mode2 = that.mode2; + draw_vectors = that.draw_vectors; + block_count = that.block_count; + block_x = that.block_x; + block_y = that.block_y; + global_positions = that.global_positions; + rotate_positions = that.rotate_positions; + global_block_w = that.global_block_w; + global_block_h = that.global_block_h; + rotation_block_w = that.rotation_block_w; + rotation_block_h = that.rotation_block_h; + magnitude = that.magnitude; + return_speed = that.return_speed; + mode3 = that.mode3; + track_frame = that.track_frame; + bottom_is_master = that.bottom_is_master; + horizontal_only = that.horizontal_only; + vertical_only = that.vertical_only; +} + +void MotionCVConfig::interpolate(MotionCVConfig &prev, MotionCVConfig &next, + int64_t prev_frame, int64_t next_frame, int64_t current_frame) +{ + copy_from(prev); +} + +MotionCVMain::MotionCVMain(PluginServer *server) + : PluginVClient(server) +{ + engine = 0; + rotate_engine = 0; + motion_rotate = 0; + total_dx = 0; + total_dy = 0; + total_angle = 0; + overlayer = 0; + search_area = 0; + search_size = 0; + temp_frame = 0; + previous_frame_number = -1; + + prev_global_ref = 0; + current_global_ref = 0; + global_target_src = 0; + global_target_dst = 0; + + active_fp = 0; + active_file[0] = 0; + tracking_frame = -1; + dx_offset = dy_offset = 0; + save_dx = load_dx = 0; + save_dy = load_dy = 0; + save_dt = load_dt = 0; + + prev_rotate_ref = 0; + current_rotate_ref = 0; + rotate_target_src = 0; + rotate_target_dst = 0; +} + +MotionCVMain::~MotionCVMain() +{ + + delete engine; + delete overlayer; + delete [] search_area; + delete temp_frame; + delete rotate_engine; + delete motion_rotate; + + delete prev_global_ref; + delete current_global_ref; + delete global_target_src; + delete global_target_dst; + + if( active_fp ) fclose(active_fp); + + delete prev_rotate_ref; + delete current_rotate_ref; + delete rotate_target_src; + delete rotate_target_dst; +} + +const char* MotionCVMain::plugin_title() { return _("MotionCV"); } +int MotionCVMain::is_realtime() { return 1; } +int MotionCVMain::is_multichannel() { return 1; } + + +NEW_WINDOW_MACRO(MotionCVMain, MotionCVWindow) + +LOAD_CONFIGURATION_MACRO(MotionCVMain, MotionCVConfig) + + + +void MotionCVMain::update_gui() +{ + if(thread) + { + if(load_configuration()) + { + thread->window->lock_window("MotionCVMain::update_gui"); + MotionCVWindow *window = (MotionCVWindow *)thread->window; + + char string[BCTEXTLEN]; + sprintf(string, "%d", config.global_positions); + window->global_search_positions->set_text(string); + sprintf(string, "%d", config.rotate_positions); + window->rotation_search_positions->set_text(string); + + window->global_block_w->update(config.global_block_w); + window->global_block_h->update(config.global_block_h); + window->rotation_block_w->update(config.rotation_block_w); + window->rotation_block_h->update(config.rotation_block_h); + window->block_x->update(config.block_x); + window->block_y->update(config.block_y); + window->block_x_text->update((float)config.block_x); + window->block_y_text->update((float)config.block_y); + window->magnitude->update(config.magnitude); + window->return_speed->update(config.return_speed); + + + window->track_single->update(config.mode3 == MotionCVConfig::TRACK_SINGLE); + window->track_frame_number->update(config.track_frame); + window->track_previous->update(config.mode3 == MotionCVConfig::TRACK_PREVIOUS); + window->previous_same->update(config.mode3 == MotionCVConfig::PREVIOUS_SAME_BLOCK); + if(config.mode3 != MotionCVConfig::TRACK_SINGLE) + window->track_frame_number->disable(); + else + window->track_frame_number->enable(); + + window->mode1->set_text( + Mode1::to_text(config.mode1)); + window->mode2->set_text( + Mode2::to_text(config.mode2)); + window->mode3->set_text( + Mode3::to_text(config.horizontal_only, config.vertical_only)); + window->master_layer->set_text( + MasterLayer::to_text(config.bottom_is_master)); + + + window->update_mode(); + window->unlock_window(); + } + } +} + + + + +void MotionCVMain::save_data(KeyFrame *keyframe) +{ + FileXML output; + +// cause data to be stored directly in text + output.set_shared_output(keyframe->get_data(), MESSAGESIZE); + output.tag.set_title("MOTION"); + + output.tag.set_property("BLOCK_COUNT", config.block_count); + output.tag.set_property("GLOBAL_POSITIONS", config.global_positions); + output.tag.set_property("ROTATE_POSITIONS", config.rotate_positions); + output.tag.set_property("GLOBAL_BLOCK_W", config.global_block_w); + output.tag.set_property("GLOBAL_BLOCK_H", config.global_block_h); + output.tag.set_property("ROTATION_BLOCK_W", config.rotation_block_w); + output.tag.set_property("ROTATION_BLOCK_H", config.rotation_block_h); + output.tag.set_property("BLOCK_X", config.block_x); + output.tag.set_property("BLOCK_Y", config.block_y); + output.tag.set_property("GLOBAL_RANGE_W", config.global_range_w); + output.tag.set_property("GLOBAL_RANGE_H", config.global_range_h); + output.tag.set_property("ROTATION_RANGE", config.rotation_range); + output.tag.set_property("MAGNITUDE", config.magnitude); + output.tag.set_property("RETURN_SPEED", config.return_speed); + output.tag.set_property("MODE1", config.mode1); + output.tag.set_property("GLOBAL", config.global); + output.tag.set_property("ROTATE", config.rotate); + output.tag.set_property("ADDTRACKEDFRAMEOFFSET", config.addtrackedframeoffset); + output.tag.set_property("TRACKING_FILE", config.tracking_file); + output.tag.set_property("MODE2", config.mode2); + output.tag.set_property("DRAW_VECTORS", config.draw_vectors); + output.tag.set_property("MODE3", config.mode3); + output.tag.set_property("TRACK_FRAME", config.track_frame); + output.tag.set_property("BOTTOM_IS_MASTER", config.bottom_is_master); + output.tag.set_property("HORIZONTAL_ONLY", config.horizontal_only); + output.tag.set_property("VERTICAL_ONLY", config.vertical_only); + output.append_tag(); + output.tag.set_title("/MOTION"); + output.append_tag(); + output.terminate_string(); +} + +void MotionCVMain::read_data(KeyFrame *keyframe) +{ + FileXML input; + + input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data())); + + int result = 0; + + while(!result) + { + result = input.read_tag(); + + if(!result) + { + if(input.tag.title_is("MOTION")) + { + config.block_count = input.tag.get_property("BLOCK_COUNT", config.block_count); + config.global_positions = input.tag.get_property("GLOBAL_POSITIONS", config.global_positions); + config.rotate_positions = input.tag.get_property("ROTATE_POSITIONS", config.rotate_positions); + config.global_block_w = input.tag.get_property("GLOBAL_BLOCK_W", config.global_block_w); + config.global_block_h = input.tag.get_property("GLOBAL_BLOCK_H", config.global_block_h); + config.rotation_block_w = input.tag.get_property("ROTATION_BLOCK_W", config.rotation_block_w); + config.rotation_block_h = input.tag.get_property("ROTATION_BLOCK_H", config.rotation_block_h); + config.block_x = input.tag.get_property("BLOCK_X", config.block_x); + config.block_y = input.tag.get_property("BLOCK_Y", config.block_y); + config.global_range_w = input.tag.get_property("GLOBAL_RANGE_W", config.global_range_w); + config.global_range_h = input.tag.get_property("GLOBAL_RANGE_H", config.global_range_h); + config.rotation_range = input.tag.get_property("ROTATION_RANGE", config.rotation_range); + config.magnitude = input.tag.get_property("MAGNITUDE", config.magnitude); + config.return_speed = input.tag.get_property("RETURN_SPEED", config.return_speed); + config.mode1 = input.tag.get_property("MODE1", config.mode1); + config.global = input.tag.get_property("GLOBAL", config.global); + config.rotate = input.tag.get_property("ROTATE", config.rotate); + config.addtrackedframeoffset = input.tag.get_property("ADDTRACKEDFRAMEOFFSET", config.addtrackedframeoffset); + input.tag.get_property("TRACKING_FILE", config.tracking_file); + config.mode2 = input.tag.get_property("MODE2", config.mode2); + config.draw_vectors = input.tag.get_property("DRAW_VECTORS", config.draw_vectors); + config.mode3 = input.tag.get_property("MODE3", config.mode3); + config.track_frame = input.tag.get_property("TRACK_FRAME", config.track_frame); + config.bottom_is_master = input.tag.get_property("BOTTOM_IS_MASTER", config.bottom_is_master); + config.horizontal_only = input.tag.get_property("HORIZONTAL_ONLY", config.horizontal_only); + config.vertical_only = input.tag.get_property("VERTICAL_ONLY", config.vertical_only); + } + } + } + config.boundaries(); +} + + + + + + + + + +void MotionCVMain::allocate_temp(int w, int h, int color_model) +{ + if(temp_frame && + (temp_frame->get_w() != w || + temp_frame->get_h() != h)) + { + delete temp_frame; + temp_frame = 0; + } + if(!temp_frame) + temp_frame = new VFrame(w, h, color_model); +} + + + +void MotionCVMain::process_global() +{ + if(!engine) engine = new MotionCVScan(this, + PluginClient::get_project_smp() + 1, + PluginClient::get_project_smp() + 1); + +// Get the current motion vector between the previous and current frame + engine->scan_frame(current_global_ref, prev_global_ref); + current_dx = engine->dx_result; + current_dy = engine->dy_result; + +// Add current motion vector to accumulation vector. + if(config.mode3 != MotionCVConfig::TRACK_SINGLE) + { +// Retract over time + total_dx = (int64_t)total_dx * (100 - config.return_speed) / 100; + total_dy = (int64_t)total_dy * (100 - config.return_speed) / 100; + total_dx += engine->dx_result; + total_dy += engine->dy_result; + } + else +// Make accumulation vector current + { + total_dx = engine->dx_result; + total_dy = engine->dy_result; + } + +// Clamp accumulation vector + if(config.magnitude < 100) + { + //int block_w = (int64_t)config.global_block_w * + // current_global_ref->get_w() / 100; + //int block_h = (int64_t)config.global_block_h * + // current_global_ref->get_h() / 100; + int block_x_orig = (int64_t)(config.block_x * + current_global_ref->get_w() / + 100); + int block_y_orig = (int64_t)(config.block_y * + current_global_ref->get_h() / + 100); + + int max_block_x = (int64_t)(current_global_ref->get_w() - block_x_orig) * + OVERSAMPLE * + config.magnitude / + 100; + int max_block_y = (int64_t)(current_global_ref->get_h() - block_y_orig) * + OVERSAMPLE * + config.magnitude / + 100; + int min_block_x = (int64_t)-block_x_orig * + OVERSAMPLE * + config.magnitude / + 100; + int min_block_y = (int64_t)-block_y_orig * + OVERSAMPLE * + config.magnitude / + 100; + + CLAMP(total_dx, min_block_x, max_block_x); + CLAMP(total_dy, min_block_y, max_block_y); + } + +#ifdef DEBUG +printf("MotionCVMain::process_global 2 total_dx=%.02f total_dy=%.02f\n", +(float)total_dx / OVERSAMPLE, +(float)total_dy / OVERSAMPLE); +#endif + + if(config.mode3 != MotionCVConfig::TRACK_SINGLE && !config.rotate) + { +// Transfer current reference frame to previous reference frame and update +// counter. Must wait for rotate to compare. + prev_global_ref->copy_from(current_global_ref); + previous_frame_number = get_source_position(); + } + +// Decide what to do with target based on requested operation + int interpolation = NEAREST_NEIGHBOR; + float dx = 0; + float dy = 0; + switch(config.mode1) + { + case MotionCVConfig::NOTHING: + global_target_dst->copy_from(global_target_src); + break; + case MotionCVConfig::TRACK_PIXEL: + interpolation = NEAREST_NEIGHBOR; + dx = (int)(total_dx / OVERSAMPLE); + dy = (int)(total_dy / OVERSAMPLE); + break; + case MotionCVConfig::STABILIZE_PIXEL: + interpolation = NEAREST_NEIGHBOR; + dx = -(int)(total_dx / OVERSAMPLE); + dy = -(int)(total_dy / OVERSAMPLE); + break; + break; + case MotionCVConfig::TRACK: + interpolation = CUBIC_LINEAR; + dx = (float)total_dx / OVERSAMPLE; + dy = (float)total_dy / OVERSAMPLE; + break; + case MotionCVConfig::STABILIZE: + interpolation = CUBIC_LINEAR; + dx = -(float)total_dx / OVERSAMPLE; + dy = -(float)total_dy / OVERSAMPLE; + break; + } + + + if(config.mode1 != MotionCVConfig::NOTHING) + { + if(!overlayer) + overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1); + global_target_dst->clear_frame(); + overlayer->overlay(global_target_dst, + global_target_src, + 0, + 0, + global_target_src->get_w(), + global_target_src->get_h(), + dx, + dy, + (float)global_target_src->get_w() + dx, + (float)global_target_src->get_h() + dy, + 1, + TRANSFER_REPLACE, + interpolation); + } +} + + + +void MotionCVMain::process_rotation() +{ + int block_x; + int block_y; + +// Convert the previous global reference into the previous rotation reference. +// Convert global target destination into rotation target source. + if(config.global) + { + if(!overlayer) + overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1); + float dx; + float dy; + if(config.mode3 == MotionCVConfig::TRACK_SINGLE) + { + dx = (float)total_dx / OVERSAMPLE; + dy = (float)total_dy / OVERSAMPLE; + } + else + { + dx = (float)current_dx / OVERSAMPLE; + dy = (float)current_dy / OVERSAMPLE; + } + + prev_rotate_ref->clear_frame(); + overlayer->overlay(prev_rotate_ref, + prev_global_ref, + 0, + 0, + prev_global_ref->get_w(), + prev_global_ref->get_h(), + dx, + dy, + (float)prev_global_ref->get_w() + dx, + (float)prev_global_ref->get_h() + dy, + 1, + TRANSFER_REPLACE, + CUBIC_LINEAR); +// Pivot is destination global position + block_x = (int)(prev_rotate_ref->get_w() * + config.block_x / + 100 + + (float)total_dx / + OVERSAMPLE); + block_y = (int)(prev_rotate_ref->get_h() * + config.block_y / + 100 + + (float)total_dy / + OVERSAMPLE); +// Use the global target output as the rotation target input + rotate_target_src->copy_from(global_target_dst); +// Transfer current reference frame to previous reference frame for global. + if(config.mode3 != MotionCVConfig::TRACK_SINGLE) + { + prev_global_ref->copy_from(current_global_ref); + previous_frame_number = get_source_position(); + } + } + else + { +// Pivot is fixed + block_x = (int)(prev_rotate_ref->get_w() * + config.block_x / + 100); + block_y = (int)(prev_rotate_ref->get_h() * + config.block_y / + 100); + } + + + +// Get rotation + if(!motion_rotate) + motion_rotate = new RotateCVScan(this, + get_project_smp() + 1, + get_project_smp() + 1); + + current_angle = motion_rotate->scan_frame(prev_rotate_ref, + current_rotate_ref, + block_x, + block_y); + + + +// Add current rotation to accumulation + if(config.mode3 != MotionCVConfig::TRACK_SINGLE) + { +// Retract over time + total_angle = total_angle * (100 - config.return_speed) / 100; + total_angle += current_angle; + + if(!config.global) + { +// Transfer current reference frame to previous reference frame and update +// counter. + prev_rotate_ref->copy_from(current_rotate_ref); + previous_frame_number = get_source_position(); + } + } + else + { + total_angle = current_angle; + } + +#ifdef DEBUG +printf("MotionCVMain::process_rotation total_angle=%f\n", total_angle); +#endif + + +// Calculate rotation parameters based on requested operation + float angle = 0; + switch(config.mode1) + { + case MotionCVConfig::NOTHING: + rotate_target_dst->copy_from(rotate_target_src); + break; + case MotionCVConfig::TRACK: + case MotionCVConfig::TRACK_PIXEL: + angle = total_angle; + break; + case MotionCVConfig::STABILIZE: + case MotionCVConfig::STABILIZE_PIXEL: + angle = -total_angle; + break; + } + + + + if(config.mode1 != MotionCVConfig::NOTHING) + { + if(!rotate_engine) + rotate_engine = new AffineEngine(PluginClient::get_project_smp() + 1, + PluginClient::get_project_smp() + 1); + + rotate_target_dst->clear_frame(); + +// Determine pivot based on a number of factors. + switch(config.mode1) + { + case MotionCVConfig::TRACK: + case MotionCVConfig::TRACK_PIXEL: +// Use destination of global tracking. + rotate_engine->set_pivot(block_x, block_y); + break; + + case MotionCVConfig::STABILIZE: + case MotionCVConfig::STABILIZE_PIXEL: + if(config.global) + { +// Use origin of global stabilize operation + rotate_engine->set_pivot((int)(rotate_target_dst->get_w() * + config.block_x / + 100), + (int)(rotate_target_dst->get_h() * + config.block_y / + 100)); + + } + else + { +// Use origin + rotate_engine->set_pivot(block_x, block_y); + } + break; + } + + + rotate_engine->rotate(rotate_target_dst, rotate_target_src, angle); +// overlayer->overlay(rotate_target_dst, +// prev_rotate_ref, +// 0, +// 0, +// prev_rotate_ref->get_w(), +// prev_rotate_ref->get_h(), +// 0, +// 0, +// prev_rotate_ref->get_w(), +// prev_rotate_ref->get_h(), +// 1, +// TRANSFER_NORMAL, +// CUBIC_LINEAR); +// overlayer->overlay(rotate_target_dst, +// current_rotate_ref, +// 0, +// 0, +// prev_rotate_ref->get_w(), +// prev_rotate_ref->get_h(), +// 0, +// 0, +// prev_rotate_ref->get_w(), +// prev_rotate_ref->get_h(), +// 1, +// TRANSFER_NORMAL, +// CUBIC_LINEAR); + + + } + + +} + + + + + + + + + +int MotionCVMain::process_buffer(VFrame **frame, + int64_t start_position, + double frame_rate) +{ + int prev_config_mode2 = config.mode2; + int need_reconfigure = load_configuration(); + int color_model = frame[0]->get_color_model(); + w = frame[0]->get_w(); + h = frame[0]->get_h(); + + +#ifdef DEBUG +printf("MotionCVMain::process_buffer 1 start_position=%jd\n", start_position); +#endif + + +// Calculate the source and destination pointers for each of the operations. +// Get the layer to track motion in. + reference_layer = config.bottom_is_master ? + PluginClient::total_in_buffers - 1 : + 0; +// Get the layer to apply motion in. + target_layer = config.bottom_is_master ? + 0 : + PluginClient::total_in_buffers - 1; + + + output_frame = frame[target_layer]; + + +// Get the position of previous reference frame. + int64_t actual_previous_number; +// Skip if match frame not available + int skip_current = 0; + + + if(config.mode3 == MotionCVConfig::TRACK_SINGLE) + { + actual_previous_number = config.track_frame; + if(get_direction() == PLAY_REVERSE) + actual_previous_number++; + if(actual_previous_number == start_position) + skip_current = 1; + } + else + { + actual_previous_number = start_position; + if(get_direction() == PLAY_FORWARD) + { + actual_previous_number--; + if(actual_previous_number < get_source_start()) + skip_current = 1; + else + { + KeyFrame *keyframe = get_prev_keyframe(start_position, 1); + if(keyframe->position > 0 && + actual_previous_number < keyframe->position) + skip_current = 1; + } + } + else + { + actual_previous_number++; + if(actual_previous_number >= get_source_start() + get_total_len()) + skip_current = 1; + else + { + KeyFrame *keyframe = get_next_keyframe(start_position, 1); + if(keyframe->position > 0 && + actual_previous_number >= keyframe->position) + skip_current = 1; + } + } + +// Only count motion since last keyframe + + + } + + + if(!config.global && !config.rotate) skip_current = 1; + + + + +// printf("process_realtime %d %lld %lld\n", +// skip_current, +// previous_frame_number, +// actual_previous_number); +// Load match frame and reset vectors + int need_reload = !skip_current && + (previous_frame_number != actual_previous_number || + need_reconfigure); + if(need_reload) + { + total_dx = 0; + total_dy = 0; + total_angle = 0; + previous_frame_number = actual_previous_number; + } + if( prev_config_mode2 != MotionCVConfig::SAVE && + config.mode2 == MotionCVConfig::SAVE ) { +#ifdef DEBUG +printf("MotionCVMain::process_buffer 2 remove tracking file: %s\n", config.tracking_file); +#endif + ::remove(config.tracking_file); + } + + + if(skip_current) + { + total_dx = 0; + total_dy = 0; + current_dx = 0; + current_dy = 0; + total_angle = 0; + current_angle = 0; + } + + + + +// Get the global pointers. Here we walk through the sequence of events. + if(config.global) + { +// Assume global only. Global reads previous frame and compares +// with current frame to get the current translation. +// The center of the search area is fixed in compensate mode or +// the user value + the accumulation vector in track mode. + if(!prev_global_ref) + prev_global_ref = new VFrame(w, h, color_model); + if(!current_global_ref) + current_global_ref = new VFrame(w, h, color_model); + +// Global loads the current target frame into the src and +// writes it to the dst frame with desired translation. + if(!global_target_src) + global_target_src = new VFrame(w, h, color_model); + if(!global_target_dst) + global_target_dst = new VFrame(w, h, color_model); + + +// Load the global frames + if(need_reload) + { + read_frame(prev_global_ref, + reference_layer, + previous_frame_number, + frame_rate, + 0); + } + + read_frame(current_global_ref, + reference_layer, + start_position, + frame_rate, + 0); + read_frame(global_target_src, + target_layer, + start_position, + frame_rate, + 0); + + + +// Global followed by rotate + if(config.rotate) + { +// Must translate the previous global reference by the current global +// accumulation vector to match the current global reference. +// The center of the search area is always the user value + the accumulation +// vector. + if(!prev_rotate_ref) + prev_rotate_ref = new VFrame(w, h, color_model); +// The current global reference is the current rotation reference. + if(!current_rotate_ref) + current_rotate_ref = new VFrame(w, h, color_model); + current_rotate_ref->copy_from(current_global_ref); + +// The global target destination is copied to the rotation target source +// then written to the rotation output with rotation. +// The pivot for the rotation is the center of the search area +// if we're tracking. +// The pivot is fixed to the user position if we're compensating. + if(!rotate_target_src) + rotate_target_src = new VFrame(w, h, color_model); + if(!rotate_target_dst) + rotate_target_dst = new VFrame(w,h , color_model); + } + } + else +// Rotation only + if(config.rotate) + { +// Rotation reads the previous reference frame and compares it with current +// reference frame. + if(!prev_rotate_ref) + prev_rotate_ref = new VFrame(w, h, color_model); + if(!current_rotate_ref) + current_rotate_ref = new VFrame(w, h, color_model); + +// Rotation loads target frame to temporary, rotates it, and writes it to the +// target frame. The pivot is always fixed. + if(!rotate_target_src) + rotate_target_src = new VFrame(w, h, color_model); + if(!rotate_target_dst) + rotate_target_dst = new VFrame(w,h , color_model); + + +// Load the rotate frames + if(need_reload) + { + read_frame(prev_rotate_ref, + reference_layer, + previous_frame_number, + frame_rate, + 0); + } + read_frame(current_rotate_ref, + reference_layer, + start_position, + frame_rate, + 0); + read_frame(rotate_target_src, + target_layer, + start_position, + frame_rate, + 0); + } + + if(!skip_current) + { + + if( config.mode2 == MotionCVConfig::LOAD ) { + char line[BCTEXTLEN]; + int64_t frame_no, no; int dx, dy; float dt; + if( config.addtrackedframeoffset && config.track_frame != tracking_frame ) { + tracking_frame = frame_no = config.track_frame; + if( !get_line_key(config.tracking_file, frame_no, line, sizeof(line)) && + sscanf(line, "%jd %d %d %f", &no, &dx, &dy, &dt) == 4 ) { + dx_offset = dx; dy_offset = dy; + } + else { +#ifdef DEBUG + printf("MotionCVMain::process_buffer: no offset data frame %jd\n", frame_no); +#endif + } + } +// Load result from disk + frame_no = get_source_position(); + if( !get_line_key(config.tracking_file, frame_no, line, sizeof(line)) && + sscanf(line, "%jd %d %d %f", &frame_no, &dx, &dy, &dt) == 4 ) { + load_dx = dx; load_dy = dy; load_dt = dt; + } + else { +#ifdef DEBUG + printf("MotionCVMain::process_buffer: no tracking data frame %jd\n", frame_no); +#endif + } + } + +// Get position change from previous frame to current frame + if(config.global) process_global(); +// Get rotation change from previous frame to current frame + if(config.rotate) process_rotation(); +//frame[target_layer]->copy_from(prev_rotate_ref); +//frame[target_layer]->copy_from(current_rotate_ref); + +// write results to disk + if( config.mode2 == MotionCVConfig::SAVE ) { + FILE *output = fopen(config.tracking_file, "aw"); + if( output ) { + int64_t frame_no = get_source_position(); + fprintf(output, "%jd %d %d %f\n", frame_no, save_dx, save_dy, save_dt); + fclose(output); + } + else { + perror("MotionCVMain::process buffer save"); + } + } + } + + +// Transfer the relevant target frame to the output + if(!skip_current) + { + if(config.rotate) + { + frame[target_layer]->copy_from(rotate_target_dst); + } + else + { + frame[target_layer]->copy_from(global_target_dst); + } + } + else +// Read the target destination directly + { + read_frame(frame[target_layer], + target_layer, + start_position, + frame_rate, + 0); + } + + if(config.draw_vectors) + { + draw_vectors(frame[target_layer]); + } + +#ifdef DEBUG +printf("MotionCVMain::process_buffer 100\n"); +#endif + return 0; +} + + +void MotionCVMain::clamp_scan(int w, + int h, + int *block_x1, + int *block_y1, + int *block_x2, + int *block_y2, + int *scan_x1, + int *scan_y1, + int *scan_x2, + int *scan_y2, + int use_absolute) +{ +// printf("MotionCVMain::clamp_scan 1 w=%d h=%d block=%d %d %d %d scan=%d %d %d %d absolute=%d\n", +// w, +// h, +// *block_x1, +// *block_y1, +// *block_x2, +// *block_y2, +// *scan_x1, +// *scan_y1, +// *scan_x2, +// *scan_y2, +// use_absolute); + + if(use_absolute) + { +// scan is always out of range before block. + if(*scan_x1 < 0) + { + int difference = -*scan_x1; + *block_x1 += difference; + *scan_x1 = 0; + } + + if(*scan_y1 < 0) + { + int difference = -*scan_y1; + *block_y1 += difference; + *scan_y1 = 0; + } + + if(*scan_x2 > w) + { + int difference = *scan_x2 - w; + *block_x2 -= difference; + *scan_x2 -= difference; + } + + if(*scan_y2 > h) + { + int difference = *scan_y2 - h; + *block_y2 -= difference; + *scan_y2 -= difference; + } + + CLAMP(*scan_x1, 0, w); + CLAMP(*scan_y1, 0, h); + CLAMP(*scan_x2, 0, w); + CLAMP(*scan_y2, 0, h); + } + else + { + if(*scan_x1 < 0) + { + int difference = -*scan_x1; + *block_x1 += difference; + *scan_x2 += difference; + *scan_x1 = 0; + } + + if(*scan_y1 < 0) + { + int difference = -*scan_y1; + *block_y1 += difference; + *scan_y2 += difference; + *scan_y1 = 0; + } + + if(*scan_x2 - *block_x1 + *block_x2 > w) + { + int difference = *scan_x2 - *block_x1 + *block_x2 - w; + *block_x2 -= difference; + } + + if(*scan_y2 - *block_y1 + *block_y2 > h) + { + int difference = *scan_y2 - *block_y1 + *block_y2 - h; + *block_y2 -= difference; + } + +// CLAMP(*scan_x1, 0, w - (*block_x2 - *block_x1)); +// CLAMP(*scan_y1, 0, h - (*block_y2 - *block_y1)); +// CLAMP(*scan_x2, 0, w - (*block_x2 - *block_x1)); +// CLAMP(*scan_y2, 0, h - (*block_y2 - *block_y1)); + } + +// Sanity checks which break the calculation but should never happen if the +// center of the block is inside the frame. + CLAMP(*block_x1, 0, w); + CLAMP(*block_x2, 0, w); + CLAMP(*block_y1, 0, h); + CLAMP(*block_y2, 0, h); + +// printf("MotionCVMain::clamp_scan 2 w=%d h=%d block=%d %d %d %d scan=%d %d %d %d absolute=%d\n", +// w, +// h, +// *block_x1, +// *block_y1, +// *block_x2, +// *block_y2, +// *scan_x1, +// *scan_y1, +// *scan_x2, +// *scan_y2, +// use_absolute); +} + + + +void MotionCVMain::draw_vectors(VFrame *frame) +{ + int w = frame->get_w(); + int h = frame->get_h(); + int global_x1, global_y1; + int global_x2, global_y2; + int block_x, block_y; + int block_w, block_h; + int block_x1, block_y1; + int block_x2, block_y2; + int block_x3, block_y3; + int block_x4, block_y4; + int search_w, search_h; + int search_x1, search_y1; + int search_x2, search_y2; + //int search_x3, search_y3; + //int search_x4, search_y4; + + if(config.global) + { +// Get vector +// Start of vector is center of previous block. +// End of vector is total accumulation. + if(config.mode3 == MotionCVConfig::TRACK_SINGLE) + { + global_x1 = (int64_t)(config.block_x * + w / + 100); + global_y1 = (int64_t)(config.block_y * + h / + 100); + global_x2 = global_x1 + total_dx / OVERSAMPLE; + global_y2 = global_y1 + total_dy / OVERSAMPLE; +//printf("MotionCVMain::draw_vectors %d %d %d %d %d %d\n", total_dx, total_dy, global_x1, global_y1, global_x2, global_y2); + } + else +// Start of vector is center of previous block. +// End of vector is current change. + if(config.mode3 == MotionCVConfig::PREVIOUS_SAME_BLOCK) + { + global_x1 = (int64_t)(config.block_x * + w / + 100); + global_y1 = (int64_t)(config.block_y * + h / + 100); + global_x2 = global_x1 + current_dx / OVERSAMPLE; + global_y2 = global_y1 + current_dy / OVERSAMPLE; + } + else + { + global_x1 = (int64_t)(config.block_x * + w / + 100 + + (total_dx - current_dx) / + OVERSAMPLE); + global_y1 = (int64_t)(config.block_y * + h / + 100 + + (total_dy - current_dy) / + OVERSAMPLE); + global_x2 = (int64_t)(config.block_x * + w / + 100 + + total_dx / + OVERSAMPLE); + global_y2 = (int64_t)(config.block_y * + h / + 100 + + total_dy / + OVERSAMPLE); + } + + block_x = global_x1; + block_y = global_y1; + block_w = config.global_block_w * w / 100; + block_h = config.global_block_h * h / 100; + block_x1 = block_x - block_w / 2; + block_y1 = block_y - block_h / 2; + block_x2 = block_x + block_w / 2; + block_y2 = block_y + block_h / 2; + search_w = config.global_range_w * w / 100; + search_h = config.global_range_h * h / 100; + search_x1 = block_x1 - search_w / 2; + search_y1 = block_y1 - search_h / 2; + search_x2 = block_x2 + search_w / 2; + search_y2 = block_y2 + search_h / 2; + +// printf("MotionCVMain::draw_vectors %d %d %d %d %d %d %d %d %d %d %d %d\n", +// global_x1, +// global_y1, +// block_w, +// block_h, +// block_x1, +// block_y1, +// block_x2, +// block_y2, +// search_x1, +// search_y1, +// search_x2, +// search_y2); + + clamp_scan(w, + h, + &block_x1, + &block_y1, + &block_x2, + &block_y2, + &search_x1, + &search_y1, + &search_x2, + &search_y2, + 1); + +// Vector + draw_arrow(frame, global_x1, global_y1, global_x2, global_y2); + +// Macroblock + draw_line(frame, block_x1, block_y1, block_x2, block_y1); + draw_line(frame, block_x2, block_y1, block_x2, block_y2); + draw_line(frame, block_x2, block_y2, block_x1, block_y2); + draw_line(frame, block_x1, block_y2, block_x1, block_y1); + + +// Search area + draw_line(frame, search_x1, search_y1, search_x2, search_y1); + draw_line(frame, search_x2, search_y1, search_x2, search_y2); + draw_line(frame, search_x2, search_y2, search_x1, search_y2); + draw_line(frame, search_x1, search_y2, search_x1, search_y1); + +// Block should be endpoint of motion + if(config.rotate) + { + block_x = global_x2; + block_y = global_y2; + } + } + else + { + block_x = (int64_t)(config.block_x * w / 100); + block_y = (int64_t)(config.block_y * h / 100); + } + + block_w = config.rotation_block_w * w / 100; + block_h = config.rotation_block_h * h / 100; + if(config.rotate) + { + float angle = total_angle * 2 * M_PI / 360; + double base_angle1 = atan((float)block_h / block_w); + double base_angle2 = atan((float)block_w / block_h); + double target_angle1 = base_angle1 + angle; + double target_angle2 = base_angle2 + angle; + double radius = sqrt(block_w * block_w + block_h * block_h) / 2; + block_x1 = (int)(block_x - cos(target_angle1) * radius); + block_y1 = (int)(block_y - sin(target_angle1) * radius); + block_x2 = (int)(block_x + sin(target_angle2) * radius); + block_y2 = (int)(block_y - cos(target_angle2) * radius); + block_x3 = (int)(block_x - sin(target_angle2) * radius); + block_y3 = (int)(block_y + cos(target_angle2) * radius); + block_x4 = (int)(block_x + cos(target_angle1) * radius); + block_y4 = (int)(block_y + sin(target_angle1) * radius); + + draw_line(frame, block_x1, block_y1, block_x2, block_y2); + draw_line(frame, block_x2, block_y2, block_x4, block_y4); + draw_line(frame, block_x4, block_y4, block_x3, block_y3); + draw_line(frame, block_x3, block_y3, block_x1, block_y1); + + +// Center + if(!config.global) + { + draw_line(frame, block_x, block_y - 5, block_x, block_y + 6); + draw_line(frame, block_x - 5, block_y, block_x + 6, block_y); + } + } +} + + + +void MotionCVMain::draw_pixel(VFrame *frame, int x, int y) +{ + if(!(x >= 0 && y >= 0 && x < frame->get_w() && y < frame->get_h())) return; + +#define DRAW_PIXEL(x, y, components, do_yuv, max, type) \ +{ \ + type **rows = (type**)frame->get_rows(); \ + rows[y][x * components] = max - rows[y][x * components]; \ + if(!do_yuv) \ + { \ + rows[y][x * components + 1] = max - rows[y][x * components + 1]; \ + rows[y][x * components + 2] = max - rows[y][x * components + 2]; \ + } \ + else \ + { \ + rows[y][x * components + 1] = (max / 2 + 1) - rows[y][x * components + 1]; \ + rows[y][x * components + 2] = (max / 2 + 1) - rows[y][x * components + 2]; \ + } \ + if(components == 4) \ + rows[y][x * components + 3] = max; \ +} + + + switch(frame->get_color_model()) + { + case BC_RGB888: + DRAW_PIXEL(x, y, 3, 0, 0xff, unsigned char); + break; + case BC_RGBA8888: + DRAW_PIXEL(x, y, 4, 0, 0xff, unsigned char); + break; + case BC_RGB_FLOAT: + DRAW_PIXEL(x, y, 3, 0, 1.0, float); + break; + case BC_RGBA_FLOAT: + DRAW_PIXEL(x, y, 4, 0, 1.0, float); + break; + case BC_YUV888: + DRAW_PIXEL(x, y, 3, 1, 0xff, unsigned char); + break; + case BC_YUVA8888: + DRAW_PIXEL(x, y, 4, 1, 0xff, unsigned char); + break; + case BC_RGB161616: + DRAW_PIXEL(x, y, 3, 0, 0xffff, uint16_t); + break; + case BC_YUV161616: + DRAW_PIXEL(x, y, 3, 1, 0xffff, uint16_t); + break; + case BC_RGBA16161616: + DRAW_PIXEL(x, y, 4, 0, 0xffff, uint16_t); + break; + case BC_YUVA16161616: + DRAW_PIXEL(x, y, 4, 1, 0xffff, uint16_t); + break; + } +} + + +void MotionCVMain::draw_line(VFrame *frame, int x1, int y1, int x2, int y2) +{ + int w = labs(x2 - x1); + int h = labs(y2 - y1); +//printf("MotionCVMain::draw_line 1 %d %d %d %d\n", x1, y1, x2, y2); + + if(!w && !h) + { + draw_pixel(frame, x1, y1); + } + else + if(w > h) + { +// Flip coordinates so x1 < x2 + if(x2 < x1) + { + y2 ^= y1; + y1 ^= y2; + y2 ^= y1; + x1 ^= x2; + x2 ^= x1; + x1 ^= x2; + } + int numerator = y2 - y1; + int denominator = x2 - x1; + for(int i = x1; i < x2; i++) + { + int y = y1 + (int64_t)(i - x1) * (int64_t)numerator / (int64_t)denominator; + draw_pixel(frame, i, y); + } + } + else + { +// Flip coordinates so y1 < y2 + if(y2 < y1) + { + y2 ^= y1; + y1 ^= y2; + y2 ^= y1; + x1 ^= x2; + x2 ^= x1; + x1 ^= x2; + } + int numerator = x2 - x1; + int denominator = y2 - y1; + for(int i = y1; i < y2; i++) + { + int x = x1 + (int64_t)(i - y1) * (int64_t)numerator / (int64_t)denominator; + draw_pixel(frame, x, i); + } + } +//printf("MotionCVMain::draw_line 2\n"); +} + +#define ARROW_SIZE 10 +void MotionCVMain::draw_arrow(VFrame *frame, int x1, int y1, int x2, int y2) +{ + double angle = atan((float)(y2 - y1) / (float)(x2 - x1)); + double angle1 = angle + (float)145 / 360 * 2 * 3.14159265; + double angle2 = angle - (float)145 / 360 * 2 * 3.14159265; + int x3; + int y3; + int x4; + int y4; + if(x2 < x1) + { + x3 = x2 - (int)(ARROW_SIZE * cos(angle1)); + y3 = y2 - (int)(ARROW_SIZE * sin(angle1)); + x4 = x2 - (int)(ARROW_SIZE * cos(angle2)); + y4 = y2 - (int)(ARROW_SIZE * sin(angle2)); + } + else + { + x3 = x2 + (int)(ARROW_SIZE * cos(angle1)); + y3 = y2 + (int)(ARROW_SIZE * sin(angle1)); + x4 = x2 + (int)(ARROW_SIZE * cos(angle2)); + y4 = y2 + (int)(ARROW_SIZE * sin(angle2)); + } + +// Main vector + draw_line(frame, x1, y1, x2, y2); +// draw_line(frame, x1, y1 + 1, x2, y2 + 1); + +// Arrow line + if(abs(y2 - y1) || abs(x2 - x1)) draw_line(frame, x2, y2, x3, y3); +// draw_line(frame, x2, y2 + 1, x3, y3 + 1); +// Arrow line + if(abs(y2 - y1) || abs(x2 - x1)) draw_line(frame, x2, y2, x4, y4); +// draw_line(frame, x2, y2 + 1, x4, y4 + 1); +} + + + + +#define ABS_DIFF(type, temp_type, multiplier, components) \ +{ \ + temp_type result_temp = 0; \ + for(int i = 0; i < h; i++) \ + { \ + type *prev_row = (type*)prev_ptr; \ + type *current_row = (type*)current_ptr; \ + for(int j = 0; j < w; j++) \ + { \ + for(int k = 0; k < 3; k++) \ + { \ + temp_type difference; \ + difference = *prev_row++ - *current_row++; \ + if(difference < 0) \ + result_temp -= difference; \ + else \ + result_temp += difference; \ + } \ + if(components == 4) \ + { \ + prev_row++; \ + current_row++; \ + } \ + } \ + prev_ptr += row_bytes; \ + current_ptr += row_bytes; \ + } \ + result = (int64_t)(result_temp * multiplier); \ +} + +int64_t MotionCVMain::abs_diff(unsigned char *prev_ptr, + unsigned char *current_ptr, + int row_bytes, + int w, + int h, + int color_model) +{ + int64_t result = 0; + switch(color_model) + { + case BC_RGB888: + ABS_DIFF(unsigned char, int64_t, 1, 3) + break; + case BC_RGBA8888: + ABS_DIFF(unsigned char, int64_t, 1, 4) + break; + case BC_RGB_FLOAT: + ABS_DIFF(float, double, 0x10000, 3) + break; + case BC_RGBA_FLOAT: + ABS_DIFF(float, double, 0x10000, 4) + break; + case BC_YUV888: + ABS_DIFF(unsigned char, int64_t, 1, 3) + break; + case BC_YUVA8888: + ABS_DIFF(unsigned char, int64_t, 1, 4) + break; + case BC_YUV161616: + ABS_DIFF(uint16_t, int64_t, 1, 3) + break; + case BC_YUVA16161616: + ABS_DIFF(uint16_t, int64_t, 1, 4) + break; + } + return result; +} + + + +#define ABS_DIFF_SUB(type, temp_type, multiplier, components) \ +{ \ + temp_type result_temp = 0; \ + temp_type y2_fraction = sub_y * 0x100 / OVERSAMPLE; \ + temp_type y1_fraction = 0x100 - y2_fraction; \ + temp_type x2_fraction = sub_x * 0x100 / OVERSAMPLE; \ + temp_type x1_fraction = 0x100 - x2_fraction; \ + for(int i = 0; i < h_sub; i++) \ + { \ + type *prev_row1 = (type*)prev_ptr; \ + type *prev_row2 = (type*)prev_ptr + components; \ + type *prev_row3 = (type*)(prev_ptr + row_bytes); \ + type *prev_row4 = (type*)(prev_ptr + row_bytes) + components; \ + type *current_row = (type*)current_ptr; \ + for(int j = 0; j < w_sub; j++) \ + { \ + for(int k = 0; k < 3; k++) \ + { \ + temp_type difference; \ + temp_type prev_value = \ + (*prev_row1++ * x1_fraction * y1_fraction + \ + *prev_row2++ * x2_fraction * y1_fraction + \ + *prev_row3++ * x1_fraction * y2_fraction + \ + *prev_row4++ * x2_fraction * y2_fraction) / \ + 0x100 / 0x100; \ + temp_type current_value = *current_row++; \ + difference = prev_value - current_value; \ + if(difference < 0) \ + result_temp -= difference; \ + else \ + result_temp += difference; \ + } \ + \ + if(components == 4) \ + { \ + prev_row1++; \ + prev_row2++; \ + prev_row3++; \ + prev_row4++; \ + current_row++; \ + } \ + } \ + prev_ptr += row_bytes; \ + current_ptr += row_bytes; \ + } \ + result = (int64_t)(result_temp * multiplier); \ +} + + + + +int64_t MotionCVMain::abs_diff_sub(unsigned char *prev_ptr, + unsigned char *current_ptr, + int row_bytes, + int w, + int h, + int color_model, + int sub_x, + int sub_y) +{ + int h_sub = h - 1; + int w_sub = w - 1; + int64_t result = 0; + + switch(color_model) + { + case BC_RGB888: + ABS_DIFF_SUB(unsigned char, int64_t, 1, 3) + break; + case BC_RGBA8888: + ABS_DIFF_SUB(unsigned char, int64_t, 1, 4) + break; + case BC_RGB_FLOAT: + ABS_DIFF_SUB(float, double, 0x10000, 3) + break; + case BC_RGBA_FLOAT: + ABS_DIFF_SUB(float, double, 0x10000, 4) + break; + case BC_YUV888: + ABS_DIFF_SUB(unsigned char, int64_t, 1, 3) + break; + case BC_YUVA8888: + ABS_DIFF_SUB(unsigned char, int64_t, 1, 4) + break; + case BC_YUV161616: + ABS_DIFF_SUB(uint16_t, int64_t, 1, 3) + break; + case BC_YUVA16161616: + ABS_DIFF_SUB(uint16_t, int64_t, 1, 4) + break; + } + return result; +} + +int MotionCVMain::get_line_key(const char *filename, int64_t key, char *line, int len) +{ + if( active_fp && strcmp(active_file, filename) ) { + fclose(active_fp); active_fp = 0; active_file[0] = 0; + } + if( !active_fp ) { + if( !(active_fp = fopen(filename, "r")) ) { + perror("open motion file"); + fprintf(stderr,"err reading key %jd\n", key); + return -1; + } + strcpy(active_file, filename); + tracking_frame = -1; + } + int64_t recd = 0; + if( fgets(line, len, active_fp) && (recd=strtol(line,0,0)) == key ) + return 0; +// binary search file + fseek(active_fp, 0, SEEK_END); + int64_t l = -1, r = ftell(active_fp); + while( (r-l) > 1 ) { + int64_t m = (l+r) / 2; + fseek(active_fp, m, SEEK_SET); + if( m > 0 && !fgets(line, len, active_fp) ) { + fprintf(stderr,"err reading key %jd\n", key); + return -1; + } + if( fgets(line, len, active_fp) ) { + recd = strtol(line,0,0); + if( recd == key ) return 0; + if( recd < key ) { l = m; continue; } + } + r = m; + } + return 1; +} + + + +MotionCVScanPackage::MotionCVScanPackage() + : LoadPackage() +{ + valid = 1; +} + + +MotionCVScanUnit::MotionCVScanUnit(MotionCVScan *server, + MotionCVMain *plugin) + : LoadClient(server) +{ + this->plugin = plugin; + this->server = server; + cache_lock = new Mutex("MotionCVScanUnit::cache_lock"); +} + +MotionCVScanUnit::~MotionCVScanUnit() +{ + delete cache_lock; +} + + + +void MotionCVScanUnit::process_package(LoadPackage *package) +{ + MotionCVScanPackage *pkg = (MotionCVScanPackage*)package; + //int w = server->current_frame->get_w(); + //int h = server->current_frame->get_h(); + int color_model = server->current_frame->get_color_model(); + int pixel_size = BC_CModels::calculate_pixelsize(color_model); + int row_bytes = server->current_frame->get_bytes_per_line(); + +// Single pixel + if(!server->subpixel) + { + int search_x = pkg->scan_x1 + (pkg->pixel % (pkg->scan_x2 - pkg->scan_x1)); + int search_y = pkg->scan_y1 + (pkg->pixel / (pkg->scan_x2 - pkg->scan_x1)); + +// Try cache + pkg->difference1 = server->get_cache(search_x, search_y); + if(pkg->difference1 < 0) + { +//printf("MotionCVScanUnit::process_package 1 %d %d\n", +//search_x, search_y, pkg->block_x2 - pkg->block_x1, pkg->block_y2 - pkg->block_y1); +// Pointers to first pixel in each block + unsigned char *prev_ptr = server->previous_frame->get_rows()[ + search_y] + + search_x * pixel_size; + unsigned char *current_ptr = server->current_frame->get_rows()[ + pkg->block_y1] + + pkg->block_x1 * pixel_size; +// Scan block + pkg->difference1 = plugin->abs_diff(prev_ptr, + current_ptr, + row_bytes, + pkg->block_x2 - pkg->block_x1, + pkg->block_y2 - pkg->block_y1, + color_model); +//printf("MotionCVScanUnit::process_package 2\n"); + server->put_cache(search_x, search_y, pkg->difference1); + } + } + else +// Sub pixel + { + int sub_x = pkg->pixel % (OVERSAMPLE * 2 - 1) + 1; + int sub_y = pkg->pixel / (OVERSAMPLE * 2 - 1) + 1; + + if(plugin->config.horizontal_only) + { + sub_y = 0; + } + + if(plugin->config.vertical_only) + { + sub_x = 0; + } + + int search_x = pkg->scan_x1 + sub_x / OVERSAMPLE; + int search_y = pkg->scan_y1 + sub_y / OVERSAMPLE; + sub_x %= OVERSAMPLE; + sub_y %= OVERSAMPLE; + + + unsigned char *prev_ptr = server->previous_frame->get_rows()[ + search_y] + + search_x * pixel_size; + unsigned char *current_ptr = server->current_frame->get_rows()[ + pkg->block_y1] + + pkg->block_x1 * pixel_size; + +// With subpixel, there are two ways to compare each position, one by shifting +// the previous frame and two by shifting the current frame. + pkg->difference1 = plugin->abs_diff_sub(prev_ptr, + current_ptr, + row_bytes, + pkg->block_x2 - pkg->block_x1, + pkg->block_y2 - pkg->block_y1, + color_model, + sub_x, + sub_y); + pkg->difference2 = plugin->abs_diff_sub(current_ptr, + prev_ptr, + row_bytes, + pkg->block_x2 - pkg->block_x1, + pkg->block_y2 - pkg->block_y1, + color_model, + sub_x, + sub_y); +// printf("MotionCVScanUnit::process_package sub_x=%d sub_y=%d search_x=%d search_y=%d diff1=%lld diff2=%lld\n", +// sub_x, +// sub_y, +// search_x, +// search_y, +// pkg->difference1, +// pkg->difference2); + } + + + + +} + + + + + + + + + + +int64_t MotionCVScanUnit::get_cache(int x, int y) +{ + int64_t result = -1; + cache_lock->lock("MotionCVScanUnit::get_cache"); + for(int i = 0; i < cache.total; i++) + { + MotionCVScanCache *ptr = cache.values[i]; + if(ptr->x == x && ptr->y == y) + { + result = ptr->difference; + break; + } + } + cache_lock->unlock(); + return result; +} + +void MotionCVScanUnit::put_cache(int x, int y, int64_t difference) +{ + MotionCVScanCache *ptr = new MotionCVScanCache(x, y, difference); + cache_lock->lock("MotionCVScanUnit::put_cache"); + cache.append(ptr); + cache_lock->unlock(); +} + + + + + + + + + + + +MotionCVScan::MotionCVScan(MotionCVMain *plugin, + int total_clients, + int total_packages) + : LoadServer( +//1, 1 +total_clients, total_packages +) +{ + this->plugin = plugin; + cache_lock = new Mutex("MotionCVScan::cache_lock"); +} + +MotionCVScan::~MotionCVScan() +{ + delete cache_lock; +} + + +void MotionCVScan::init_packages() +{ +// Set package coords + for(int i = 0; i < get_total_packages(); i++) + { + MotionCVScanPackage *pkg = (MotionCVScanPackage*)get_package(i); + + pkg->block_x1 = block_x1; + pkg->block_x2 = block_x2; + pkg->block_y1 = block_y1; + pkg->block_y2 = block_y2; + pkg->scan_x1 = scan_x1; + pkg->scan_x2 = scan_x2; + pkg->scan_y1 = scan_y1; + pkg->scan_y2 = scan_y2; + pkg->pixel = (int64_t)i * (int64_t)total_pixels / (int64_t)total_steps; + pkg->difference1 = 0; + pkg->difference2 = 0; + pkg->dx = 0; + pkg->dy = 0; + pkg->valid = 1; + } +} + +LoadClient* MotionCVScan::new_client() +{ + return new MotionCVScanUnit(this, plugin); +} + +LoadPackage* MotionCVScan::new_package() +{ + return new MotionCVScanPackage; +} + + +void MotionCVScan::scan_frame(VFrame *previous_frame, + VFrame *current_frame) +{ + this->previous_frame = previous_frame; + this->current_frame = current_frame; + subpixel = 0; + + cache.remove_all_objects(); + + +// Single macroblock + int w = current_frame->get_w(); + int h = current_frame->get_h(); + +// Initial search parameters + int scan_w = w * plugin->config.global_range_w / 100; + int scan_h = h * plugin->config.global_range_h / 100; + int block_w = w * plugin->config.global_block_w / 100; + int block_h = h * plugin->config.global_block_h / 100; + +// Location of block in previous frame + block_x1 = (int)(w * plugin->config.block_x / 100 - block_w / 2); + block_y1 = (int)(h * plugin->config.block_y / 100 - block_h / 2); + block_x2 = (int)(w * plugin->config.block_x / 100 + block_w / 2); + block_y2 = (int)(h * plugin->config.block_y / 100 + block_h / 2); + +// Offset to location of previous block. This offset needn't be very accurate +// since it's the offset of the previous image and current image we want. + if(plugin->config.mode3 == MotionCVConfig::TRACK_PREVIOUS) + { + block_x1 += plugin->total_dx / OVERSAMPLE; + block_y1 += plugin->total_dy / OVERSAMPLE; + block_x2 += plugin->total_dx / OVERSAMPLE; + block_y2 += plugin->total_dy / OVERSAMPLE; + } + + skip = 0; + + switch(plugin->config.mode2) + { +// Don't calculate + case MotionCVConfig::NO_CALCULATE: + dx_result = 0; + dy_result = 0; + skip = 1; + break; + + case MotionCVConfig::LOAD: + { + dx_result = plugin->load_dx; + dy_result = plugin->load_dy; + skip = 1; + break; + } + +// Scan from scratch + default: + skip = 0; + break; + } + +// Perform scan + if(!skip) + { +// Location of block in current frame + int x_result = block_x1; + int y_result = block_y1; + +// printf("MotionCVScan::scan_frame 1 %d %d %d %d %d %d %d %d\n", +// block_x1 + block_w / 2, +// block_y1 + block_h / 2, +// block_w, +// block_h, +// block_x1, +// block_y1, +// block_x2, +// block_y2); + + while(1) + { + scan_x1 = x_result - scan_w / 2; + scan_y1 = y_result - scan_h / 2; + scan_x2 = x_result + scan_w / 2; + scan_y2 = y_result + scan_h / 2; + + + +// Zero out requested values + if(plugin->config.horizontal_only) + { + scan_y1 = block_y1; + scan_y2 = block_y1 + 1; + } + if(plugin->config.vertical_only) + { + scan_x1 = block_x1; + scan_x2 = block_x1 + 1; + } + +// printf("MotionCVScan::scan_frame 1 %d %d %d %d %d %d %d %d\n", +// block_x1, +// block_y1, +// block_x2, +// block_y2, +// scan_x1, +// scan_y1, +// scan_x2, +// scan_y2); +// Clamp the block coords before the scan so we get useful scan coords. + MotionCVMain::clamp_scan(w, + h, + &block_x1, + &block_y1, + &block_x2, + &block_y2, + &scan_x1, + &scan_y1, + &scan_x2, + &scan_y2, + 0); +// printf("MotionCVScan::scan_frame 1\n block_x1=%d block_y1=%d block_x2=%d block_y2=%d\n scan_x1=%d scan_y1=%d scan_x2=%d scan_y2=%d\n x_result=%d y_result=%d\n", +// block_x1, +// block_y1, +// block_x2, +// block_y2, +// scan_x1, +// scan_y1, +// scan_x2, +// scan_y2, +// x_result, +// y_result); + + +// Give up if invalid coords. + if(scan_y2 <= scan_y1 || + scan_x2 <= scan_x1 || + block_x2 <= block_x1 || + block_y2 <= block_y1) + break; + +// For subpixel, the top row and left column are skipped + if(subpixel) + { + if(plugin->config.horizontal_only || + plugin->config.vertical_only) + { + total_pixels = 4 * OVERSAMPLE * OVERSAMPLE - 4 * OVERSAMPLE; + } + else + { + total_pixels = 4 * OVERSAMPLE; + } + + total_steps = total_pixels; + + set_package_count(total_steps); + process_packages(); + +// Get least difference + int64_t min_difference = -1; + for(int i = 0; i < get_total_packages(); i++) + { + MotionCVScanPackage *pkg = (MotionCVScanPackage*)get_package(i); + if(pkg->difference1 < min_difference || min_difference == -1) + { + min_difference = pkg->difference1; + + if(plugin->config.vertical_only) + x_result = scan_x1 * OVERSAMPLE; + else + x_result = scan_x1 * OVERSAMPLE + + (pkg->pixel % (OVERSAMPLE * 2 - 1)) + 1; + + if(plugin->config.horizontal_only) + y_result = scan_y1 * OVERSAMPLE; + else + y_result = scan_y1 * OVERSAMPLE + + (pkg->pixel / (OVERSAMPLE * 2 - 1)) + 1; + + +// Fill in results + dx_result = block_x1 * OVERSAMPLE - x_result; + dy_result = block_y1 * OVERSAMPLE - y_result; + } + + if(pkg->difference2 < min_difference) + { + min_difference = pkg->difference2; + + if(plugin->config.vertical_only) + x_result = scan_x1 * OVERSAMPLE; + else + x_result = scan_x2 * OVERSAMPLE - + ((pkg->pixel % (OVERSAMPLE * 2 - 1)) + 1); + + if(plugin->config.horizontal_only) + y_result = scan_y1 * OVERSAMPLE; + else + y_result = scan_y2 * OVERSAMPLE - + ((pkg->pixel / (OVERSAMPLE * 2 - 1)) + 1); + + dx_result = block_x1 * OVERSAMPLE - x_result; + dy_result = block_y1 * OVERSAMPLE - y_result; + } + } + +//printf("MotionCVScan::scan_frame 1 %d %d %d %d\n", block_x1, block_y1, x_result, y_result); + break; + } + else + { + total_pixels = (scan_x2 - scan_x1) * (scan_y2 - scan_y1); + total_steps = MIN(plugin->config.global_positions, total_pixels); + + set_package_count(total_steps); + process_packages(); + +// Get least difference + int64_t min_difference = -1; + for(int i = 0; i < get_total_packages(); i++) + { + MotionCVScanPackage *pkg = (MotionCVScanPackage*)get_package(i); + if(pkg->difference1 < min_difference || min_difference == -1) + { + min_difference = pkg->difference1; + x_result = scan_x1 + (pkg->pixel % (scan_x2 - scan_x1)); + y_result = scan_y1 + (pkg->pixel / (scan_x2 - scan_x1)); + x_result *= OVERSAMPLE; + y_result *= OVERSAMPLE; + } + } + +// printf("MotionCVScan::scan_frame 10 total_steps=%d total_pixels=%d subpixel=%d\n", +// total_steps, +// total_pixels, +// subpixel); +// +// printf(" scan w=%d h=%d scan x1=%d y1=%d x2=%d y2=%d\n", +// scan_w, +// scan_h, +// scan_x1, +// scan_y1, +// scan_x2, +// scan_y2); +// +// printf("MotionCVScan::scan_frame 2 block x1=%d y1=%d x2=%d y2=%d result x=%.2f y=%.2f\n", +// block_x1, +// block_y1, +// block_x2, +// block_y2, +// (float)x_result / 4, +// (float)y_result / 4); + + +// If a new search is required, rescale results back to pixels. + if(total_steps >= total_pixels) + { +// Single pixel accuracy reached. Now do exhaustive subpixel search. + if(plugin->config.mode1 == MotionCVConfig::STABILIZE || + plugin->config.mode1 == MotionCVConfig::TRACK || + plugin->config.mode1 == MotionCVConfig::NOTHING) + { + x_result /= OVERSAMPLE; + y_result /= OVERSAMPLE; + scan_w = 2; + scan_h = 2; + subpixel = 1; + } + else + { +// Fill in results and quit + dx_result = block_x1 * OVERSAMPLE - x_result; + dy_result = block_y1 * OVERSAMPLE - y_result; + break; + } + } + else +// Reduce scan area and try again + { + scan_w = (scan_x2 - scan_x1) / 2; + scan_h = (scan_y2 - scan_y1) / 2; + x_result /= OVERSAMPLE; + y_result /= OVERSAMPLE; + } + } + } + + // Add offsets from the "tracked single frame" + dx_result = plugin->dx_offset - dx_result; + dy_result = plugin->dy_offset - dy_result; + + if(plugin->config.mode2 == MotionCVConfig::SAVE) + { + plugin->save_dx = dx_result; + plugin->save_dy = dy_result; + } + } + +#ifdef DEBUG +printf("MotionCVScan::scan_frame 10 dx=%.2f dy=%.2f\n", +(float)this->dx_result / OVERSAMPLE, +(float)this->dy_result / OVERSAMPLE); +#endif +} + + + + + + + + + + + + + + + + + +int64_t MotionCVScan::get_cache(int x, int y) +{ + int64_t result = -1; + cache_lock->lock("MotionCVScan::get_cache"); + for(int i = 0; i < cache.total; i++) + { + MotionCVScanCache *ptr = cache.values[i]; + if(ptr->x == x && ptr->y == y) + { + result = ptr->difference; + break; + } + } + cache_lock->unlock(); + return result; +} + +void MotionCVScan::put_cache(int x, int y, int64_t difference) +{ + MotionCVScanCache *ptr = new MotionCVScanCache(x, y, difference); + cache_lock->lock("MotionCVScan::put_cache"); + cache.append(ptr); + cache_lock->unlock(); +} + + + + + +MotionCVScanCache::MotionCVScanCache(int x, int y, int64_t difference) +{ + this->x = x; + this->y = y; + this->difference = difference; +} + + + + + + + + + + + + + + +RotateCVScanPackage::RotateCVScanPackage() +{ +} + + +RotateCVScanUnit::RotateCVScanUnit(RotateCVScan *server, MotionCVMain *plugin) + : LoadClient(server) +{ + this->server = server; + this->plugin = plugin; + rotater = 0; + temp = 0; +} + +RotateCVScanUnit::~RotateCVScanUnit() +{ + delete rotater; + delete temp; +} + +void RotateCVScanUnit::process_package(LoadPackage *package) +{ + if(server->skip) return; + RotateCVScanPackage *pkg = (RotateCVScanPackage*)package; + + if((pkg->difference = server->get_cache(pkg->angle)) < 0) + { +//printf("RotateCVScanUnit::process_package 1\n"); + int color_model = server->previous_frame->get_color_model(); + int pixel_size = BC_CModels::calculate_pixelsize(color_model); + int row_bytes = server->previous_frame->get_bytes_per_line(); + + if(!rotater) + rotater = new AffineEngine(1, 1); + if(!temp) temp = new VFrame( + server->previous_frame->get_w(), + server->previous_frame->get_h(), + color_model); + + +// RotateCV original block size + rotater->set_viewport(server->block_x1, + server->block_y1, + server->block_x2 - server->block_x1, + server->block_y2 - server->block_y1); + rotater->set_pivot(server->block_x, server->block_y); +//pkg->angle = 2; + rotater->rotate(temp, + server->previous_frame, + pkg->angle); +// Clamp coordinates + int x1 = server->scan_x; + int y1 = server->scan_y; + int x2 = x1 + server->scan_w; + int y2 = y1 + server->scan_h; + x2 = MIN(temp->get_w(), x2); + y2 = MIN(temp->get_h(), y2); + x2 = MIN(server->current_frame->get_w(), x2); + y2 = MIN(server->current_frame->get_h(), y2); + x1 = MAX(0, x1); + y1 = MAX(0, y1); + + if(x2 > x1 && y2 > y1) + { + pkg->difference = plugin->abs_diff( + temp->get_rows()[y1] + x1 * pixel_size, + server->current_frame->get_rows()[y1] + x1 * pixel_size, + row_bytes, + x2 - x1, + y2 - y1, + color_model); +//printf("RotateCVScanUnit::process_package %d\n", __LINE__); + server->put_cache(pkg->angle, pkg->difference); + } + +// printf("RotateCVScanUnit::process_package 10 x=%d y=%d w=%d h=%d block_x=%d block_y=%d angle=%f scan_w=%d scan_h=%d diff=%lld\n", +// server->block_x1, +// server->block_y1, +// server->block_x2 - server->block_x1, +// server->block_y2 - server->block_y1, +// server->block_x, +// server->block_y, +// pkg->angle, +// server->scan_w, +// server->scan_h, +// pkg->difference); + } +} + + + + + + + + + + + + + + + + + + + + + + +RotateCVScan::RotateCVScan(MotionCVMain *plugin, + int total_clients, + int total_packages) + : LoadServer( +//1, 1 +total_clients, total_packages +) +{ + this->plugin = plugin; + cache_lock = new Mutex("RotateCVScan::cache_lock"); +} + + +RotateCVScan::~RotateCVScan() +{ + delete cache_lock; +} + +void RotateCVScan::init_packages() +{ + for(int i = 0; i < get_total_packages(); i++) + { + RotateCVScanPackage *pkg = (RotateCVScanPackage*)get_package(i); + pkg->angle = i * + (scan_angle2 - scan_angle1) / + (total_steps - 1) + + scan_angle1; + } +} + +LoadClient* RotateCVScan::new_client() +{ + return new RotateCVScanUnit(this, plugin); +} + +LoadPackage* RotateCVScan::new_package() +{ + return new RotateCVScanPackage; +} + + +float RotateCVScan::scan_frame(VFrame *previous_frame, + VFrame *current_frame, + int block_x, + int block_y) +{ + skip = 0; + this->block_x = block_x; + this->block_y = block_y; + + switch(plugin->config.mode2) + { + case MotionCVConfig::NO_CALCULATE: + result = 0; + skip = 1; + break; + + case MotionCVConfig::LOAD: + { + result = plugin->load_dt; + skip = 1; + break; + } + } + + + this->previous_frame = previous_frame; + this->current_frame = current_frame; + int w = current_frame->get_w(); + int h = current_frame->get_h(); + int block_w = w * plugin->config.rotation_block_w / 100; + int block_h = h * plugin->config.rotation_block_h / 100; + + if(this->block_x - block_w / 2 < 0) block_w = this->block_x * 2; + if(this->block_y - block_h / 2 < 0) block_h = this->block_y * 2; + if(this->block_x + block_w / 2 > w) block_w = (w - this->block_x) * 2; + if(this->block_y + block_h / 2 > h) block_h = (h - this->block_y) * 2; + + block_x1 = this->block_x - block_w / 2; + block_x2 = this->block_x + block_w / 2; + block_y1 = this->block_y - block_h / 2; + block_y2 = this->block_y + block_h / 2; + + +// Calculate the maximum area available to scan after rotation. +// Must be calculated from the starting range because of cache. +// Get coords of rectangle after rotation. + double center_x = this->block_x; + double center_y = this->block_y; + double max_angle = plugin->config.rotation_range; + double base_angle1 = atan((float)block_h / block_w); + double base_angle2 = atan((float)block_w / block_h); + double target_angle1 = base_angle1 + max_angle * 2 * M_PI / 360; + double target_angle2 = base_angle2 + max_angle * 2 * M_PI / 360; + double radius = sqrt(block_w * block_w + block_h * block_h) / 2; + double x1 = center_x - cos(target_angle1) * radius; + double y1 = center_y - sin(target_angle1) * radius; + double x2 = center_x + sin(target_angle2) * radius; + double y2 = center_y - cos(target_angle2) * radius; + double x3 = center_x - sin(target_angle2) * radius; + double y3 = center_y + cos(target_angle2) * radius; + +// Track top edge to find greatest area. + double max_area1 = 0; + //double max_x1 = 0; + double max_y1 = 0; + for(double x = x1; x < x2; x++) + { + double y = y1 + (y2 - y1) * (x - x1) / (x2 - x1); + if(x >= center_x && x < block_x2 && y >= block_y1 && y < center_y) + { + double area = fabs(x - center_x) * fabs(y - center_y); + if(area > max_area1) + { + max_area1 = area; + //max_x1 = x; + max_y1 = y; + } + } + } + +// Track left edge to find greatest area. + double max_area2 = 0; + double max_x2 = 0; + //double max_y2 = 0; + for(double y = y1; y < y3; y++) + { + double x = x1 + (x3 - x1) * (y - y1) / (y3 - y1); + if(x >= block_x1 && x < center_x && y >= block_y1 && y < center_y) + { + double area = fabs(x - center_x) * fabs(y - center_y); + if(area > max_area2) + { + max_area2 = area; + max_x2 = x; + //max_y2 = y; + } + } + } + + double max_x, max_y; + max_x = max_x2; + max_y = max_y1; + +// Get reduced scan coords + scan_w = (int)(fabs(max_x - center_x) * 2); + scan_h = (int)(fabs(max_y - center_y) * 2); + scan_x = (int)(center_x - scan_w / 2); + scan_y = (int)(center_y - scan_h / 2); +// printf("RotateCVScan::scan_frame center=%d,%d scan=%d,%d %dx%d\n", +// this->block_x, this->block_y, scan_x, scan_y, scan_w, scan_h); +// printf(" angle_range=%f block= %d,%d,%d,%d\n", max_angle, block_x1, block_y1, block_x2, block_y2); + +// Determine min angle from size of block + double angle1 = atan((double)block_h / block_w); + double angle2 = atan((double)(block_h - 1) / (block_w + 1)); + double min_angle = fabs(angle2 - angle1) / OVERSAMPLE; + min_angle = MAX(min_angle, MIN_ANGLE); + +#ifdef DEBUG +printf("RotateCVScan::scan_frame min_angle=%f\n", min_angle * 360 / 2 / M_PI); +#endif + + cache.remove_all_objects(); + if(!skip) + { +// Initial search range + float angle_range = (float)plugin->config.rotation_range; + result = 0; + total_steps = plugin->config.rotate_positions; + + + while(angle_range >= min_angle * total_steps) + { + scan_angle1 = result - angle_range; + scan_angle2 = result + angle_range; + + + set_package_count(total_steps); +//set_package_count(1); + process_packages(); + + int64_t min_difference = -1; + for(int i = 0; i < get_total_packages(); i++) + { + RotateCVScanPackage *pkg = (RotateCVScanPackage*)get_package(i); + if(pkg->difference < min_difference || min_difference == -1) + { + min_difference = pkg->difference; + result = pkg->angle; + } +//break; + } + + angle_range /= 2; + +//break; + } + + if(plugin->config.mode2 == MotionCVConfig::SAVE) { + plugin->save_dt = result; + } + } + +#ifdef DEBUG +printf("RotateCVScan::scan_frame 10 angle=%f\n", result); +#endif + + return result; +} + +int64_t RotateCVScan::get_cache(float angle) +{ + int64_t result = -1; + cache_lock->lock("RotateCVScan::get_cache"); + for(int i = 0; i < cache.total; i++) + { + RotateCVScanCache *ptr = cache.values[i]; + if(fabs(ptr->angle - angle) <= MIN_ANGLE) + { + result = ptr->difference; + break; + } + } + cache_lock->unlock(); + return result; +} + +void RotateCVScan::put_cache(float angle, int64_t difference) +{ + RotateCVScanCache *ptr = new RotateCVScanCache(angle, difference); + cache_lock->lock("RotateCVScan::put_cache"); + cache.append(ptr); + cache_lock->unlock(); +} + + + + + + + + + +RotateCVScanCache::RotateCVScanCache(float angle, int64_t difference) +{ + this->angle = angle; + this->difference = difference; +} + + + diff --git a/cinelerra-5.1/plugins/motion-cv/motion-cv.C.orig b/cinelerra-5.1/plugins/motion-cv/motion-cv.C.orig new file mode 100644 index 00000000..a5eb1912 --- /dev/null +++ b/cinelerra-5.1/plugins/motion-cv/motion-cv.C.orig @@ -0,0 +1,2731 @@ + +/* + * CINELERRA + * Copyright (C) 2008 Adam Williams + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "affine.h" +#include "bcdisplayinfo.h" +#include "clip.h" +#include "bchash.h" +#include "bcsignals.h" +#include "filexml.h" +#include "keyframe.h" +#include "language.h" +#include "motion-cv.h" +#include "motionwindow-cv.h" +#include "mutex.h" +#include "overlayframe.h" +#include "rotateframe.h" +#include "transportque.h" + + +#include +#include + +REGISTER_PLUGIN(MotionCVMain) + +//#undef DEBUG + +#ifndef DEBUG +#define DEBUG +#endif + + +MotionCVConfig::MotionCVConfig() +{ + global_range_w = 5; + global_range_h = 5; + rotation_range = 5; + block_count = 1; + global_block_w = MIN_BLOCK; + global_block_h = MIN_BLOCK; + rotation_block_w = MIN_BLOCK; + rotation_block_h = MIN_BLOCK; + block_x = 50; + block_y = 50; + global_positions = 256; + rotate_positions = 4; + magnitude = 100; + return_speed = 0; + mode1 = STABILIZE; + global = 1; + rotate = 1; + addtrackedframeoffset = 0; + mode2 = RECALCULATE; + draw_vectors = 1; + mode3 = MotionCVConfig::TRACK_SINGLE; + track_frame = 0; + bottom_is_master = 1; + horizontal_only = 0; + vertical_only = 0; +} + +void MotionCVConfig::boundaries() +{ + CLAMP(global_range_w, MIN_RADIUS, MAX_RADIUS); + CLAMP(global_range_h, MIN_RADIUS, MAX_RADIUS); + CLAMP(rotation_range, MIN_ROTATION, MAX_ROTATION); + CLAMP(block_count, MIN_BLOCKS, MAX_BLOCKS); + CLAMP(global_block_w, MIN_BLOCK, MAX_BLOCK); + CLAMP(global_block_h, MIN_BLOCK, MAX_BLOCK); + CLAMP(rotation_block_w, MIN_BLOCK, MAX_BLOCK); + CLAMP(rotation_block_h, MIN_BLOCK, MAX_BLOCK); +} + +int MotionCVConfig::equivalent(MotionCVConfig &that) +{ + return global_range_w == that.global_range_w && + global_range_h == that.global_range_h && + rotation_range == that.rotation_range && + mode1 == that.mode1 && + global == that.global && + rotate == that.rotate && + addtrackedframeoffset == that.addtrackedframeoffset && + draw_vectors == that.draw_vectors && + block_count == that.block_count && + global_block_w == that.global_block_w && + global_block_h == that.global_block_h && + rotation_block_w == that.rotation_block_w && + rotation_block_h == that.rotation_block_h && + EQUIV(block_x, that.block_x) && + EQUIV(block_y, that.block_y) && + global_positions == that.global_positions && + rotate_positions == that.rotate_positions && + magnitude == that.magnitude && + return_speed == that.return_speed && + mode3 == that.mode3 && + track_frame == that.track_frame && + bottom_is_master == that.bottom_is_master && + horizontal_only == that.horizontal_only && + vertical_only == that.vertical_only; +} + +void MotionCVConfig::copy_from(MotionCVConfig &that) +{ + global_range_w = that.global_range_w; + global_range_h = that.global_range_h; + rotation_range = that.rotation_range; + mode1 = that.mode1; + global = that.global; + rotate = that.rotate; + addtrackedframeoffset = that.addtrackedframeoffset; + mode2 = that.mode2; + draw_vectors = that.draw_vectors; + block_count = that.block_count; + block_x = that.block_x; + block_y = that.block_y; + global_positions = that.global_positions; + rotate_positions = that.rotate_positions; + global_block_w = that.global_block_w; + global_block_h = that.global_block_h; + rotation_block_w = that.rotation_block_w; + rotation_block_h = that.rotation_block_h; + magnitude = that.magnitude; + return_speed = that.return_speed; + mode3 = that.mode3; + track_frame = that.track_frame; + bottom_is_master = that.bottom_is_master; + horizontal_only = that.horizontal_only; + vertical_only = that.vertical_only; +} + +void MotionCVConfig::interpolate(MotionCVConfig &prev, + MotionCVConfig &next, + int64_t prev_frame, + int64_t next_frame, + int64_t current_frame) +{ + //double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame); + //double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame); + this->block_x = prev.block_x; + this->block_y = prev.block_y; + global_range_w = prev.global_range_w; + global_range_h = prev.global_range_h; + rotation_range = prev.rotation_range; + mode1 = prev.mode1; + global = prev.global; + rotate = prev.rotate; + addtrackedframeoffset = prev.addtrackedframeoffset; + mode2 = prev.mode2; + draw_vectors = prev.draw_vectors; + block_count = prev.block_count; + global_positions = prev.global_positions; + rotate_positions = prev.rotate_positions; + global_block_w = prev.global_block_w; + global_block_h = prev.global_block_h; + rotation_block_w = prev.rotation_block_w; + rotation_block_h = prev.rotation_block_h; + magnitude = prev.magnitude; + return_speed = prev.return_speed; + mode3 = prev.mode3; + track_frame = prev.track_frame; + bottom_is_master = prev.bottom_is_master; + horizontal_only = prev.horizontal_only; + vertical_only = prev.vertical_only; +} + + + + + + + + + + + + + + + + + + + +MotionCVMain::MotionCVMain(PluginServer *server) + : PluginVClient(server) +{ + engine = 0; + rotate_engine = 0; + motion_rotate = 0; + total_dx = 0; + total_dy = 0; + total_angle = 0; + overlayer = 0; + search_area = 0; + search_size = 0; + temp_frame = 0; + previous_frame_number = -1; + + prev_global_ref = 0; + current_global_ref = 0; + global_target_src = 0; + global_target_dst = 0; + + prev_rotate_ref = 0; + current_rotate_ref = 0; + rotate_target_src = 0; + rotate_target_dst = 0; +} + +MotionCVMain::~MotionCVMain() +{ + + delete engine; + delete overlayer; + delete [] search_area; + delete temp_frame; + delete rotate_engine; + delete motion_rotate; + + + delete prev_global_ref; + delete current_global_ref; + delete global_target_src; + delete global_target_dst; + + delete prev_rotate_ref; + delete current_rotate_ref; + delete rotate_target_src; + delete rotate_target_dst; +} + +const char* MotionCVMain::plugin_title() { return _("MotionCV"); } +int MotionCVMain::is_realtime() { return 1; } +int MotionCVMain::is_multichannel() { return 1; } + + +NEW_WINDOW_MACRO(MotionCVMain, MotionCVWindow) + +LOAD_CONFIGURATION_MACRO(MotionCVMain, MotionCVConfig) + + + +void MotionCVMain::update_gui() +{ + if(thread) + { + if(load_configuration()) + { + thread->window->lock_window("MotionCVMain::update_gui"); + MotionCVWindow *window = (MotionCVWindow *)thread->window; + + char string[BCTEXTLEN]; + sprintf(string, "%d", config.global_positions); + window->global_search_positions->set_text(string); + sprintf(string, "%d", config.rotate_positions); + window->rotation_search_positions->set_text(string); + + window->global_block_w->update(config.global_block_w); + window->global_block_h->update(config.global_block_h); + window->rotation_block_w->update(config.rotation_block_w); + window->rotation_block_h->update(config.rotation_block_h); + window->block_x->update(config.block_x); + window->block_y->update(config.block_y); + window->block_x_text->update((float)config.block_x); + window->block_y_text->update((float)config.block_y); + window->magnitude->update(config.magnitude); + window->return_speed->update(config.return_speed); + + + window->track_single->update(config.mode3 == MotionCVConfig::TRACK_SINGLE); + window->track_frame_number->update(config.track_frame); + window->track_previous->update(config.mode3 == MotionCVConfig::TRACK_PREVIOUS); + window->previous_same->update(config.mode3 == MotionCVConfig::PREVIOUS_SAME_BLOCK); + if(config.mode3 != MotionCVConfig::TRACK_SINGLE) + window->track_frame_number->disable(); + else + window->track_frame_number->enable(); + + window->mode1->set_text( + Mode1::to_text(config.mode1)); + window->mode2->set_text( + Mode2::to_text(config.mode2)); + window->mode3->set_text( + Mode3::to_text(config.horizontal_only, config.vertical_only)); + window->master_layer->set_text( + MasterLayer::to_text(config.bottom_is_master)); + + + window->update_mode(); + window->unlock_window(); + } + } +} + + + + +void MotionCVMain::save_data(KeyFrame *keyframe) +{ + FileXML output; + +// cause data to be stored directly in text + output.set_shared_output(keyframe->get_data(), MESSAGESIZE); + output.tag.set_title("MOTION"); + + output.tag.set_property("BLOCK_COUNT", config.block_count); + output.tag.set_property("GLOBAL_POSITIONS", config.global_positions); + output.tag.set_property("ROTATE_POSITIONS", config.rotate_positions); + output.tag.set_property("GLOBAL_BLOCK_W", config.global_block_w); + output.tag.set_property("GLOBAL_BLOCK_H", config.global_block_h); + output.tag.set_property("ROTATION_BLOCK_W", config.rotation_block_w); + output.tag.set_property("ROTATION_BLOCK_H", config.rotation_block_h); + output.tag.set_property("BLOCK_X", config.block_x); + output.tag.set_property("BLOCK_Y", config.block_y); + output.tag.set_property("GLOBAL_RANGE_W", config.global_range_w); + output.tag.set_property("GLOBAL_RANGE_H", config.global_range_h); + output.tag.set_property("ROTATION_RANGE", config.rotation_range); + output.tag.set_property("MAGNITUDE", config.magnitude); + output.tag.set_property("RETURN_SPEED", config.return_speed); + output.tag.set_property("MODE1", config.mode1); + output.tag.set_property("GLOBAL", config.global); + output.tag.set_property("ROTATE", config.rotate); + output.tag.set_property("ADDTRACKEDFRAMEOFFSET", config.addtrackedframeoffset); + output.tag.set_property("MODE2", config.mode2); + output.tag.set_property("DRAW_VECTORS", config.draw_vectors); + output.tag.set_property("MODE3", config.mode3); + output.tag.set_property("TRACK_FRAME", config.track_frame); + output.tag.set_property("BOTTOM_IS_MASTER", config.bottom_is_master); + output.tag.set_property("HORIZONTAL_ONLY", config.horizontal_only); + output.tag.set_property("VERTICAL_ONLY", config.vertical_only); + output.append_tag(); + output.tag.set_title("/MOTION"); + output.append_tag(); + output.terminate_string(); +} + +void MotionCVMain::read_data(KeyFrame *keyframe) +{ + FileXML input; + + input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data())); + + int result = 0; + + while(!result) + { + result = input.read_tag(); + + if(!result) + { + if(input.tag.title_is("MOTION")) + { + config.block_count = input.tag.get_property("BLOCK_COUNT", config.block_count); + config.global_positions = input.tag.get_property("GLOBAL_POSITIONS", config.global_positions); + config.rotate_positions = input.tag.get_property("ROTATE_POSITIONS", config.rotate_positions); + config.global_block_w = input.tag.get_property("GLOBAL_BLOCK_W", config.global_block_w); + config.global_block_h = input.tag.get_property("GLOBAL_BLOCK_H", config.global_block_h); + config.rotation_block_w = input.tag.get_property("ROTATION_BLOCK_W", config.rotation_block_w); + config.rotation_block_h = input.tag.get_property("ROTATION_BLOCK_H", config.rotation_block_h); + config.block_x = input.tag.get_property("BLOCK_X", config.block_x); + config.block_y = input.tag.get_property("BLOCK_Y", config.block_y); + config.global_range_w = input.tag.get_property("GLOBAL_RANGE_W", config.global_range_w); + config.global_range_h = input.tag.get_property("GLOBAL_RANGE_H", config.global_range_h); + config.rotation_range = input.tag.get_property("ROTATION_RANGE", config.rotation_range); + config.magnitude = input.tag.get_property("MAGNITUDE", config.magnitude); + config.return_speed = input.tag.get_property("RETURN_SPEED", config.return_speed); + config.mode1 = input.tag.get_property("MODE1", config.mode1); + config.global = input.tag.get_property("GLOBAL", config.global); + config.rotate = input.tag.get_property("ROTATE", config.rotate); + config.addtrackedframeoffset = input.tag.get_property("ADDTRACKEDFRAMEOFFSET", config.addtrackedframeoffset); + config.mode2 = input.tag.get_property("MODE2", config.mode2); + config.draw_vectors = input.tag.get_property("DRAW_VECTORS", config.draw_vectors); + config.mode3 = input.tag.get_property("MODE3", config.mode3); + config.track_frame = input.tag.get_property("TRACK_FRAME", config.track_frame); + config.bottom_is_master = input.tag.get_property("BOTTOM_IS_MASTER", config.bottom_is_master); + config.horizontal_only = input.tag.get_property("HORIZONTAL_ONLY", config.horizontal_only); + config.vertical_only = input.tag.get_property("VERTICAL_ONLY", config.vertical_only); + } + } + } + config.boundaries(); +} + + + + + + + + + +void MotionCVMain::allocate_temp(int w, int h, int color_model) +{ + if(temp_frame && + (temp_frame->get_w() != w || + temp_frame->get_h() != h)) + { + delete temp_frame; + temp_frame = 0; + } + if(!temp_frame) + temp_frame = new VFrame(w, h, color_model); +} + + + +void MotionCVMain::process_global() +{ + if(!engine) engine = new MotionCVScan(this, + PluginClient::get_project_smp() + 1, + PluginClient::get_project_smp() + 1); + +// Get the current motion vector between the previous and current frame + engine->scan_frame(current_global_ref, prev_global_ref); + current_dx = engine->dx_result; + current_dy = engine->dy_result; + +// Add current motion vector to accumulation vector. + if(config.mode3 != MotionCVConfig::TRACK_SINGLE) + { +// Retract over time + total_dx = (int64_t)total_dx * (100 - config.return_speed) / 100; + total_dy = (int64_t)total_dy * (100 - config.return_speed) / 100; + total_dx += engine->dx_result; + total_dy += engine->dy_result; + } + else +// Make accumulation vector current + { + total_dx = engine->dx_result; + total_dy = engine->dy_result; + } + +// Clamp accumulation vector + if(config.magnitude < 100) + { + //int block_w = (int64_t)config.global_block_w * + // current_global_ref->get_w() / 100; + //int block_h = (int64_t)config.global_block_h * + // current_global_ref->get_h() / 100; + int block_x_orig = (int64_t)(config.block_x * + current_global_ref->get_w() / + 100); + int block_y_orig = (int64_t)(config.block_y * + current_global_ref->get_h() / + 100); + + int max_block_x = (int64_t)(current_global_ref->get_w() - block_x_orig) * + OVERSAMPLE * + config.magnitude / + 100; + int max_block_y = (int64_t)(current_global_ref->get_h() - block_y_orig) * + OVERSAMPLE * + config.magnitude / + 100; + int min_block_x = (int64_t)-block_x_orig * + OVERSAMPLE * + config.magnitude / + 100; + int min_block_y = (int64_t)-block_y_orig * + OVERSAMPLE * + config.magnitude / + 100; + + CLAMP(total_dx, min_block_x, max_block_x); + CLAMP(total_dy, min_block_y, max_block_y); + } + +#ifdef DEBUG +printf("MotionCVMain::process_global 2 total_dx=%.02f total_dy=%.02f\n", +(float)total_dx / OVERSAMPLE, +(float)total_dy / OVERSAMPLE); +#endif + + if(config.mode3 != MotionCVConfig::TRACK_SINGLE && !config.rotate) + { +// Transfer current reference frame to previous reference frame and update +// counter. Must wait for rotate to compare. + prev_global_ref->copy_from(current_global_ref); + previous_frame_number = get_source_position(); + } + +// Decide what to do with target based on requested operation + int interpolation = NEAREST_NEIGHBOR; + float dx = 0; + float dy = 0; + switch(config.mode1) + { + case MotionCVConfig::NOTHING: + global_target_dst->copy_from(global_target_src); + break; + case MotionCVConfig::TRACK_PIXEL: + interpolation = NEAREST_NEIGHBOR; + dx = (int)(total_dx / OVERSAMPLE); + dy = (int)(total_dy / OVERSAMPLE); + break; + case MotionCVConfig::STABILIZE_PIXEL: + interpolation = NEAREST_NEIGHBOR; + dx = -(int)(total_dx / OVERSAMPLE); + dy = -(int)(total_dy / OVERSAMPLE); + break; + break; + case MotionCVConfig::TRACK: + interpolation = CUBIC_LINEAR; + dx = (float)total_dx / OVERSAMPLE; + dy = (float)total_dy / OVERSAMPLE; + break; + case MotionCVConfig::STABILIZE: + interpolation = CUBIC_LINEAR; + dx = -(float)total_dx / OVERSAMPLE; + dy = -(float)total_dy / OVERSAMPLE; + break; + } + + + if(config.mode1 != MotionCVConfig::NOTHING) + { + if(!overlayer) + overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1); + global_target_dst->clear_frame(); + overlayer->overlay(global_target_dst, + global_target_src, + 0, + 0, + global_target_src->get_w(), + global_target_src->get_h(), + dx, + dy, + (float)global_target_src->get_w() + dx, + (float)global_target_src->get_h() + dy, + 1, + TRANSFER_REPLACE, + interpolation); + } +} + + + +void MotionCVMain::process_rotation() +{ + int block_x; + int block_y; + +// Convert the previous global reference into the previous rotation reference. +// Convert global target destination into rotation target source. + if(config.global) + { + if(!overlayer) + overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1); + float dx; + float dy; + if(config.mode3 == MotionCVConfig::TRACK_SINGLE) + { + dx = (float)total_dx / OVERSAMPLE; + dy = (float)total_dy / OVERSAMPLE; + } + else + { + dx = (float)current_dx / OVERSAMPLE; + dy = (float)current_dy / OVERSAMPLE; + } + + prev_rotate_ref->clear_frame(); + overlayer->overlay(prev_rotate_ref, + prev_global_ref, + 0, + 0, + prev_global_ref->get_w(), + prev_global_ref->get_h(), + dx, + dy, + (float)prev_global_ref->get_w() + dx, + (float)prev_global_ref->get_h() + dy, + 1, + TRANSFER_REPLACE, + CUBIC_LINEAR); +// Pivot is destination global position + block_x = (int)(prev_rotate_ref->get_w() * + config.block_x / + 100 + + (float)total_dx / + OVERSAMPLE); + block_y = (int)(prev_rotate_ref->get_h() * + config.block_y / + 100 + + (float)total_dy / + OVERSAMPLE); +// Use the global target output as the rotation target input + rotate_target_src->copy_from(global_target_dst); +// Transfer current reference frame to previous reference frame for global. + if(config.mode3 != MotionCVConfig::TRACK_SINGLE) + { + prev_global_ref->copy_from(current_global_ref); + previous_frame_number = get_source_position(); + } + } + else + { +// Pivot is fixed + block_x = (int)(prev_rotate_ref->get_w() * + config.block_x / + 100); + block_y = (int)(prev_rotate_ref->get_h() * + config.block_y / + 100); + } + + + +// Get rotation + if(!motion_rotate) + motion_rotate = new RotateCVScan(this, + get_project_smp() + 1, + get_project_smp() + 1); + + current_angle = motion_rotate->scan_frame(prev_rotate_ref, + current_rotate_ref, + block_x, + block_y); + + + +// Add current rotation to accumulation + if(config.mode3 != MotionCVConfig::TRACK_SINGLE) + { +// Retract over time + total_angle = total_angle * (100 - config.return_speed) / 100; + total_angle += current_angle; + + if(!config.global) + { +// Transfer current reference frame to previous reference frame and update +// counter. + prev_rotate_ref->copy_from(current_rotate_ref); + previous_frame_number = get_source_position(); + } + } + else + { + total_angle = current_angle; + } + +#ifdef DEBUG +printf("MotionCVMain::process_rotation total_angle=%f\n", total_angle); +#endif + + +// Calculate rotation parameters based on requested operation + float angle = 0; + switch(config.mode1) + { + case MotionCVConfig::NOTHING: + rotate_target_dst->copy_from(rotate_target_src); + break; + case MotionCVConfig::TRACK: + case MotionCVConfig::TRACK_PIXEL: + angle = total_angle; + break; + case MotionCVConfig::STABILIZE: + case MotionCVConfig::STABILIZE_PIXEL: + angle = -total_angle; + break; + } + + + + if(config.mode1 != MotionCVConfig::NOTHING) + { + if(!rotate_engine) + rotate_engine = new AffineEngine(PluginClient::get_project_smp() + 1, + PluginClient::get_project_smp() + 1); + + rotate_target_dst->clear_frame(); + +// Determine pivot based on a number of factors. + switch(config.mode1) + { + case MotionCVConfig::TRACK: + case MotionCVConfig::TRACK_PIXEL: +// Use destination of global tracking. + rotate_engine->set_pivot(block_x, block_y); + break; + + case MotionCVConfig::STABILIZE: + case MotionCVConfig::STABILIZE_PIXEL: + if(config.global) + { +// Use origin of global stabilize operation + rotate_engine->set_pivot((int)(rotate_target_dst->get_w() * + config.block_x / + 100), + (int)(rotate_target_dst->get_h() * + config.block_y / + 100)); + + } + else + { +// Use origin + rotate_engine->set_pivot(block_x, block_y); + } + break; + } + + + rotate_engine->rotate(rotate_target_dst, rotate_target_src, angle); +// overlayer->overlay(rotate_target_dst, +// prev_rotate_ref, +// 0, +// 0, +// prev_rotate_ref->get_w(), +// prev_rotate_ref->get_h(), +// 0, +// 0, +// prev_rotate_ref->get_w(), +// prev_rotate_ref->get_h(), +// 1, +// TRANSFER_NORMAL, +// CUBIC_LINEAR); +// overlayer->overlay(rotate_target_dst, +// current_rotate_ref, +// 0, +// 0, +// prev_rotate_ref->get_w(), +// prev_rotate_ref->get_h(), +// 0, +// 0, +// prev_rotate_ref->get_w(), +// prev_rotate_ref->get_h(), +// 1, +// TRANSFER_NORMAL, +// CUBIC_LINEAR); + + + } + + +} + + + + + + + + + +int MotionCVMain::process_buffer(VFrame **frame, + int64_t start_position, + double frame_rate) +{ + int need_reconfigure = load_configuration(); + int color_model = frame[0]->get_color_model(); + w = frame[0]->get_w(); + h = frame[0]->get_h(); + + +#ifdef DEBUG +printf("MotionCVMain::process_buffer 1 start_position=%jd\n", start_position); +#endif + + +// Calculate the source and destination pointers for each of the operations. +// Get the layer to track motion in. + reference_layer = config.bottom_is_master ? + PluginClient::total_in_buffers - 1 : + 0; +// Get the layer to apply motion in. + target_layer = config.bottom_is_master ? + 0 : + PluginClient::total_in_buffers - 1; + + + output_frame = frame[target_layer]; + + +// Get the position of previous reference frame. + int64_t actual_previous_number; +// Skip if match frame not available + int skip_current = 0; + + + if(config.mode3 == MotionCVConfig::TRACK_SINGLE) + { + actual_previous_number = config.track_frame; + if(get_direction() == PLAY_REVERSE) + actual_previous_number++; + if(actual_previous_number == start_position) + skip_current = 1; + } + else + { + actual_previous_number = start_position; + if(get_direction() == PLAY_FORWARD) + { + actual_previous_number--; + if(actual_previous_number < get_source_start()) + skip_current = 1; + else + { + KeyFrame *keyframe = get_prev_keyframe(start_position, 1); + if(keyframe->position > 0 && + actual_previous_number < keyframe->position) + skip_current = 1; + } + } + else + { + actual_previous_number++; + if(actual_previous_number >= get_source_start() + get_total_len()) + skip_current = 1; + else + { + KeyFrame *keyframe = get_next_keyframe(start_position, 1); + if(keyframe->position > 0 && + actual_previous_number >= keyframe->position) + skip_current = 1; + } + } + +// Only count motion since last keyframe + + + } + + + if(!config.global && !config.rotate) skip_current = 1; + + + + +// printf("process_realtime %d %lld %lld\n", +// skip_current, +// previous_frame_number, +// actual_previous_number); +// Load match frame and reset vectors + int need_reload = !skip_current && + (previous_frame_number != actual_previous_number || + need_reconfigure); + if(need_reload) + { + total_dx = 0; + total_dy = 0; + total_angle = 0; + previous_frame_number = actual_previous_number; + } + + + if(skip_current) + { + total_dx = 0; + total_dy = 0; + current_dx = 0; + current_dy = 0; + total_angle = 0; + current_angle = 0; + } + + + + +// Get the global pointers. Here we walk through the sequence of events. + if(config.global) + { +// Assume global only. Global reads previous frame and compares +// with current frame to get the current translation. +// The center of the search area is fixed in compensate mode or +// the user value + the accumulation vector in track mode. + if(!prev_global_ref) + prev_global_ref = new VFrame(w, h, color_model); + if(!current_global_ref) + current_global_ref = new VFrame(w, h, color_model); + +// Global loads the current target frame into the src and +// writes it to the dst frame with desired translation. + if(!global_target_src) + global_target_src = new VFrame(w, h, color_model); + if(!global_target_dst) + global_target_dst = new VFrame(w, h, color_model); + + +// Load the global frames + if(need_reload) + { + read_frame(prev_global_ref, + reference_layer, + previous_frame_number, + frame_rate, + 0); + } + + read_frame(current_global_ref, + reference_layer, + start_position, + frame_rate, + 0); + read_frame(global_target_src, + target_layer, + start_position, + frame_rate, + 0); + + + +// Global followed by rotate + if(config.rotate) + { +// Must translate the previous global reference by the current global +// accumulation vector to match the current global reference. +// The center of the search area is always the user value + the accumulation +// vector. + if(!prev_rotate_ref) + prev_rotate_ref = new VFrame(w, h, color_model); +// The current global reference is the current rotation reference. + if(!current_rotate_ref) + current_rotate_ref = new VFrame(w, h, color_model); + current_rotate_ref->copy_from(current_global_ref); + +// The global target destination is copied to the rotation target source +// then written to the rotation output with rotation. +// The pivot for the rotation is the center of the search area +// if we're tracking. +// The pivot is fixed to the user position if we're compensating. + if(!rotate_target_src) + rotate_target_src = new VFrame(w, h, color_model); + if(!rotate_target_dst) + rotate_target_dst = new VFrame(w,h , color_model); + } + } + else +// Rotation only + if(config.rotate) + { +// Rotation reads the previous reference frame and compares it with current +// reference frame. + if(!prev_rotate_ref) + prev_rotate_ref = new VFrame(w, h, color_model); + if(!current_rotate_ref) + current_rotate_ref = new VFrame(w, h, color_model); + +// Rotation loads target frame to temporary, rotates it, and writes it to the +// target frame. The pivot is always fixed. + if(!rotate_target_src) + rotate_target_src = new VFrame(w, h, color_model); + if(!rotate_target_dst) + rotate_target_dst = new VFrame(w,h , color_model); + + +// Load the rotate frames + if(need_reload) + { + read_frame(prev_rotate_ref, + reference_layer, + previous_frame_number, + frame_rate, + 0); + } + read_frame(current_rotate_ref, + reference_layer, + start_position, + frame_rate, + 0); + read_frame(rotate_target_src, + target_layer, + start_position, + frame_rate, + 0); + } + + + + + + + + + + + if(!skip_current) + { +// Get position change from previous frame to current frame + if(config.global) process_global(); +// Get rotation change from previous frame to current frame + if(config.rotate) process_rotation(); +//frame[target_layer]->copy_from(prev_rotate_ref); +//frame[target_layer]->copy_from(current_rotate_ref); + } + + + + + + +// Transfer the relevant target frame to the output + if(!skip_current) + { + if(config.rotate) + { + frame[target_layer]->copy_from(rotate_target_dst); + } + else + { + frame[target_layer]->copy_from(global_target_dst); + } + } + else +// Read the target destination directly + { + read_frame(frame[target_layer], + target_layer, + start_position, + frame_rate, + 0); + } + + if(config.draw_vectors) + { + draw_vectors(frame[target_layer]); + } + +#ifdef DEBUG +printf("MotionCVMain::process_buffer 100\n"); +#endif + return 0; +} + + +void MotionCVMain::clamp_scan(int w, + int h, + int *block_x1, + int *block_y1, + int *block_x2, + int *block_y2, + int *scan_x1, + int *scan_y1, + int *scan_x2, + int *scan_y2, + int use_absolute) +{ +// printf("MotionCVMain::clamp_scan 1 w=%d h=%d block=%d %d %d %d scan=%d %d %d %d absolute=%d\n", +// w, +// h, +// *block_x1, +// *block_y1, +// *block_x2, +// *block_y2, +// *scan_x1, +// *scan_y1, +// *scan_x2, +// *scan_y2, +// use_absolute); + + if(use_absolute) + { +// scan is always out of range before block. + if(*scan_x1 < 0) + { + int difference = -*scan_x1; + *block_x1 += difference; + *scan_x1 = 0; + } + + if(*scan_y1 < 0) + { + int difference = -*scan_y1; + *block_y1 += difference; + *scan_y1 = 0; + } + + if(*scan_x2 > w) + { + int difference = *scan_x2 - w; + *block_x2 -= difference; + *scan_x2 -= difference; + } + + if(*scan_y2 > h) + { + int difference = *scan_y2 - h; + *block_y2 -= difference; + *scan_y2 -= difference; + } + + CLAMP(*scan_x1, 0, w); + CLAMP(*scan_y1, 0, h); + CLAMP(*scan_x2, 0, w); + CLAMP(*scan_y2, 0, h); + } + else + { + if(*scan_x1 < 0) + { + int difference = -*scan_x1; + *block_x1 += difference; + *scan_x2 += difference; + *scan_x1 = 0; + } + + if(*scan_y1 < 0) + { + int difference = -*scan_y1; + *block_y1 += difference; + *scan_y2 += difference; + *scan_y1 = 0; + } + + if(*scan_x2 - *block_x1 + *block_x2 > w) + { + int difference = *scan_x2 - *block_x1 + *block_x2 - w; + *block_x2 -= difference; + } + + if(*scan_y2 - *block_y1 + *block_y2 > h) + { + int difference = *scan_y2 - *block_y1 + *block_y2 - h; + *block_y2 -= difference; + } + +// CLAMP(*scan_x1, 0, w - (*block_x2 - *block_x1)); +// CLAMP(*scan_y1, 0, h - (*block_y2 - *block_y1)); +// CLAMP(*scan_x2, 0, w - (*block_x2 - *block_x1)); +// CLAMP(*scan_y2, 0, h - (*block_y2 - *block_y1)); + } + +// Sanity checks which break the calculation but should never happen if the +// center of the block is inside the frame. + CLAMP(*block_x1, 0, w); + CLAMP(*block_x2, 0, w); + CLAMP(*block_y1, 0, h); + CLAMP(*block_y2, 0, h); + +// printf("MotionCVMain::clamp_scan 2 w=%d h=%d block=%d %d %d %d scan=%d %d %d %d absolute=%d\n", +// w, +// h, +// *block_x1, +// *block_y1, +// *block_x2, +// *block_y2, +// *scan_x1, +// *scan_y1, +// *scan_x2, +// *scan_y2, +// use_absolute); +} + + + +void MotionCVMain::draw_vectors(VFrame *frame) +{ + int w = frame->get_w(); + int h = frame->get_h(); + int global_x1, global_y1; + int global_x2, global_y2; + int block_x, block_y; + int block_w, block_h; + int block_x1, block_y1; + int block_x2, block_y2; + int block_x3, block_y3; + int block_x4, block_y4; + int search_w, search_h; + int search_x1, search_y1; + int search_x2, search_y2; + //int search_x3, search_y3; + //int search_x4, search_y4; + + if(config.global) + { +// Get vector +// Start of vector is center of previous block. +// End of vector is total accumulation. + if(config.mode3 == MotionCVConfig::TRACK_SINGLE) + { + global_x1 = (int64_t)(config.block_x * + w / + 100); + global_y1 = (int64_t)(config.block_y * + h / + 100); + global_x2 = global_x1 + total_dx / OVERSAMPLE; + global_y2 = global_y1 + total_dy / OVERSAMPLE; +//printf("MotionCVMain::draw_vectors %d %d %d %d %d %d\n", total_dx, total_dy, global_x1, global_y1, global_x2, global_y2); + } + else +// Start of vector is center of previous block. +// End of vector is current change. + if(config.mode3 == MotionCVConfig::PREVIOUS_SAME_BLOCK) + { + global_x1 = (int64_t)(config.block_x * + w / + 100); + global_y1 = (int64_t)(config.block_y * + h / + 100); + global_x2 = global_x1 + current_dx / OVERSAMPLE; + global_y2 = global_y1 + current_dy / OVERSAMPLE; + } + else + { + global_x1 = (int64_t)(config.block_x * + w / + 100 + + (total_dx - current_dx) / + OVERSAMPLE); + global_y1 = (int64_t)(config.block_y * + h / + 100 + + (total_dy - current_dy) / + OVERSAMPLE); + global_x2 = (int64_t)(config.block_x * + w / + 100 + + total_dx / + OVERSAMPLE); + global_y2 = (int64_t)(config.block_y * + h / + 100 + + total_dy / + OVERSAMPLE); + } + + block_x = global_x1; + block_y = global_y1; + block_w = config.global_block_w * w / 100; + block_h = config.global_block_h * h / 100; + block_x1 = block_x - block_w / 2; + block_y1 = block_y - block_h / 2; + block_x2 = block_x + block_w / 2; + block_y2 = block_y + block_h / 2; + search_w = config.global_range_w * w / 100; + search_h = config.global_range_h * h / 100; + search_x1 = block_x1 - search_w / 2; + search_y1 = block_y1 - search_h / 2; + search_x2 = block_x2 + search_w / 2; + search_y2 = block_y2 + search_h / 2; + +// printf("MotionCVMain::draw_vectors %d %d %d %d %d %d %d %d %d %d %d %d\n", +// global_x1, +// global_y1, +// block_w, +// block_h, +// block_x1, +// block_y1, +// block_x2, +// block_y2, +// search_x1, +// search_y1, +// search_x2, +// search_y2); + + clamp_scan(w, + h, + &block_x1, + &block_y1, + &block_x2, + &block_y2, + &search_x1, + &search_y1, + &search_x2, + &search_y2, + 1); + +// Vector + draw_arrow(frame, global_x1, global_y1, global_x2, global_y2); + +// Macroblock + draw_line(frame, block_x1, block_y1, block_x2, block_y1); + draw_line(frame, block_x2, block_y1, block_x2, block_y2); + draw_line(frame, block_x2, block_y2, block_x1, block_y2); + draw_line(frame, block_x1, block_y2, block_x1, block_y1); + + +// Search area + draw_line(frame, search_x1, search_y1, search_x2, search_y1); + draw_line(frame, search_x2, search_y1, search_x2, search_y2); + draw_line(frame, search_x2, search_y2, search_x1, search_y2); + draw_line(frame, search_x1, search_y2, search_x1, search_y1); + +// Block should be endpoint of motion + if(config.rotate) + { + block_x = global_x2; + block_y = global_y2; + } + } + else + { + block_x = (int64_t)(config.block_x * w / 100); + block_y = (int64_t)(config.block_y * h / 100); + } + + block_w = config.rotation_block_w * w / 100; + block_h = config.rotation_block_h * h / 100; + if(config.rotate) + { + float angle = total_angle * 2 * M_PI / 360; + double base_angle1 = atan((float)block_h / block_w); + double base_angle2 = atan((float)block_w / block_h); + double target_angle1 = base_angle1 + angle; + double target_angle2 = base_angle2 + angle; + double radius = sqrt(block_w * block_w + block_h * block_h) / 2; + block_x1 = (int)(block_x - cos(target_angle1) * radius); + block_y1 = (int)(block_y - sin(target_angle1) * radius); + block_x2 = (int)(block_x + sin(target_angle2) * radius); + block_y2 = (int)(block_y - cos(target_angle2) * radius); + block_x3 = (int)(block_x - sin(target_angle2) * radius); + block_y3 = (int)(block_y + cos(target_angle2) * radius); + block_x4 = (int)(block_x + cos(target_angle1) * radius); + block_y4 = (int)(block_y + sin(target_angle1) * radius); + + draw_line(frame, block_x1, block_y1, block_x2, block_y2); + draw_line(frame, block_x2, block_y2, block_x4, block_y4); + draw_line(frame, block_x4, block_y4, block_x3, block_y3); + draw_line(frame, block_x3, block_y3, block_x1, block_y1); + + +// Center + if(!config.global) + { + draw_line(frame, block_x, block_y - 5, block_x, block_y + 6); + draw_line(frame, block_x - 5, block_y, block_x + 6, block_y); + } + } +} + + + +void MotionCVMain::draw_pixel(VFrame *frame, int x, int y) +{ + if(!(x >= 0 && y >= 0 && x < frame->get_w() && y < frame->get_h())) return; + +#define DRAW_PIXEL(x, y, components, do_yuv, max, type) \ +{ \ + type **rows = (type**)frame->get_rows(); \ + rows[y][x * components] = max - rows[y][x * components]; \ + if(!do_yuv) \ + { \ + rows[y][x * components + 1] = max - rows[y][x * components + 1]; \ + rows[y][x * components + 2] = max - rows[y][x * components + 2]; \ + } \ + else \ + { \ + rows[y][x * components + 1] = (max / 2 + 1) - rows[y][x * components + 1]; \ + rows[y][x * components + 2] = (max / 2 + 1) - rows[y][x * components + 2]; \ + } \ + if(components == 4) \ + rows[y][x * components + 3] = max; \ +} + + + switch(frame->get_color_model()) + { + case BC_RGB888: + DRAW_PIXEL(x, y, 3, 0, 0xff, unsigned char); + break; + case BC_RGBA8888: + DRAW_PIXEL(x, y, 4, 0, 0xff, unsigned char); + break; + case BC_RGB_FLOAT: + DRAW_PIXEL(x, y, 3, 0, 1.0, float); + break; + case BC_RGBA_FLOAT: + DRAW_PIXEL(x, y, 4, 0, 1.0, float); + break; + case BC_YUV888: + DRAW_PIXEL(x, y, 3, 1, 0xff, unsigned char); + break; + case BC_YUVA8888: + DRAW_PIXEL(x, y, 4, 1, 0xff, unsigned char); + break; + case BC_RGB161616: + DRAW_PIXEL(x, y, 3, 0, 0xffff, uint16_t); + break; + case BC_YUV161616: + DRAW_PIXEL(x, y, 3, 1, 0xffff, uint16_t); + break; + case BC_RGBA16161616: + DRAW_PIXEL(x, y, 4, 0, 0xffff, uint16_t); + break; + case BC_YUVA16161616: + DRAW_PIXEL(x, y, 4, 1, 0xffff, uint16_t); + break; + } +} + + +void MotionCVMain::draw_line(VFrame *frame, int x1, int y1, int x2, int y2) +{ + int w = labs(x2 - x1); + int h = labs(y2 - y1); +//printf("MotionCVMain::draw_line 1 %d %d %d %d\n", x1, y1, x2, y2); + + if(!w && !h) + { + draw_pixel(frame, x1, y1); + } + else + if(w > h) + { +// Flip coordinates so x1 < x2 + if(x2 < x1) + { + y2 ^= y1; + y1 ^= y2; + y2 ^= y1; + x1 ^= x2; + x2 ^= x1; + x1 ^= x2; + } + int numerator = y2 - y1; + int denominator = x2 - x1; + for(int i = x1; i < x2; i++) + { + int y = y1 + (int64_t)(i - x1) * (int64_t)numerator / (int64_t)denominator; + draw_pixel(frame, i, y); + } + } + else + { +// Flip coordinates so y1 < y2 + if(y2 < y1) + { + y2 ^= y1; + y1 ^= y2; + y2 ^= y1; + x1 ^= x2; + x2 ^= x1; + x1 ^= x2; + } + int numerator = x2 - x1; + int denominator = y2 - y1; + for(int i = y1; i < y2; i++) + { + int x = x1 + (int64_t)(i - y1) * (int64_t)numerator / (int64_t)denominator; + draw_pixel(frame, x, i); + } + } +//printf("MotionCVMain::draw_line 2\n"); +} + +#define ARROW_SIZE 10 +void MotionCVMain::draw_arrow(VFrame *frame, int x1, int y1, int x2, int y2) +{ + double angle = atan((float)(y2 - y1) / (float)(x2 - x1)); + double angle1 = angle + (float)145 / 360 * 2 * 3.14159265; + double angle2 = angle - (float)145 / 360 * 2 * 3.14159265; + int x3; + int y3; + int x4; + int y4; + if(x2 < x1) + { + x3 = x2 - (int)(ARROW_SIZE * cos(angle1)); + y3 = y2 - (int)(ARROW_SIZE * sin(angle1)); + x4 = x2 - (int)(ARROW_SIZE * cos(angle2)); + y4 = y2 - (int)(ARROW_SIZE * sin(angle2)); + } + else + { + x3 = x2 + (int)(ARROW_SIZE * cos(angle1)); + y3 = y2 + (int)(ARROW_SIZE * sin(angle1)); + x4 = x2 + (int)(ARROW_SIZE * cos(angle2)); + y4 = y2 + (int)(ARROW_SIZE * sin(angle2)); + } + +// Main vector + draw_line(frame, x1, y1, x2, y2); +// draw_line(frame, x1, y1 + 1, x2, y2 + 1); + +// Arrow line + if(abs(y2 - y1) || abs(x2 - x1)) draw_line(frame, x2, y2, x3, y3); +// draw_line(frame, x2, y2 + 1, x3, y3 + 1); +// Arrow line + if(abs(y2 - y1) || abs(x2 - x1)) draw_line(frame, x2, y2, x4, y4); +// draw_line(frame, x2, y2 + 1, x4, y4 + 1); +} + + + + +#define ABS_DIFF(type, temp_type, multiplier, components) \ +{ \ + temp_type result_temp = 0; \ + for(int i = 0; i < h; i++) \ + { \ + type *prev_row = (type*)prev_ptr; \ + type *current_row = (type*)current_ptr; \ + for(int j = 0; j < w; j++) \ + { \ + for(int k = 0; k < 3; k++) \ + { \ + temp_type difference; \ + difference = *prev_row++ - *current_row++; \ + if(difference < 0) \ + result_temp -= difference; \ + else \ + result_temp += difference; \ + } \ + if(components == 4) \ + { \ + prev_row++; \ + current_row++; \ + } \ + } \ + prev_ptr += row_bytes; \ + current_ptr += row_bytes; \ + } \ + result = (int64_t)(result_temp * multiplier); \ +} + +int64_t MotionCVMain::abs_diff(unsigned char *prev_ptr, + unsigned char *current_ptr, + int row_bytes, + int w, + int h, + int color_model) +{ + int64_t result = 0; + switch(color_model) + { + case BC_RGB888: + ABS_DIFF(unsigned char, int64_t, 1, 3) + break; + case BC_RGBA8888: + ABS_DIFF(unsigned char, int64_t, 1, 4) + break; + case BC_RGB_FLOAT: + ABS_DIFF(float, double, 0x10000, 3) + break; + case BC_RGBA_FLOAT: + ABS_DIFF(float, double, 0x10000, 4) + break; + case BC_YUV888: + ABS_DIFF(unsigned char, int64_t, 1, 3) + break; + case BC_YUVA8888: + ABS_DIFF(unsigned char, int64_t, 1, 4) + break; + case BC_YUV161616: + ABS_DIFF(uint16_t, int64_t, 1, 3) + break; + case BC_YUVA16161616: + ABS_DIFF(uint16_t, int64_t, 1, 4) + break; + } + return result; +} + + + +#define ABS_DIFF_SUB(type, temp_type, multiplier, components) \ +{ \ + temp_type result_temp = 0; \ + temp_type y2_fraction = sub_y * 0x100 / OVERSAMPLE; \ + temp_type y1_fraction = 0x100 - y2_fraction; \ + temp_type x2_fraction = sub_x * 0x100 / OVERSAMPLE; \ + temp_type x1_fraction = 0x100 - x2_fraction; \ + for(int i = 0; i < h_sub; i++) \ + { \ + type *prev_row1 = (type*)prev_ptr; \ + type *prev_row2 = (type*)prev_ptr + components; \ + type *prev_row3 = (type*)(prev_ptr + row_bytes); \ + type *prev_row4 = (type*)(prev_ptr + row_bytes) + components; \ + type *current_row = (type*)current_ptr; \ + for(int j = 0; j < w_sub; j++) \ + { \ + for(int k = 0; k < 3; k++) \ + { \ + temp_type difference; \ + temp_type prev_value = \ + (*prev_row1++ * x1_fraction * y1_fraction + \ + *prev_row2++ * x2_fraction * y1_fraction + \ + *prev_row3++ * x1_fraction * y2_fraction + \ + *prev_row4++ * x2_fraction * y2_fraction) / \ + 0x100 / 0x100; \ + temp_type current_value = *current_row++; \ + difference = prev_value - current_value; \ + if(difference < 0) \ + result_temp -= difference; \ + else \ + result_temp += difference; \ + } \ + \ + if(components == 4) \ + { \ + prev_row1++; \ + prev_row2++; \ + prev_row3++; \ + prev_row4++; \ + current_row++; \ + } \ + } \ + prev_ptr += row_bytes; \ + current_ptr += row_bytes; \ + } \ + result = (int64_t)(result_temp * multiplier); \ +} + + + + +int64_t MotionCVMain::abs_diff_sub(unsigned char *prev_ptr, + unsigned char *current_ptr, + int row_bytes, + int w, + int h, + int color_model, + int sub_x, + int sub_y) +{ + int h_sub = h - 1; + int w_sub = w - 1; + int64_t result = 0; + + switch(color_model) + { + case BC_RGB888: + ABS_DIFF_SUB(unsigned char, int64_t, 1, 3) + break; + case BC_RGBA8888: + ABS_DIFF_SUB(unsigned char, int64_t, 1, 4) + break; + case BC_RGB_FLOAT: + ABS_DIFF_SUB(float, double, 0x10000, 3) + break; + case BC_RGBA_FLOAT: + ABS_DIFF_SUB(float, double, 0x10000, 4) + break; + case BC_YUV888: + ABS_DIFF_SUB(unsigned char, int64_t, 1, 3) + break; + case BC_YUVA8888: + ABS_DIFF_SUB(unsigned char, int64_t, 1, 4) + break; + case BC_YUV161616: + ABS_DIFF_SUB(uint16_t, int64_t, 1, 3) + break; + case BC_YUVA16161616: + ABS_DIFF_SUB(uint16_t, int64_t, 1, 4) + break; + } + return result; +} + + + + + +MotionCVScanPackage::MotionCVScanPackage() + : LoadPackage() +{ + valid = 1; +} + + + + + + +MotionCVScanUnit::MotionCVScanUnit(MotionCVScan *server, + MotionCVMain *plugin) + : LoadClient(server) +{ + this->plugin = plugin; + this->server = server; + cache_lock = new Mutex("MotionCVScanUnit::cache_lock"); +} + +MotionCVScanUnit::~MotionCVScanUnit() +{ + delete cache_lock; +} + + + +void MotionCVScanUnit::process_package(LoadPackage *package) +{ + MotionCVScanPackage *pkg = (MotionCVScanPackage*)package; + //int w = server->current_frame->get_w(); + //int h = server->current_frame->get_h(); + int color_model = server->current_frame->get_color_model(); + int pixel_size = BC_CModels::calculate_pixelsize(color_model); + int row_bytes = server->current_frame->get_bytes_per_line(); + + + + + + + + + + + + +// Single pixel + if(!server->subpixel) + { + int search_x = pkg->scan_x1 + (pkg->pixel % (pkg->scan_x2 - pkg->scan_x1)); + int search_y = pkg->scan_y1 + (pkg->pixel / (pkg->scan_x2 - pkg->scan_x1)); + +// Try cache + pkg->difference1 = server->get_cache(search_x, search_y); + if(pkg->difference1 < 0) + { +//printf("MotionCVScanUnit::process_package 1 %d %d\n", +//search_x, search_y, pkg->block_x2 - pkg->block_x1, pkg->block_y2 - pkg->block_y1); +// Pointers to first pixel in each block + unsigned char *prev_ptr = server->previous_frame->get_rows()[ + search_y] + + search_x * pixel_size; + unsigned char *current_ptr = server->current_frame->get_rows()[ + pkg->block_y1] + + pkg->block_x1 * pixel_size; +// Scan block + pkg->difference1 = plugin->abs_diff(prev_ptr, + current_ptr, + row_bytes, + pkg->block_x2 - pkg->block_x1, + pkg->block_y2 - pkg->block_y1, + color_model); +//printf("MotionCVScanUnit::process_package 2\n"); + server->put_cache(search_x, search_y, pkg->difference1); + } + } + + + + + + + + else + + + + + + + + +// Sub pixel + { + int sub_x = pkg->pixel % (OVERSAMPLE * 2 - 1) + 1; + int sub_y = pkg->pixel / (OVERSAMPLE * 2 - 1) + 1; + + if(plugin->config.horizontal_only) + { + sub_y = 0; + } + + if(plugin->config.vertical_only) + { + sub_x = 0; + } + + int search_x = pkg->scan_x1 + sub_x / OVERSAMPLE; + int search_y = pkg->scan_y1 + sub_y / OVERSAMPLE; + sub_x %= OVERSAMPLE; + sub_y %= OVERSAMPLE; + + + unsigned char *prev_ptr = server->previous_frame->get_rows()[ + search_y] + + search_x * pixel_size; + unsigned char *current_ptr = server->current_frame->get_rows()[ + pkg->block_y1] + + pkg->block_x1 * pixel_size; + +// With subpixel, there are two ways to compare each position, one by shifting +// the previous frame and two by shifting the current frame. + pkg->difference1 = plugin->abs_diff_sub(prev_ptr, + current_ptr, + row_bytes, + pkg->block_x2 - pkg->block_x1, + pkg->block_y2 - pkg->block_y1, + color_model, + sub_x, + sub_y); + pkg->difference2 = plugin->abs_diff_sub(current_ptr, + prev_ptr, + row_bytes, + pkg->block_x2 - pkg->block_x1, + pkg->block_y2 - pkg->block_y1, + color_model, + sub_x, + sub_y); +// printf("MotionCVScanUnit::process_package sub_x=%d sub_y=%d search_x=%d search_y=%d diff1=%lld diff2=%lld\n", +// sub_x, +// sub_y, +// search_x, +// search_y, +// pkg->difference1, +// pkg->difference2); + } + + + + +} + + + + + + + + + + +int64_t MotionCVScanUnit::get_cache(int x, int y) +{ + int64_t result = -1; + cache_lock->lock("MotionCVScanUnit::get_cache"); + for(int i = 0; i < cache.total; i++) + { + MotionCVScanCache *ptr = cache.values[i]; + if(ptr->x == x && ptr->y == y) + { + result = ptr->difference; + break; + } + } + cache_lock->unlock(); + return result; +} + +void MotionCVScanUnit::put_cache(int x, int y, int64_t difference) +{ + MotionCVScanCache *ptr = new MotionCVScanCache(x, y, difference); + cache_lock->lock("MotionCVScanUnit::put_cache"); + cache.append(ptr); + cache_lock->unlock(); +} + + + + + + + + + + + +MotionCVScan::MotionCVScan(MotionCVMain *plugin, + int total_clients, + int total_packages) + : LoadServer( +//1, 1 +total_clients, total_packages +) +{ + this->plugin = plugin; + cache_lock = new Mutex("MotionCVScan::cache_lock"); +} + +MotionCVScan::~MotionCVScan() +{ + delete cache_lock; +} + + +void MotionCVScan::init_packages() +{ +// Set package coords + for(int i = 0; i < get_total_packages(); i++) + { + MotionCVScanPackage *pkg = (MotionCVScanPackage*)get_package(i); + + pkg->block_x1 = block_x1; + pkg->block_x2 = block_x2; + pkg->block_y1 = block_y1; + pkg->block_y2 = block_y2; + pkg->scan_x1 = scan_x1; + pkg->scan_x2 = scan_x2; + pkg->scan_y1 = scan_y1; + pkg->scan_y2 = scan_y2; + pkg->pixel = (int64_t)i * (int64_t)total_pixels / (int64_t)total_steps; + pkg->difference1 = 0; + pkg->difference2 = 0; + pkg->dx = 0; + pkg->dy = 0; + pkg->valid = 1; + } +} + +LoadClient* MotionCVScan::new_client() +{ + return new MotionCVScanUnit(this, plugin); +} + +LoadPackage* MotionCVScan::new_package() +{ + return new MotionCVScanPackage; +} + + +void MotionCVScan::scan_frame(VFrame *previous_frame, + VFrame *current_frame) +{ + this->previous_frame = previous_frame; + this->current_frame = current_frame; + subpixel = 0; + + cache.remove_all_objects(); + + +// Single macroblock + int w = current_frame->get_w(); + int h = current_frame->get_h(); + +// Initial search parameters + int scan_w = w * plugin->config.global_range_w / 100; + int scan_h = h * plugin->config.global_range_h / 100; + int block_w = w * plugin->config.global_block_w / 100; + int block_h = h * plugin->config.global_block_h / 100; + +// Location of block in previous frame + block_x1 = (int)(w * plugin->config.block_x / 100 - block_w / 2); + block_y1 = (int)(h * plugin->config.block_y / 100 - block_h / 2); + block_x2 = (int)(w * plugin->config.block_x / 100 + block_w / 2); + block_y2 = (int)(h * plugin->config.block_y / 100 + block_h / 2); + +// Offset to location of previous block. This offset needn't be very accurate +// since it's the offset of the previous image and current image we want. + if(plugin->config.mode3 == MotionCVConfig::TRACK_PREVIOUS) + { + block_x1 += plugin->total_dx / OVERSAMPLE; + block_y1 += plugin->total_dy / OVERSAMPLE; + block_x2 += plugin->total_dx / OVERSAMPLE; + block_y2 += plugin->total_dy / OVERSAMPLE; + } + + skip = 0; + + switch(plugin->config.mode2) + { +// Don't calculate + case MotionCVConfig::NO_CALCULATE: + dx_result = 0; + dy_result = 0; + skip = 1; + break; + + case MotionCVConfig::LOAD: + { +// Load result from disk + char string[BCTEXTLEN]; + sprintf(string, "%s%06jd", MOTION_FILE, plugin->get_source_position()); + FILE *input = fopen(string, "r"); + if(input) + { + fscanf(input, + "%d %d", + &dx_result, + &dy_result); + fclose(input); + skip = 1; + } + break; + } + +// Scan from scratch + default: + skip = 0; + break; + } + +// Perform scan + if(!skip) + { +// Location of block in current frame + int x_result = block_x1; + int y_result = block_y1; + +// printf("MotionCVScan::scan_frame 1 %d %d %d %d %d %d %d %d\n", +// block_x1 + block_w / 2, +// block_y1 + block_h / 2, +// block_w, +// block_h, +// block_x1, +// block_y1, +// block_x2, +// block_y2); + + while(1) + { + scan_x1 = x_result - scan_w / 2; + scan_y1 = y_result - scan_h / 2; + scan_x2 = x_result + scan_w / 2; + scan_y2 = y_result + scan_h / 2; + + + +// Zero out requested values + if(plugin->config.horizontal_only) + { + scan_y1 = block_y1; + scan_y2 = block_y1 + 1; + } + if(plugin->config.vertical_only) + { + scan_x1 = block_x1; + scan_x2 = block_x1 + 1; + } + +// printf("MotionCVScan::scan_frame 1 %d %d %d %d %d %d %d %d\n", +// block_x1, +// block_y1, +// block_x2, +// block_y2, +// scan_x1, +// scan_y1, +// scan_x2, +// scan_y2); +// Clamp the block coords before the scan so we get useful scan coords. + MotionCVMain::clamp_scan(w, + h, + &block_x1, + &block_y1, + &block_x2, + &block_y2, + &scan_x1, + &scan_y1, + &scan_x2, + &scan_y2, + 0); +// printf("MotionCVScan::scan_frame 1\n block_x1=%d block_y1=%d block_x2=%d block_y2=%d\n scan_x1=%d scan_y1=%d scan_x2=%d scan_y2=%d\n x_result=%d y_result=%d\n", +// block_x1, +// block_y1, +// block_x2, +// block_y2, +// scan_x1, +// scan_y1, +// scan_x2, +// scan_y2, +// x_result, +// y_result); + + +// Give up if invalid coords. + if(scan_y2 <= scan_y1 || + scan_x2 <= scan_x1 || + block_x2 <= block_x1 || + block_y2 <= block_y1) + break; + +// For subpixel, the top row and left column are skipped + if(subpixel) + { + if(plugin->config.horizontal_only || + plugin->config.vertical_only) + { + total_pixels = 4 * OVERSAMPLE * OVERSAMPLE - 4 * OVERSAMPLE; + } + else + { + total_pixels = 4 * OVERSAMPLE; + } + + total_steps = total_pixels; + + set_package_count(total_steps); + process_packages(); + +// Get least difference + int64_t min_difference = -1; + for(int i = 0; i < get_total_packages(); i++) + { + MotionCVScanPackage *pkg = (MotionCVScanPackage*)get_package(i); + if(pkg->difference1 < min_difference || min_difference == -1) + { + min_difference = pkg->difference1; + + if(plugin->config.vertical_only) + x_result = scan_x1 * OVERSAMPLE; + else + x_result = scan_x1 * OVERSAMPLE + + (pkg->pixel % (OVERSAMPLE * 2 - 1)) + 1; + + if(plugin->config.horizontal_only) + y_result = scan_y1 * OVERSAMPLE; + else + y_result = scan_y1 * OVERSAMPLE + + (pkg->pixel / (OVERSAMPLE * 2 - 1)) + 1; + + +// Fill in results + dx_result = block_x1 * OVERSAMPLE - x_result; + dy_result = block_y1 * OVERSAMPLE - y_result; + } + + if(pkg->difference2 < min_difference) + { + min_difference = pkg->difference2; + + if(plugin->config.vertical_only) + x_result = scan_x1 * OVERSAMPLE; + else + x_result = scan_x2 * OVERSAMPLE - + ((pkg->pixel % (OVERSAMPLE * 2 - 1)) + 1); + + if(plugin->config.horizontal_only) + y_result = scan_y1 * OVERSAMPLE; + else + y_result = scan_y2 * OVERSAMPLE - + ((pkg->pixel / (OVERSAMPLE * 2 - 1)) + 1); + + dx_result = block_x1 * OVERSAMPLE - x_result; + dy_result = block_y1 * OVERSAMPLE - y_result; + } + } + +//printf("MotionCVScan::scan_frame 1 %d %d %d %d\n", block_x1, block_y1, x_result, y_result); + break; + } + else + { + total_pixels = (scan_x2 - scan_x1) * (scan_y2 - scan_y1); + total_steps = MIN(plugin->config.global_positions, total_pixels); + + set_package_count(total_steps); + process_packages(); + +// Get least difference + int64_t min_difference = -1; + for(int i = 0; i < get_total_packages(); i++) + { + MotionCVScanPackage *pkg = (MotionCVScanPackage*)get_package(i); + if(pkg->difference1 < min_difference || min_difference == -1) + { + min_difference = pkg->difference1; + x_result = scan_x1 + (pkg->pixel % (scan_x2 - scan_x1)); + y_result = scan_y1 + (pkg->pixel / (scan_x2 - scan_x1)); + x_result *= OVERSAMPLE; + y_result *= OVERSAMPLE; + } + } + +// printf("MotionCVScan::scan_frame 10 total_steps=%d total_pixels=%d subpixel=%d\n", +// total_steps, +// total_pixels, +// subpixel); +// +// printf(" scan w=%d h=%d scan x1=%d y1=%d x2=%d y2=%d\n", +// scan_w, +// scan_h, +// scan_x1, +// scan_y1, +// scan_x2, +// scan_y2); +// +// printf("MotionCVScan::scan_frame 2 block x1=%d y1=%d x2=%d y2=%d result x=%.2f y=%.2f\n", +// block_x1, +// block_y1, +// block_x2, +// block_y2, +// (float)x_result / 4, +// (float)y_result / 4); + + +// If a new search is required, rescale results back to pixels. + if(total_steps >= total_pixels) + { +// Single pixel accuracy reached. Now do exhaustive subpixel search. + if(plugin->config.mode1 == MotionCVConfig::STABILIZE || + plugin->config.mode1 == MotionCVConfig::TRACK || + plugin->config.mode1 == MotionCVConfig::NOTHING) + { + x_result /= OVERSAMPLE; + y_result /= OVERSAMPLE; + scan_w = 2; + scan_h = 2; + subpixel = 1; + } + else + { +// Fill in results and quit + dx_result = block_x1 * OVERSAMPLE - x_result; + dy_result = block_y1 * OVERSAMPLE - y_result; + break; + } + } + else +// Reduce scan area and try again + { + scan_w = (scan_x2 - scan_x1) / 2; + scan_h = (scan_y2 - scan_y1) / 2; + x_result /= OVERSAMPLE; + y_result /= OVERSAMPLE; + } + } + } + + dx_result *= -1; + dy_result *= -1; + + // Add offsets from the "tracked single frame" + if (plugin->config.addtrackedframeoffset) { + int tf_dx_result, tf_dy_result; + char string[BCTEXTLEN]; + sprintf(string, "%s%06jd", MOTION_FILE, plugin->config.track_frame); + FILE *input = fopen(string, "r"); + if(input) + { + fscanf(input, + "%d %d", + &tf_dx_result, + &tf_dy_result); + dx_result += tf_dx_result; + dy_result += tf_dy_result; + fclose(input); + } + } + + } + + + + + + +// Write results + if(plugin->config.mode2 == MotionCVConfig::SAVE) + { + char string[BCTEXTLEN]; + sprintf(string, + "%s%06jd", + MOTION_FILE, + plugin->get_source_position()); + FILE *output = fopen(string, "w"); + if(output) + { + fprintf(output, + "%d %d\n", + dx_result, + dy_result); + fclose(output); + } + else + { + perror("MotionCVScan::scan_frame SAVE 1"); + } + } + +#ifdef DEBUG +printf("MotionCVScan::scan_frame 10 dx=%.2f dy=%.2f\n", +(float)this->dx_result / OVERSAMPLE, +(float)this->dy_result / OVERSAMPLE); +#endif +} + + + + + + + + + + + + + + + + + +int64_t MotionCVScan::get_cache(int x, int y) +{ + int64_t result = -1; + cache_lock->lock("MotionCVScan::get_cache"); + for(int i = 0; i < cache.total; i++) + { + MotionCVScanCache *ptr = cache.values[i]; + if(ptr->x == x && ptr->y == y) + { + result = ptr->difference; + break; + } + } + cache_lock->unlock(); + return result; +} + +void MotionCVScan::put_cache(int x, int y, int64_t difference) +{ + MotionCVScanCache *ptr = new MotionCVScanCache(x, y, difference); + cache_lock->lock("MotionCVScan::put_cache"); + cache.append(ptr); + cache_lock->unlock(); +} + + + + + +MotionCVScanCache::MotionCVScanCache(int x, int y, int64_t difference) +{ + this->x = x; + this->y = y; + this->difference = difference; +} + + + + + + + + + + + + + + +RotateCVScanPackage::RotateCVScanPackage() +{ +} + + +RotateCVScanUnit::RotateCVScanUnit(RotateCVScan *server, MotionCVMain *plugin) + : LoadClient(server) +{ + this->server = server; + this->plugin = plugin; + rotater = 0; + temp = 0; +} + +RotateCVScanUnit::~RotateCVScanUnit() +{ + delete rotater; + delete temp; +} + +void RotateCVScanUnit::process_package(LoadPackage *package) +{ + if(server->skip) return; + RotateCVScanPackage *pkg = (RotateCVScanPackage*)package; + + if((pkg->difference = server->get_cache(pkg->angle)) < 0) + { +//printf("RotateCVScanUnit::process_package 1\n"); + int color_model = server->previous_frame->get_color_model(); + int pixel_size = BC_CModels::calculate_pixelsize(color_model); + int row_bytes = server->previous_frame->get_bytes_per_line(); + + if(!rotater) + rotater = new AffineEngine(1, 1); + if(!temp) temp = new VFrame( + server->previous_frame->get_w(), + server->previous_frame->get_h(), + color_model); + + +// RotateCV original block size + rotater->set_viewport(server->block_x1, + server->block_y1, + server->block_x2 - server->block_x1, + server->block_y2 - server->block_y1); + rotater->set_pivot(server->block_x, server->block_y); +//pkg->angle = 2; + rotater->rotate(temp, + server->previous_frame, + pkg->angle); +// Clamp coordinates + int x1 = server->scan_x; + int y1 = server->scan_y; + int x2 = x1 + server->scan_w; + int y2 = y1 + server->scan_h; + x2 = MIN(temp->get_w(), x2); + y2 = MIN(temp->get_h(), y2); + x2 = MIN(server->current_frame->get_w(), x2); + y2 = MIN(server->current_frame->get_h(), y2); + x1 = MAX(0, x1); + y1 = MAX(0, y1); + + if(x2 > x1 && y2 > y1) + { + pkg->difference = plugin->abs_diff( + temp->get_rows()[y1] + x1 * pixel_size, + server->current_frame->get_rows()[y1] + x1 * pixel_size, + row_bytes, + x2 - x1, + y2 - y1, + color_model); +//printf("RotateCVScanUnit::process_package %d\n", __LINE__); + server->put_cache(pkg->angle, pkg->difference); + } + +// printf("RotateCVScanUnit::process_package 10 x=%d y=%d w=%d h=%d block_x=%d block_y=%d angle=%f scan_w=%d scan_h=%d diff=%lld\n", +// server->block_x1, +// server->block_y1, +// server->block_x2 - server->block_x1, +// server->block_y2 - server->block_y1, +// server->block_x, +// server->block_y, +// pkg->angle, +// server->scan_w, +// server->scan_h, +// pkg->difference); + } +} + + + + + + + + + + + + + + + + + + + + + + +RotateCVScan::RotateCVScan(MotionCVMain *plugin, + int total_clients, + int total_packages) + : LoadServer( +//1, 1 +total_clients, total_packages +) +{ + this->plugin = plugin; + cache_lock = new Mutex("RotateCVScan::cache_lock"); +} + + +RotateCVScan::~RotateCVScan() +{ + delete cache_lock; +} + +void RotateCVScan::init_packages() +{ + for(int i = 0; i < get_total_packages(); i++) + { + RotateCVScanPackage *pkg = (RotateCVScanPackage*)get_package(i); + pkg->angle = i * + (scan_angle2 - scan_angle1) / + (total_steps - 1) + + scan_angle1; + } +} + +LoadClient* RotateCVScan::new_client() +{ + return new RotateCVScanUnit(this, plugin); +} + +LoadPackage* RotateCVScan::new_package() +{ + return new RotateCVScanPackage; +} + + +float RotateCVScan::scan_frame(VFrame *previous_frame, + VFrame *current_frame, + int block_x, + int block_y) +{ + skip = 0; + this->block_x = block_x; + this->block_y = block_y; + + switch(plugin->config.mode2) + { + case MotionCVConfig::NO_CALCULATE: + result = 0; + skip = 1; + break; + + case MotionCVConfig::LOAD: + { + char string[BCTEXTLEN]; + sprintf(string, "%s%06jd", ROTATION_FILE, plugin->get_source_position()); + FILE *input = fopen(string, "r"); + if(input) + { + fscanf(input, "%f", &result); + fclose(input); + skip = 1; + } + else + { + perror("RotateCVScan::scan_frame LOAD"); + } + break; + } + } + + + + + + + + + this->previous_frame = previous_frame; + this->current_frame = current_frame; + int w = current_frame->get_w(); + int h = current_frame->get_h(); + int block_w = w * plugin->config.rotation_block_w / 100; + int block_h = h * plugin->config.rotation_block_h / 100; + + if(this->block_x - block_w / 2 < 0) block_w = this->block_x * 2; + if(this->block_y - block_h / 2 < 0) block_h = this->block_y * 2; + if(this->block_x + block_w / 2 > w) block_w = (w - this->block_x) * 2; + if(this->block_y + block_h / 2 > h) block_h = (h - this->block_y) * 2; + + block_x1 = this->block_x - block_w / 2; + block_x2 = this->block_x + block_w / 2; + block_y1 = this->block_y - block_h / 2; + block_y2 = this->block_y + block_h / 2; + + +// Calculate the maximum area available to scan after rotation. +// Must be calculated from the starting range because of cache. +// Get coords of rectangle after rotation. + double center_x = this->block_x; + double center_y = this->block_y; + double max_angle = plugin->config.rotation_range; + double base_angle1 = atan((float)block_h / block_w); + double base_angle2 = atan((float)block_w / block_h); + double target_angle1 = base_angle1 + max_angle * 2 * M_PI / 360; + double target_angle2 = base_angle2 + max_angle * 2 * M_PI / 360; + double radius = sqrt(block_w * block_w + block_h * block_h) / 2; + double x1 = center_x - cos(target_angle1) * radius; + double y1 = center_y - sin(target_angle1) * radius; + double x2 = center_x + sin(target_angle2) * radius; + double y2 = center_y - cos(target_angle2) * radius; + double x3 = center_x - sin(target_angle2) * radius; + double y3 = center_y + cos(target_angle2) * radius; + +// Track top edge to find greatest area. + double max_area1 = 0; + //double max_x1 = 0; + double max_y1 = 0; + for(double x = x1; x < x2; x++) + { + double y = y1 + (y2 - y1) * (x - x1) / (x2 - x1); + if(x >= center_x && x < block_x2 && y >= block_y1 && y < center_y) + { + double area = fabs(x - center_x) * fabs(y - center_y); + if(area > max_area1) + { + max_area1 = area; + //max_x1 = x; + max_y1 = y; + } + } + } + +// Track left edge to find greatest area. + double max_area2 = 0; + double max_x2 = 0; + //double max_y2 = 0; + for(double y = y1; y < y3; y++) + { + double x = x1 + (x3 - x1) * (y - y1) / (y3 - y1); + if(x >= block_x1 && x < center_x && y >= block_y1 && y < center_y) + { + double area = fabs(x - center_x) * fabs(y - center_y); + if(area > max_area2) + { + max_area2 = area; + max_x2 = x; + //max_y2 = y; + } + } + } + + double max_x, max_y; + max_x = max_x2; + max_y = max_y1; + +// Get reduced scan coords + scan_w = (int)(fabs(max_x - center_x) * 2); + scan_h = (int)(fabs(max_y - center_y) * 2); + scan_x = (int)(center_x - scan_w / 2); + scan_y = (int)(center_y - scan_h / 2); +// printf("RotateCVScan::scan_frame center=%d,%d scan=%d,%d %dx%d\n", +// this->block_x, this->block_y, scan_x, scan_y, scan_w, scan_h); +// printf(" angle_range=%f block= %d,%d,%d,%d\n", max_angle, block_x1, block_y1, block_x2, block_y2); + +// Determine min angle from size of block + double angle1 = atan((double)block_h / block_w); + double angle2 = atan((double)(block_h - 1) / (block_w + 1)); + double min_angle = fabs(angle2 - angle1) / OVERSAMPLE; + min_angle = MAX(min_angle, MIN_ANGLE); + +#ifdef DEBUG +printf("RotateCVScan::scan_frame min_angle=%f\n", min_angle * 360 / 2 / M_PI); +#endif + + cache.remove_all_objects(); + if(!skip) + { +// Initial search range + float angle_range = (float)plugin->config.rotation_range; + result = 0; + total_steps = plugin->config.rotate_positions; + + + while(angle_range >= min_angle * total_steps) + { + scan_angle1 = result - angle_range; + scan_angle2 = result + angle_range; + + + set_package_count(total_steps); +//set_package_count(1); + process_packages(); + + int64_t min_difference = -1; + for(int i = 0; i < get_total_packages(); i++) + { + RotateCVScanPackage *pkg = (RotateCVScanPackage*)get_package(i); + if(pkg->difference < min_difference || min_difference == -1) + { + min_difference = pkg->difference; + result = pkg->angle; + } +//break; + } + + angle_range /= 2; + +//break; + } + } + + + if(!skip && plugin->config.mode2 == MotionCVConfig::SAVE) + { + char string[BCTEXTLEN]; + sprintf(string, + "%s%06jd", + ROTATION_FILE, + plugin->get_source_position()); + FILE *output = fopen(string, "w"); + if(output) + { + fprintf(output, "%f\n", result); + fclose(output); + } + else + { + perror("RotateCVScan::scan_frame SAVE"); + } + } + +#ifdef DEBUG +printf("RotateCVScan::scan_frame 10 angle=%f\n", result); +#endif + + + + return result; +} + +int64_t RotateCVScan::get_cache(float angle) +{ + int64_t result = -1; + cache_lock->lock("RotateCVScan::get_cache"); + for(int i = 0; i < cache.total; i++) + { + RotateCVScanCache *ptr = cache.values[i]; + if(fabs(ptr->angle - angle) <= MIN_ANGLE) + { + result = ptr->difference; + break; + } + } + cache_lock->unlock(); + return result; +} + +void RotateCVScan::put_cache(float angle, int64_t difference) +{ + RotateCVScanCache *ptr = new RotateCVScanCache(angle, difference); + cache_lock->lock("RotateCVScan::put_cache"); + cache.append(ptr); + cache_lock->unlock(); +} + + + + + + + + + +RotateCVScanCache::RotateCVScanCache(float angle, int64_t difference) +{ + this->angle = angle; + this->difference = difference; +} + + + diff --git a/cinelerra-5.1/plugins/motion-cv/motion-cv.h b/cinelerra-5.1/plugins/motion-cv/motion-cv.h new file mode 100644 index 00000000..c8788a02 --- /dev/null +++ b/cinelerra-5.1/plugins/motion-cv/motion-cv.h @@ -0,0 +1,494 @@ + +/* + * CINELERRA + * Copyright (C) 2008 Adam Williams + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef MOTION_H +#define MOTION_H + +#include +#include +#include + +#include "affine.inc" +#include "bchash.inc" +#include "filexml.inc" +#include "keyframe.inc" +#include "loadbalance.h" +#include "overlayframe.inc" +#include "pluginvclient.h" +#include "rotateframe.inc" +#include "vframe.inc" + +class MotionCVMain; +class MotionCVWindow; +class MotionCVScan; +class RotateCVScan; + + +#define OVERSAMPLE 4 + + +// Limits of global range in percent +#define MIN_RADIUS 1 +#define MAX_RADIUS 50 + +// Limits of rotation range in degrees +#define MIN_ROTATION 1 +#define MAX_ROTATION 25 + +// Limits of block size in percent. +#define MIN_BLOCK 1 +#define MAX_BLOCK 100 + +// Limits of block count +#define MIN_BLOCKS 1 +#define MAX_BLOCKS 200 + +// Precision of rotation +#define MIN_ANGLE 0.0001 + +#define TRACKING_FILE "/tmp/motion" + +class MotionCVConfig +{ +public: + MotionCVConfig(); + + int equivalent(MotionCVConfig &that); + void copy_from(MotionCVConfig &that); + void interpolate(MotionCVConfig &prev, MotionCVConfig &next, + int64_t prev_frame, int64_t next_frame, int64_t current_frame); + void boundaries(); + + int block_count; + int global_range_w; + int global_range_h; + int rotation_range; + int magnitude; + int return_speed; + int draw_vectors; +// Percent of image size + int global_block_w; + int global_block_h; + int rotation_block_w; + int rotation_block_h; +// Number of search positions in each refinement of the log search + int global_positions; + int rotate_positions; +// Block position in percentage 0 - 100 + double block_x; + double block_y; + + int horizontal_only; + int vertical_only; + int global; + int rotate; + int addtrackedframeoffset; + char tracking_file[BCTEXTLEN]; +// Track or stabilize, single pixel, scan only, or nothing + int mode1; +// Recalculate, no calculate, save, or load coordinates from disk + int mode2; +// Track a single frame, previous frame, or previous frame same block + int mode3; + enum + { +// mode1 + TRACK, + STABILIZE, + TRACK_PIXEL, + STABILIZE_PIXEL, + NOTHING, +// mode2 + RECALCULATE, + SAVE, + LOAD, + NO_CALCULATE, +// mode3 + TRACK_SINGLE, + TRACK_PREVIOUS, + PREVIOUS_SAME_BLOCK + }; +// Number of single frame to track relative to timeline start + int64_t track_frame; +// Master layer + int bottom_is_master; +}; + + + + +class MotionCVMain : public PluginVClient +{ +public: + MotionCVMain(PluginServer *server); + ~MotionCVMain(); + + int process_buffer(VFrame **frame, + int64_t start_position, + double frame_rate); + void process_global(); + void process_rotation(); + void draw_vectors(VFrame *frame); + int is_multichannel(); + int is_realtime(); + void save_data(KeyFrame *keyframe); + void read_data(KeyFrame *keyframe); + void update_gui(); +// Calculate frame to copy from and frame to move + void calculate_pointers(VFrame **frame, VFrame **src, VFrame **dst); + void allocate_temp(int w, int h, int color_model); + + PLUGIN_CLASS_MEMBERS2(MotionCVConfig) + + + int64_t abs_diff(unsigned char *prev_ptr, + unsigned char *current_ptr, + int row_bytes, + int w, + int h, + int color_model); + int64_t abs_diff_sub(unsigned char *prev_ptr, + unsigned char *current_ptr, + int row_bytes, + int w, + int h, + int color_model, + int sub_x, + int sub_y); + + static void clamp_scan(int w, + int h, + int *block_x1, + int *block_y1, + int *block_x2, + int *block_y2, + int *scan_x1, + int *scan_y1, + int *scan_x2, + int *scan_y2, + int use_absolute); + static void draw_pixel(VFrame *frame, int x, int y); + static void draw_line(VFrame *frame, int x1, int y1, int x2, int y2); + void draw_arrow(VFrame *frame, int x1, int y1, int x2, int y2); + +// Number of the previous reference frame on the timeline. + int64_t previous_frame_number; +// The frame compared with the previous frame to get the motion. +// It is moved to compensate for motion and copied to the previous_frame. + VFrame *temp_frame; + MotionCVScan *engine; + RotateCVScan *motion_rotate; + OverlayFrame *overlayer; + AffineEngine *rotate_engine; + +// Accumulation of all global tracks since the plugin start. +// Multiplied by OVERSAMPLE. + int total_dx; + int total_dy; + +// Rotation motion tracking + float total_angle; + +// Current motion vector for drawing vectors + int current_dx; + int current_dy; + float current_angle; + + FILE *active_fp; + char active_file[BCTEXTLEN]; + int get_line_key(const char *filename, int64_t key, char *line, int len); +// add constant frame offset values + int dx_offset, dy_offset; + int64_t tracking_frame; +// save/load result values + int save_dx, load_dx; + int save_dy, load_dy; + float save_dt, load_dt; + +// Oversampled current frame for motion estimation + int32_t *search_area; + int search_size; + + +// The layer to track motion in. + int reference_layer; +// The layer to apply motion in. + int target_layer; + +// Pointer to the source and destination of each operation. +// These are fully allocated buffers. + +// The previous reference frame for global motion tracking + VFrame *prev_global_ref; +// The current reference frame for global motion tracking + VFrame *current_global_ref; +// The input target frame for global motion tracking + VFrame *global_target_src; +// The output target frame for global motion tracking + VFrame *global_target_dst; + +// The previous reference frame for rotation tracking + VFrame *prev_rotate_ref; +// The current reference frame for rotation tracking + VFrame *current_rotate_ref; +// The input target frame for rotation tracking. + VFrame *rotate_target_src; +// The output target frame for rotation tracking. + VFrame *rotate_target_dst; + +// The output of process_buffer + VFrame *output_frame; + int w; + int h; +}; + + + + + + + + + + + + + + + + + + + + + + + + + +class MotionCVScanPackage : public LoadPackage +{ +public: + MotionCVScanPackage(); + +// For multiple blocks + int block_x1, block_y1, block_x2, block_y2; + int scan_x1, scan_y1, scan_x2, scan_y2; + int dx; + int dy; + int64_t max_difference; + int64_t min_difference; + int64_t min_pixel; + int is_border; + int valid; +// For single block + int pixel; + int64_t difference1; + int64_t difference2; +}; + +class MotionCVScanCache +{ +public: + MotionCVScanCache(int x, int y, int64_t difference); + int x, y; + int64_t difference; +}; + +class MotionCVScanUnit : public LoadClient +{ +public: + MotionCVScanUnit(MotionCVScan *server, MotionCVMain *plugin); + ~MotionCVScanUnit(); + + void process_package(LoadPackage *package); + int64_t get_cache(int x, int y); + void put_cache(int x, int y, int64_t difference); + + MotionCVScan *server; + MotionCVMain *plugin; + + ArrayList cache; + Mutex *cache_lock; +}; + +class MotionCVScan : public LoadServer +{ +public: + MotionCVScan(MotionCVMain *plugin, + int total_clients, + int total_packages); + ~MotionCVScan(); + + friend class MotionCVScanUnit; + + void init_packages(); + LoadClient* new_client(); + LoadPackage* new_package(); + +// Invoke the motion engine for a search +// Frame before motion + void scan_frame(VFrame *previous_frame, +// Frame after motion + VFrame *current_frame); + int64_t get_cache(int x, int y); + void put_cache(int x, int y, int64_t difference); + +// Change between previous frame and current frame multiplied by +// OVERSAMPLE + int dx_result; + int dy_result; + +private: + VFrame *previous_frame; +// Frame after motion + VFrame *current_frame; + MotionCVMain *plugin; + + int skip; +// For single block + int block_x1; + int block_x2; + int block_y1; + int block_y2; + int scan_x1; + int scan_y1; + int scan_x2; + int scan_y2; + int total_pixels; + int total_steps; + int subpixel; + + + ArrayList cache; + Mutex *cache_lock; +}; + + + + + + + + + + + + + +class RotateCVScanPackage : public LoadPackage +{ +public: + RotateCVScanPackage(); + float angle; + int64_t difference; +}; + +class RotateCVScanCache +{ +public: + RotateCVScanCache(float angle, int64_t difference); + float angle; + int64_t difference; +}; + +class RotateCVScanUnit : public LoadClient +{ +public: + RotateCVScanUnit(RotateCVScan *server, MotionCVMain *plugin); + ~RotateCVScanUnit(); + + void process_package(LoadPackage *package); + + RotateCVScan *server; + MotionCVMain *plugin; + AffineEngine *rotater; + VFrame *temp; +}; + +class RotateCVScan : public LoadServer +{ +public: + RotateCVScan(MotionCVMain *plugin, + int total_clients, + int total_packages); + ~RotateCVScan(); + + friend class RotateCVScanUnit; + + void init_packages(); + LoadClient* new_client(); + LoadPackage* new_package(); + +// Invoke the motion engine for a search +// Frame before rotation + float scan_frame(VFrame *previous_frame, +// Frame after rotation + VFrame *current_frame, +// Pivot + int block_x, + int block_y); + int64_t get_cache(float angle); + void put_cache(float angle, int64_t difference); + + +// Angle result + float result; + +private: + VFrame *previous_frame; +// Frame after motion + VFrame *current_frame; + + MotionCVMain *plugin; + int skip; + +// Pivot + int block_x; + int block_y; +// Block to rotate + int block_x1; + int block_x2; + int block_y1; + int block_y2; +// Area to compare + int scan_x; + int scan_y; + int scan_w; + int scan_h; +// Range of angles to compare + float scan_angle1, scan_angle2; + int total_steps; + + ArrayList cache; + Mutex *cache_lock; +}; + + + + +#endif + + + + + + diff --git a/cinelerra-5.1/plugins/motion-cv/motionwindow-cv.C b/cinelerra-5.1/plugins/motion-cv/motionwindow-cv.C new file mode 100644 index 00000000..e0e2b109 --- /dev/null +++ b/cinelerra-5.1/plugins/motion-cv/motionwindow-cv.C @@ -0,0 +1,1023 @@ + +/* + * CINELERRA + * Copyright (C) 2008 Adam Williams + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "bcdisplayinfo.h" +#include "clip.h" +#include "language.h" +#include "motion-cv.h" +#include "motionwindow-cv.h" + + + + + + + + + +MotionCVWindow::MotionCVWindow(MotionCVMain *plugin) + : PluginClientWindow(plugin, 815, 650, 815, 650, 0) +{ + this->plugin = plugin; +} + +MotionCVWindow::~MotionCVWindow() +{ +} + +void MotionCVWindow::create_objects() +{ + int x1 = 10, x = 10, y = 10; + int x2 = 410; + BC_Title *title; + + + + add_subwindow(global = new MotionCVGlobal(plugin, + this, + x1, + y)); + + add_subwindow(rotate = new MotionCVRotate(plugin, + this, + x2, + y)); + y += 50; + + add_subwindow(title = new BC_Title(x1, + y, + _("Translation search radius:\n(W/H Percent of image)"))); + add_subwindow(global_range_w = new GlobalRange(plugin, + x1 + title->get_w() + 10, + y, + &plugin->config.global_range_w)); + add_subwindow(global_range_h = new GlobalRange(plugin, + x1 + title->get_w() + 30 + global_range_w->get_w(), + y, + &plugin->config.global_range_h)); + + add_subwindow(title = new BC_Title(x2, + y, + _("Rotation search radius:\n(Degrees)"))); + add_subwindow(rotation_range = new RotationRange(plugin, + x2 + title->get_w() + 10, + y)); + + y += 50; + add_subwindow(title = new BC_Title(x1, + y, + _("Translation block size:\n(W/H Percent of image)"))); + add_subwindow(global_block_w = new BlockSize(plugin, + x1 + title->get_w() + 10, + y, + &plugin->config.global_block_w)); + add_subwindow(global_block_h = new BlockSize(plugin, + x1 + title->get_w() + 30 + global_block_w->get_w(), + y, + &plugin->config.global_block_h)); + + add_subwindow(title = new BC_Title(x2, + y, + _("Rotation block size:\n(W/H Percent of image)"))); + add_subwindow(rotation_block_w = new BlockSize(plugin, + x2 + title->get_w() + 10, + y, + &plugin->config.rotation_block_w)); + add_subwindow(rotation_block_h = new BlockSize(plugin, + x2 + title->get_w() + 30 + rotation_block_w->get_w(), + y, + &plugin->config.rotation_block_h)); + + y += 50; + add_subwindow(title = new BC_Title(x1, y, _("Translation search steps:"))); + add_subwindow(global_search_positions = new GlobalSearchPositions(plugin, + x1 + title->get_w() + 10, + y, + 80)); + global_search_positions->create_objects(); + + add_subwindow(title = new BC_Title(x2, y, _("Rotation search steps:"))); + add_subwindow(rotation_search_positions = new RotationSearchPositions(plugin, + x2 + title->get_w() + 10, + y, + 80)); + rotation_search_positions->create_objects(); + + y += 50; + add_subwindow(title = new BC_Title(x, y, _("Translation direction:"))); + add_subwindow(mode3 = new Mode3(plugin, + this, + x + title->get_w() + 10, + y)); + mode3->create_objects(); + + y += 40; + add_subwindow(title = new BC_Title(x, y + 10, _("Block X:"))); + add_subwindow(block_x = new MotionCVBlockX(plugin, + this, + x + title->get_w() + 10, + y)); + add_subwindow(block_x_text = new MotionCVBlockXText(plugin, + this, + x + title->get_w() + 10 + block_x->get_w() + 10, + y + 10)); + + y += 40; + add_subwindow(title = new BC_Title(x, y + 10, _("Block Y:"))); + add_subwindow(block_y = new MotionCVBlockY(plugin, + this, + x + title->get_w() + 10, + y)); + add_subwindow(block_y_text = new MotionCVBlockYText(plugin, + this, + x + title->get_w() + 10 + block_y->get_w() + 10, + y + 10)); + + y += 50; + add_subwindow(title = new BC_Title(x, y + 10, _("Maximum absolute offset:"))); + add_subwindow(magnitude = new MotionCVMagnitude(plugin, + x + title->get_w() + 10, + y)); + + y += 40; + add_subwindow(title = new BC_Title(x, y + 10, _("Settling speed:"))); + add_subwindow(return_speed = new MotionCVReturnSpeed(plugin, + x + title->get_w() + 10, + y)); + + + + y += 40; + add_subwindow(vectors = new MotionCVDrawVectors(plugin, + this, + x, + y)); + + add_subwindow(title = new BC_Title(x2, y, _("Tracking file:"))); + add_subwindow(tracking_file = new MotionCVTrackingFile(plugin, + plugin->config.tracking_file, this, x2+title->get_w() + 20, y)); + + y += 40; + add_subwindow(track_single = new TrackSingleFrame(plugin, + this, + x, + y)); + add_subwindow(title = new BC_Title(x + track_single->get_w() + 20, + y, + _("Frame number:"))); + add_subwindow(track_frame_number = new TrackFrameNumber(plugin, + this, + x + track_single->get_w() + title->get_w() + 20, + y)); + add_subwindow(addtrackedframeoffset = new AddTrackedFrameOffset(plugin, + this, + x + track_single->get_w() + title->get_w() + 30, + y + track_single->get_h())); + + + y += 20; + add_subwindow(track_previous = new TrackPreviousFrame(plugin, + this, + x, + y)); + + y += 20; + add_subwindow(previous_same = new PreviousFrameSameBlock(plugin, + this, + x, + y)); + + y += 40; + //int y1 = y; + add_subwindow(title = new BC_Title(x, y, _("Master layer:"))); + add_subwindow(master_layer = new MasterLayer(plugin, + this, + x + title->get_w() + 10, + y)); + master_layer->create_objects(); + y += 30; + + + add_subwindow(title = new BC_Title(x, y, _("Action:"))); + add_subwindow(mode1 = new Mode1(plugin, + this, + x + title->get_w() + 10, + y)); + mode1->create_objects(); + y += 30; + + + + + add_subwindow(title = new BC_Title(x, y, _("Calculation:"))); + add_subwindow(mode2 = new Mode2(plugin, + this, + x + title->get_w() + 10, + y)); + mode2->create_objects(); + + + + show_window(1); +} + +void MotionCVWindow::update_mode() +{ + global_range_w->update(plugin->config.global_range_w, + MIN_RADIUS, + MAX_RADIUS); + global_range_h->update(plugin->config.global_range_h, + MIN_RADIUS, + MAX_RADIUS); + rotation_range->update(plugin->config.rotation_range, + MIN_ROTATION, + MAX_ROTATION); + vectors->update(plugin->config.draw_vectors); + tracking_file->update(plugin->config.tracking_file); + global->update(plugin->config.global); + rotate->update(plugin->config.rotate); + addtrackedframeoffset->update(plugin->config.addtrackedframeoffset); +} + + + + + + + + + + + + +GlobalRange::GlobalRange(MotionCVMain *plugin, + int x, + int y, + int *value) + : BC_IPot(x, + y, + (int64_t)*value, + (int64_t)MIN_RADIUS, + (int64_t)MAX_RADIUS) +{ + this->plugin = plugin; + this->value = value; +} + + +int GlobalRange::handle_event() +{ + *value = (int)get_value(); + plugin->send_configure_change(); + return 1; +} + + + + +RotationRange::RotationRange(MotionCVMain *plugin, + int x, + int y) + : BC_IPot(x, + y, + (int64_t)plugin->config.rotation_range, + (int64_t)MIN_ROTATION, + (int64_t)MAX_ROTATION) +{ + this->plugin = plugin; +} + + +int RotationRange::handle_event() +{ + plugin->config.rotation_range = (int)get_value(); + plugin->send_configure_change(); + return 1; +} + + + + + + + + +BlockSize::BlockSize(MotionCVMain *plugin, + int x, + int y, + int *value) + : BC_IPot(x, + y, + (int64_t)*value, + (int64_t)MIN_BLOCK, + (int64_t)MAX_BLOCK) +{ + this->plugin = plugin; + this->value = value; +} + + +int BlockSize::handle_event() +{ + *value = (int)get_value(); + plugin->send_configure_change(); + return 1; +} + + + + + + + + + + + + + +GlobalSearchPositions::GlobalSearchPositions(MotionCVMain *plugin, + int x, + int y, + int w) + : BC_PopupMenu(x, + y, + w, + "", + 1) +{ + this->plugin = plugin; +} +void GlobalSearchPositions::create_objects() +{ + add_item(new BC_MenuItem("64")); + add_item(new BC_MenuItem("128")); + add_item(new BC_MenuItem("256")); + add_item(new BC_MenuItem("512")); + add_item(new BC_MenuItem("1024")); + add_item(new BC_MenuItem("2048")); + add_item(new BC_MenuItem("4096")); + add_item(new BC_MenuItem("8192")); + add_item(new BC_MenuItem("16384")); + add_item(new BC_MenuItem("32768")); + add_item(new BC_MenuItem("65536")); + add_item(new BC_MenuItem("131072")); + char string[BCTEXTLEN]; + sprintf(string, "%d", plugin->config.global_positions); + set_text(string); +} + +int GlobalSearchPositions::handle_event() +{ + plugin->config.global_positions = atoi(get_text()); + plugin->send_configure_change(); + return 1; +} + + + + + + + +RotationSearchPositions::RotationSearchPositions(MotionCVMain *plugin, + int x, + int y, + int w) + : BC_PopupMenu(x, + y, + w, + "", + 1) +{ + this->plugin = plugin; +} +void RotationSearchPositions::create_objects() +{ + add_item(new BC_MenuItem("4")); + add_item(new BC_MenuItem("8")); + add_item(new BC_MenuItem("16")); + add_item(new BC_MenuItem("32")); + char string[BCTEXTLEN]; + sprintf(string, "%d", plugin->config.rotate_positions); + set_text(string); +} + +int RotationSearchPositions::handle_event() +{ + plugin->config.rotate_positions = atoi(get_text()); + plugin->send_configure_change(); + return 1; +} + + + + + + + + +MotionCVMagnitude::MotionCVMagnitude(MotionCVMain *plugin, + int x, + int y) + : BC_IPot(x, + y, + (int64_t)plugin->config.magnitude, + (int64_t)0, + (int64_t)100) +{ + this->plugin = plugin; +} + +int MotionCVMagnitude::handle_event() +{ + plugin->config.magnitude = (int)get_value(); + plugin->send_configure_change(); + return 1; +} + + +MotionCVReturnSpeed::MotionCVReturnSpeed(MotionCVMain *plugin, + int x, + int y) + : BC_IPot(x, + y, + (int64_t)plugin->config.return_speed, + (int64_t)0, + (int64_t)100) +{ + this->plugin = plugin; +} + +int MotionCVReturnSpeed::handle_event() +{ + plugin->config.return_speed = (int)get_value(); + plugin->send_configure_change(); + return 1; +} + + + +AddTrackedFrameOffset::AddTrackedFrameOffset(MotionCVMain *plugin, + MotionCVWindow *gui, + int x, + int y) + : BC_CheckBox(x, + y, + plugin->config.addtrackedframeoffset, + _("Add (loaded) offset from tracked frame")) +{ + this->plugin = plugin; + this->gui = gui; +} + +int AddTrackedFrameOffset::handle_event() +{ + plugin->config.addtrackedframeoffset = get_value(); + plugin->send_configure_change(); + return 1; +} + +MotionCVTrackingFile::MotionCVTrackingFile(MotionCVMain *plugin, + const char *filename, MotionCVWindow *gui, int x, int y) + : BC_TextBox(x, y, 250, 1, filename) +{ + this->plugin = plugin; + this->gui = gui; +}; + +int MotionCVTrackingFile::handle_event() +{ + strcpy(plugin->config.tracking_file, get_text()); + plugin->send_configure_change(); + return 1; +} + +MotionCVGlobal::MotionCVGlobal(MotionCVMain *plugin, + MotionCVWindow *gui, + int x, + int y) + : BC_CheckBox(x, + y, + plugin->config.global, + _("Track translation")) +{ + this->plugin = plugin; + this->gui = gui; +} + +int MotionCVGlobal::handle_event() +{ + plugin->config.global = get_value(); + plugin->send_configure_change(); + return 1; +} + +MotionCVRotate::MotionCVRotate(MotionCVMain *plugin, + MotionCVWindow *gui, + int x, + int y) + : BC_CheckBox(x, + y, + plugin->config.rotate, + _("Track rotation")) +{ + this->plugin = plugin; + this->gui = gui; +} + +int MotionCVRotate::handle_event() +{ + plugin->config.rotate = get_value(); + plugin->send_configure_change(); + return 1; +} + + + + + +MotionCVBlockX::MotionCVBlockX(MotionCVMain *plugin, + MotionCVWindow *gui, + int x, + int y) + : BC_FPot(x, + y, + plugin->config.block_x, + (float)0, + (float)100) +{ + this->plugin = plugin; + this->gui = gui; +} + +int MotionCVBlockX::handle_event() +{ + plugin->config.block_x = get_value(); + gui->block_x_text->update((float)plugin->config.block_x); + plugin->send_configure_change(); + return 1; +} + + + + +MotionCVBlockY::MotionCVBlockY(MotionCVMain *plugin, + MotionCVWindow *gui, + int x, + int y) + : BC_FPot(x, + y, + (float)plugin->config.block_y, + (float)0, + (float)100) +{ + this->plugin = plugin; + this->gui = gui; +} + +int MotionCVBlockY::handle_event() +{ + plugin->config.block_y = get_value(); + gui->block_y_text->update((float)plugin->config.block_y); + plugin->send_configure_change(); + return 1; +} + +MotionCVBlockXText::MotionCVBlockXText(MotionCVMain *plugin, + MotionCVWindow *gui, + int x, + int y) + : BC_TextBox(x, + y, + 75, + 1, + (float)plugin->config.block_x) +{ + this->plugin = plugin; + this->gui = gui; + set_precision(4); +} + +int MotionCVBlockXText::handle_event() +{ + plugin->config.block_x = atof(get_text()); + gui->block_x->update(plugin->config.block_x); + plugin->send_configure_change(); + return 1; +} + + + + +MotionCVBlockYText::MotionCVBlockYText(MotionCVMain *plugin, + MotionCVWindow *gui, + int x, + int y) + : BC_TextBox(x, + y, + 75, + 1, + (float)plugin->config.block_y) +{ + this->plugin = plugin; + this->gui = gui; + set_precision(4); +} + +int MotionCVBlockYText::handle_event() +{ + plugin->config.block_y = atof(get_text()); + gui->block_y->update(plugin->config.block_y); + plugin->send_configure_change(); + return 1; +} + + + + + + + + + + + + + + + + +MotionCVDrawVectors::MotionCVDrawVectors(MotionCVMain *plugin, + MotionCVWindow *gui, + int x, + int y) + : BC_CheckBox(x, + y, + plugin->config.draw_vectors, + _("Draw vectors")) +{ + this->gui = gui; + this->plugin = plugin; +} + +int MotionCVDrawVectors::handle_event() +{ + plugin->config.draw_vectors = get_value(); + plugin->send_configure_change(); + return 1; +} + + + + + + + + +TrackSingleFrame::TrackSingleFrame(MotionCVMain *plugin, + MotionCVWindow *gui, + int x, + int y) + : BC_Radial(x, + y, + plugin->config.mode3 == MotionCVConfig::TRACK_SINGLE, + _("Track single frame")) +{ + this->plugin = plugin; + this->gui = gui; +} + +int TrackSingleFrame::handle_event() +{ + plugin->config.mode3 = MotionCVConfig::TRACK_SINGLE; + gui->track_previous->update(0); + gui->previous_same->update(0); + gui->track_frame_number->enable(); + plugin->send_configure_change(); + return 1; +} + + + + + + + + +TrackFrameNumber::TrackFrameNumber(MotionCVMain *plugin, + MotionCVWindow *gui, + int x, + int y) + : BC_TextBox(x, y, 100, 1, plugin->config.track_frame) +{ + this->plugin = plugin; + this->gui = gui; + if(plugin->config.mode3 != MotionCVConfig::TRACK_SINGLE) disable(); +} + +int TrackFrameNumber::handle_event() +{ + plugin->config.track_frame = atol(get_text()); + plugin->send_configure_change(); + return 1; +} + + + + + + + +TrackPreviousFrame::TrackPreviousFrame(MotionCVMain *plugin, + MotionCVWindow *gui, + int x, + int y) + : BC_Radial(x, + y, + plugin->config.mode3 == MotionCVConfig::TRACK_PREVIOUS, + _("Track previous frame")) +{ + this->plugin = plugin; + this->gui = gui; +} +int TrackPreviousFrame::handle_event() +{ + plugin->config.mode3 = MotionCVConfig::TRACK_PREVIOUS; + gui->track_single->update(0); + gui->previous_same->update(0); + gui->track_frame_number->disable(); + plugin->send_configure_change(); + return 1; +} + + + + + + + + +PreviousFrameSameBlock::PreviousFrameSameBlock(MotionCVMain *plugin, + MotionCVWindow *gui, + int x, + int y) + : BC_Radial(x, + y, + plugin->config.mode3 == MotionCVConfig::PREVIOUS_SAME_BLOCK, + _("Previous frame same block")) +{ + this->plugin = plugin; + this->gui = gui; +} +int PreviousFrameSameBlock::handle_event() +{ + plugin->config.mode3 = MotionCVConfig::PREVIOUS_SAME_BLOCK; + gui->track_single->update(0); + gui->track_previous->update(0); + gui->track_frame_number->disable(); + plugin->send_configure_change(); + return 1; +} + + + + + + + + +MasterLayer::MasterLayer(MotionCVMain *plugin, MotionCVWindow *gui, int x, int y) + : BC_PopupMenu(x, y, calculate_w(gui), + to_text(plugin->config.bottom_is_master)) +{ + this->plugin = plugin; + this->gui = gui; +} + +int MasterLayer::handle_event() +{ + plugin->config.bottom_is_master = from_text(get_text()); + plugin->send_configure_change(); + return 1; +} + +void MasterLayer::create_objects() +{ + add_item(new BC_MenuItem(to_text(0))); + add_item(new BC_MenuItem(to_text(1))); +} + +int MasterLayer::from_text(char *text) +{ + if(!strcmp(text, _("Top"))) return 0; + return 1; +} + +const char* MasterLayer::to_text(int mode) +{ + return mode ? _("Bottom") : _("Top"); +} + +int MasterLayer::calculate_w(MotionCVWindow *gui) +{ + int result = 0; + result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(0))); + result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(1))); + return result + 50; +} + + + + + + + + +Mode1::Mode1(MotionCVMain *plugin, MotionCVWindow *gui, int x, int y) + : BC_PopupMenu(x, y, calculate_w(gui), + to_text(plugin->config.mode1)) +{ + this->plugin = plugin; + this->gui = gui; +} + +int Mode1::handle_event() +{ + plugin->config.mode1 = from_text(get_text()); + plugin->send_configure_change(); + return 1; +} + +void Mode1::create_objects() +{ + add_item(new BC_MenuItem(to_text(MotionCVConfig::TRACK))); + add_item(new BC_MenuItem(to_text(MotionCVConfig::TRACK_PIXEL))); + add_item(new BC_MenuItem(to_text(MotionCVConfig::STABILIZE))); + add_item(new BC_MenuItem(to_text(MotionCVConfig::STABILIZE_PIXEL))); + add_item(new BC_MenuItem(to_text(MotionCVConfig::NOTHING))); +} + +int Mode1::from_text(char *text) +{ + if(!strcmp(text, _("Track Subpixel"))) return MotionCVConfig::TRACK; + if(!strcmp(text, _("Track Pixel"))) return MotionCVConfig::TRACK_PIXEL; + if(!strcmp(text, _("Stabilize Subpixel"))) return MotionCVConfig::STABILIZE; + if(!strcmp(text, _("Stabilize Pixel"))) return MotionCVConfig::STABILIZE_PIXEL; + //if(!strcmp(text, _("Do Nothing"))) return MotionCVConfig::NOTHING; + return MotionCVConfig::NOTHING; +} + +const char* Mode1::to_text(int mode) +{ + switch(mode) { + case MotionCVConfig::TRACK: return _("Track Subpixel"); + case MotionCVConfig::TRACK_PIXEL: return _("Track Pixel"); + case MotionCVConfig::STABILIZE: return _("Stabilize Subpixel"); + case MotionCVConfig::STABILIZE_PIXEL: return _("Stabilize Pixel"); + case MotionCVConfig::NOTHING: return _("Do Nothing"); + } + return ""; +} + +int Mode1::calculate_w(MotionCVWindow *gui) +{ + int result = 0; + result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(MotionCVConfig::TRACK))); + result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(MotionCVConfig::TRACK_PIXEL))); + result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(MotionCVConfig::STABILIZE))); + result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(MotionCVConfig::STABILIZE_PIXEL))); + result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(MotionCVConfig::NOTHING))); + return result + 50; +} + + + + + +Mode2::Mode2(MotionCVMain *plugin, MotionCVWindow *gui, int x, int y) + : BC_PopupMenu(x, y, calculate_w(gui), + to_text(plugin->config.mode2)) +{ + this->plugin = plugin; + this->gui = gui; +} + +int Mode2::handle_event() +{ + plugin->config.mode2 = from_text(get_text()); + plugin->send_configure_change(); + return 1; +} + +void Mode2::create_objects() +{ + add_item(new BC_MenuItem(to_text(MotionCVConfig::NO_CALCULATE))); + add_item(new BC_MenuItem(to_text(MotionCVConfig::RECALCULATE))); + add_item(new BC_MenuItem(to_text(MotionCVConfig::SAVE))); + add_item(new BC_MenuItem(to_text(MotionCVConfig::LOAD))); +} + +int Mode2::from_text(char *text) +{ + if(!strcmp(text, _("Recalculate"))) return MotionCVConfig::RECALCULATE; + if(!strcmp(text, _("Save coords to tracking file"))) return MotionCVConfig::SAVE; + if(!strcmp(text, _("Load coords from tracking file"))) return MotionCVConfig::LOAD; + //if(!strcmp(text, _("Don't Calculate"))) return MotionCVConfig::NO_CALCULATE; + return MotionCVConfig::NO_CALCULATE; +} + +const char* Mode2::to_text(int mode) +{ + switch(mode) { + case MotionCVConfig::NO_CALCULATE: return _("Don't Calculate"); + case MotionCVConfig::RECALCULATE: return _("Recalculate"); + case MotionCVConfig::SAVE: return _("Save coords to tracking file"); + case MotionCVConfig::LOAD: return _("Load coords from tracking file"); + } + return ""; +} + +int Mode2::calculate_w(MotionCVWindow *gui) +{ + int result = 0; + result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(MotionCVConfig::NO_CALCULATE))); + result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(MotionCVConfig::RECALCULATE))); + result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(MotionCVConfig::SAVE))); + result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(MotionCVConfig::LOAD))); + return result + 50; +} + + + + + + + + + + +Mode3::Mode3(MotionCVMain *plugin, MotionCVWindow *gui, int x, int y) + : BC_PopupMenu(x, y, calculate_w(gui), + to_text(plugin->config.horizontal_only, plugin->config.vertical_only)) +{ + this->plugin = plugin; + this->gui = gui; +} + +int Mode3::handle_event() +{ + from_text(&plugin->config.horizontal_only, &plugin->config.vertical_only, get_text()); + plugin->send_configure_change(); + return 1; +} + +void Mode3::create_objects() +{ + add_item(new BC_MenuItem(to_text(1, 0))); + add_item(new BC_MenuItem(to_text(0, 1))); + add_item(new BC_MenuItem(to_text(0, 0))); +} + +void Mode3::from_text(int *horizontal_only, int *vertical_only, char *text) +{ + *horizontal_only = 0; + *vertical_only = 0; + if(!strcmp(text, to_text(1, 0))) *horizontal_only = 1; + if(!strcmp(text, to_text(0, 1))) *vertical_only = 1; +} + +const char* Mode3::to_text(int horizontal_only, int vertical_only) +{ + if(horizontal_only) return _("Horizontal only"); + if(vertical_only) return _("Vertical only"); + return _("Both"); +} + +int Mode3::calculate_w(MotionCVWindow *gui) +{ + int result = 0; + result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(1, 0))); + result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(0, 1))); + result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(0, 0))); + return result + 50; +} + diff --git a/cinelerra-5.1/plugins/motion-cv/motionwindow-cv.h b/cinelerra-5.1/plugins/motion-cv/motionwindow-cv.h new file mode 100644 index 00000000..c3cd5f8d --- /dev/null +++ b/cinelerra-5.1/plugins/motion-cv/motionwindow-cv.h @@ -0,0 +1,360 @@ + +/* + * CINELERRA + * Copyright (C) 2008 Adam Williams + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "guicast.h" + +class MotionCVMain; +class MotionCVWindow; +class MotionCVScan; +class RotateCVScan; + +class MasterLayer : public BC_PopupMenu +{ +public: + MasterLayer(MotionCVMain *plugin, MotionCVWindow *gui, int x, int y); + int handle_event(); + void create_objects(); + static int calculate_w(MotionCVWindow *gui); + static int from_text(char *text); + static const char* to_text(int mode); + MotionCVMain *plugin; + MotionCVWindow *gui; +}; + +class Mode1 : public BC_PopupMenu +{ +public: + Mode1(MotionCVMain *plugin, MotionCVWindow *gui, int x, int y); + int handle_event(); + void create_objects(); + static int calculate_w(MotionCVWindow *gui); + static int from_text(char *text); + static const char* to_text(int mode); + MotionCVMain *plugin; + MotionCVWindow *gui; +}; + +class Mode2 : public BC_PopupMenu +{ +public: + Mode2(MotionCVMain *plugin, MotionCVWindow *gui, int x, int y); + int handle_event(); + void create_objects(); + static int calculate_w(MotionCVWindow *gui); + static int from_text(char *text); + static const char* to_text(int mode); + MotionCVMain *plugin; + MotionCVWindow *gui; +}; + +class Mode3 : public BC_PopupMenu +{ +public: + Mode3(MotionCVMain *plugin, MotionCVWindow *gui, int x, int y); + int handle_event(); + void create_objects(); + static int calculate_w(MotionCVWindow *gui); + static void from_text(int *horizontal_only, int *vertical_only, char *text); + static const char* to_text(int horizontal_only, int vertical_only); + MotionCVMain *plugin; + MotionCVWindow *gui; +}; + + +class TrackSingleFrame : public BC_Radial +{ +public: + TrackSingleFrame(MotionCVMain *plugin, + MotionCVWindow *gui, + int x, + int y); + int handle_event(); + MotionCVMain *plugin; + MotionCVWindow *gui; +}; + +class TrackFrameNumber : public BC_TextBox +{ +public: + TrackFrameNumber(MotionCVMain *plugin, + MotionCVWindow *gui, + int x, + int y); + int handle_event(); + MotionCVMain *plugin; + MotionCVWindow *gui; +}; + +class TrackPreviousFrame : public BC_Radial +{ +public: + TrackPreviousFrame(MotionCVMain *plugin, + MotionCVWindow *gui, + int x, + int y); + int handle_event(); + MotionCVMain *plugin; + MotionCVWindow *gui; +}; + +class PreviousFrameSameBlock : public BC_Radial +{ +public: + PreviousFrameSameBlock(MotionCVMain *plugin, + MotionCVWindow *gui, + int x, + int y); + int handle_event(); + MotionCVMain *plugin; + MotionCVWindow *gui; +}; + +class GlobalRange : public BC_IPot +{ +public: + GlobalRange(MotionCVMain *plugin, + int x, + int y, + int *value); + int handle_event(); + MotionCVMain *plugin; + int *value; +}; + +class RotationRange : public BC_IPot +{ +public: + RotationRange(MotionCVMain *plugin, + int x, + int y); + int handle_event(); + MotionCVMain *plugin; +}; + +class BlockSize : public BC_IPot +{ +public: + BlockSize(MotionCVMain *plugin, + int x, + int y, + int *value); + int handle_event(); + MotionCVMain *plugin; + int *value; +}; + +class MotionCVBlockX : public BC_FPot +{ +public: + MotionCVBlockX(MotionCVMain *plugin, + MotionCVWindow *gui, + int x, + int y); + int handle_event(); + MotionCVWindow *gui; + MotionCVMain *plugin; +}; + +class MotionCVBlockY : public BC_FPot +{ +public: + MotionCVBlockY(MotionCVMain *plugin, + MotionCVWindow *gui, + int x, + int y); + int handle_event(); + MotionCVWindow *gui; + MotionCVMain *plugin; +}; + +class MotionCVBlockXText : public BC_TextBox +{ +public: + MotionCVBlockXText(MotionCVMain *plugin, + MotionCVWindow *gui, + int x, + int y); + int handle_event(); + MotionCVWindow *gui; + MotionCVMain *plugin; +}; + +class MotionCVBlockYText : public BC_TextBox +{ +public: + MotionCVBlockYText(MotionCVMain *plugin, + MotionCVWindow *gui, + int x, + int y); + int handle_event(); + MotionCVWindow *gui; + MotionCVMain *plugin; +}; + +class GlobalSearchPositions : public BC_PopupMenu +{ +public: + GlobalSearchPositions(MotionCVMain *plugin, + int x, + int y, + int w); + void create_objects(); + int handle_event(); + MotionCVMain *plugin; +}; + +class RotationSearchPositions : public BC_PopupMenu +{ +public: + RotationSearchPositions(MotionCVMain *plugin, + int x, + int y, + int w); + void create_objects(); + int handle_event(); + MotionCVMain *plugin; +}; + +class MotionCVMagnitude : public BC_IPot +{ +public: + MotionCVMagnitude(MotionCVMain *plugin, + int x, + int y); + int handle_event(); + MotionCVMain *plugin; +}; + +class MotionCVReturnSpeed : public BC_IPot +{ +public: + MotionCVReturnSpeed(MotionCVMain *plugin, + int x, + int y); + int handle_event(); + MotionCVMain *plugin; +}; + + + +class MotionCVDrawVectors : public BC_CheckBox +{ +public: + MotionCVDrawVectors(MotionCVMain *plugin, + MotionCVWindow *gui, + int x, + int y); + int handle_event(); + MotionCVMain *plugin; + MotionCVWindow *gui; +}; + +class AddTrackedFrameOffset : public BC_CheckBox +{ +public: + AddTrackedFrameOffset(MotionCVMain *plugin, + MotionCVWindow *gui, + int x, + int y); + int handle_event(); + MotionCVWindow *gui; + MotionCVMain *plugin; +}; + +class MotionCVTrackingFile : public BC_TextBox +{ +public: + MotionCVTrackingFile(MotionCVMain *plugin, const char *filename, + MotionCVWindow *gui, int x, int y); + int handle_event(); + MotionCVMain *plugin; + MotionCVWindow *gui; +}; + +class MotionCVGlobal : public BC_CheckBox +{ +public: + MotionCVGlobal(MotionCVMain *plugin, + MotionCVWindow *gui, + int x, + int y); + int handle_event(); + MotionCVWindow *gui; + MotionCVMain *plugin; +}; + +class MotionCVRotate : public BC_CheckBox +{ +public: + MotionCVRotate(MotionCVMain *plugin, + MotionCVWindow *gui, + int x, + int y); + int handle_event(); + MotionCVWindow *gui; + MotionCVMain *plugin; +}; + + + +class MotionCVWindow : public PluginClientWindow +{ +public: + MotionCVWindow(MotionCVMain *plugin); + ~MotionCVWindow(); + + void create_objects(); + void update_mode(); + char* get_radius_title(); + + GlobalRange *global_range_w; + GlobalRange *global_range_h; + RotationRange *rotation_range; + BlockSize *global_block_w; + BlockSize *global_block_h; + BlockSize *rotation_block_w; + BlockSize *rotation_block_h; + MotionCVBlockX *block_x; + MotionCVBlockY *block_y; + MotionCVBlockXText *block_x_text; + MotionCVBlockYText *block_y_text; + GlobalSearchPositions *global_search_positions; + RotationSearchPositions *rotation_search_positions; + MotionCVMagnitude *magnitude; + MotionCVReturnSpeed *return_speed; + Mode1 *mode1; + MotionCVDrawVectors *vectors; + MotionCVTrackingFile *tracking_file; + MotionCVGlobal *global; + MotionCVRotate *rotate; + AddTrackedFrameOffset *addtrackedframeoffset; + TrackSingleFrame *track_single; + TrackFrameNumber *track_frame_number; + TrackPreviousFrame *track_previous; + PreviousFrameSameBlock *previous_same; + MasterLayer *master_layer; + Mode2 *mode2; + Mode3 *mode3; + + MotionCVMain *plugin; +}; + + diff --git a/cinelerra-5.1/plugins/motion-cv/picon.png b/cinelerra-5.1/plugins/motion-cv/picon.png new file mode 100644 index 0000000000000000000000000000000000000000..a854b23ef9d5baa558c791ef9be58502926a8ea3 GIT binary patch literal 2301 zcmVWFU8GbZ8({Xk{QrNlj2~u$KS;00>=4L_t(&-tAa> zOjKtU|J{4%&M?m0x$}O2!$SldA6VZO2%{LcU2V~uW1UHS*BYa}`bM}v`11JJwO?H#{B`5d0 z_nhxMzI^BV&bdGwal{cv9C5@EM;vjC1Ll9lku_`9OdT8?%nXG>`r+Rj8ylxuES3i@ zm+PNzB#`3b;;ai7E^H14gGfzHb-3MbrADK%vMig})6+9yJ<(_sgM)+MI8KN}BHU;) zdcD4_tE(&VjRaz~TL0+rc>Z8Eo5AxOQd51<>9jB!+aQxMU?!-U2oOaLOeQ%}Qv9&n zdq?mqD+L@n)Q6QTS1zrrtgI<4Ec}h%@7K6ot`o!MWRne~y}ezf)oS5(cj4Bp6f`w; zqp`6cJw42*>$PZ+4e!6-3LrcR|M3_Lroy2^eWn5qs*N4Hu!SR9y5g`(ZOv*+<0Sk`fa&O$Yp=fAms1Aq2 zXlQ7#i=v1?Ab_HxBK4?&?Ao0%sF=K zm^K^^YogI8h-g&hy}eRWIvfeuw=Wr9@4!nPF&HAq%8Ei1A5SWfx8Dwdh)`EoH+SdG zoqwsRsqyyr_b-|;W5!7U5Jhn|0|3jGEi0|AuKsIc;uOrEPe@PK!)on-Lcv3=7DiR> z^+wRzI*6~n>ITEm8@*$QWH6gGU>NROuh&~_v)LYcJf1sANlE)!T3UWmtJT|a{rYvr zZnxi=Hcdx zn608BJtZb4x&a_5Dd{1@F#ni3b!sNVFeC^<8P={{JDcZu`t(!FD+Lh>f!=xN<);)8 zXzf~QeE9H-^G}|XczgFw%-+6zA}JJ#;K73jjdr{Jp;oKCT2N4s$#ERXM}tXAtanayT%GðMk50-@pHbR zL3*xUJ+|DIEkJoWaQZax+0*;?*fHr@QUVY$xO~}+y1I4%47^@7T3h|d&Q3x}i5D7; zbQ&6skCB$9MkJzM(9_dXJ1{VS%F0Uke7>Lje!m)Kv-yM|uoMchBUWHwU<#c+t)+?z zP(=kp6%`DfIb$Zb+dxE8VgsO#jq%FN8nQ>j$d8jYqNN~Q7y0IIFcj7@m-D1qk9Gmfg< zU=ZlsxoPy)ThjZ96XS1-_ui9auU!Lu^NlnvDFJ=_aq_sOjb5*&mKG_P4I31sQmMR4 zmo8oSG+%b*=jZRp%E~$lo6Yt;0J?vFHW7iMQ8^V9n8#EulgVh+DuDoyAb^e@1-ac) zvfsQpl-+`ZQRI{TrT(K=jSKN<#L)bWlDK$ z4<{!l*8-qBcQS|w)X2!{0h^$sUKK~rZ&IS?^0F??@whXv< z@%f?=*t{7C28WWozu!Nih+ME>0n*dcp;D=knwpAv^XA2PckfC?X=|$#=adGkYv3^7*lesyLn{O~kr$c@HP(NWb8llx{WADqCFURfMx8e8uV|~Wny};hR z3-|8bi}CvV2k^lMk6<>NTXS=Bzde8c{EMxE zLZK)N27_OZW{l@~l$DjiWHNz>P*qh0m&+A%fam#;!C;8+JRkLVJa?5!C4#}={ZJ?r z(rUFxN=k}t)6vk`+4*iqM@QDM?E#fag}%PN5%c77d9%~${OzSnm#SZ?th~HDNvqYa z6Gd^ID2nT>R_nRpNz1Y<84LyzMe&7%b~qfRuT#~oTD7WZBHd+LTH4MMWvE^@Faku&|)1spvc&je_U-zCa+*XtUYca&vRf zoIQK?=FizY?b)+OdG+emoKPrK==1r~&1Q46!{PYk)TvVg@&BlC#1Tgval{eF|0(_p X8QqE-Pc3wU00000NkvXXu0mjfmN8Ji literal 0 HcmV?d00001 diff --git a/cinelerra-5.1/plugins/motion-hv/Makefile b/cinelerra-5.1/plugins/motion-hv/Makefile new file mode 100644 index 00000000..695fd5ee --- /dev/null +++ b/cinelerra-5.1/plugins/motion-hv/Makefile @@ -0,0 +1,14 @@ +include ../../plugin_defs + +OBJS := \ + $(OBJDIR)/motion-hv.o \ + $(OBJDIR)/motionscan-hv.o \ + $(OBJDIR)/motionwindow-hv.o + +PLUGIN = motion-hv + +include ../../plugin_config + +$(OBJDIR)/motion-hv.o: motion-hv.C +$(OBJDIR)/motionscan-hv.o: motionscan-hv.C +$(OBJDIR)/motionwindow-hv.o: motionwindow-hv.C diff --git a/cinelerra-5.1/plugins/motion.new/motion.C b/cinelerra-5.1/plugins/motion-hv/motion-hv.C similarity index 60% rename from cinelerra-5.1/plugins/motion.new/motion.C rename to cinelerra-5.1/plugins/motion-hv/motion-hv.C index 81d9705b..3b0b128a 100644 --- a/cinelerra-5.1/plugins/motion.new/motion.C +++ b/cinelerra-5.1/plugins/motion-hv/motion-hv.C @@ -1,7 +1,7 @@ /* * CINELERRA - * Copyright (C) 2008 Adam Williams + * Copyright (C) 2016 Adam Williams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,9 +27,9 @@ #include "filexml.h" #include "keyframe.h" #include "language.h" -#include "motion.h" -#include "motionscan.h" -#include "motionwindow.h" +#include "motion-hv.h" +#include "motionscan-hv.h" +#include "motionwindow-hv.h" #include "mutex.h" #include "overlayframe.h" #include "rotateframe.h" @@ -39,7 +39,7 @@ #include #include -REGISTER_PLUGIN(MotionMain) +REGISTER_PLUGIN(MotionHVMain) #undef DEBUG @@ -49,7 +49,7 @@ REGISTER_PLUGIN(MotionMain) -MotionConfig::MotionConfig() +MotionHVConfig::MotionHVConfig() { global_range_w = 5; global_range_h = 5; @@ -58,29 +58,29 @@ MotionConfig::MotionConfig() block_count = 1; global_block_w = MIN_BLOCK; global_block_h = MIN_BLOCK; - rotation_block_w = MIN_BLOCK; - rotation_block_h = MIN_BLOCK; +// rotation_block_w = MIN_BLOCK; +// rotation_block_h = MIN_BLOCK; block_x = 50; block_y = 50; - global_positions = 256; - rotate_positions = 4; +// global_positions = 256; +// rotate_positions = 4; magnitude = 100; rotate_magnitude = 90; return_speed = 0; rotate_return_speed = 0; - action_type = MotionScan::STABILIZE; - global = 1; + action_type = MotionHVScan::STABILIZE; +// global = 1; rotate = 1; - tracking_type = MotionScan::NO_CALCULATE; + tracking_type = MotionHVScan::NO_CALCULATE; draw_vectors = 1; - tracking_object = MotionScan::TRACK_SINGLE; + tracking_object = MotionHVScan::TRACK_SINGLE; track_frame = 0; bottom_is_master = 1; horizontal_only = 0; vertical_only = 0; } -void MotionConfig::boundaries() +void MotionHVConfig::boundaries() { CLAMP(global_range_w, MIN_RADIUS, MAX_RADIUS); CLAMP(global_range_h, MIN_RADIUS, MAX_RADIUS); @@ -89,29 +89,29 @@ void MotionConfig::boundaries() CLAMP(block_count, MIN_BLOCKS, MAX_BLOCKS); CLAMP(global_block_w, MIN_BLOCK, MAX_BLOCK); CLAMP(global_block_h, MIN_BLOCK, MAX_BLOCK); - CLAMP(rotation_block_w, MIN_BLOCK, MAX_BLOCK); - CLAMP(rotation_block_h, MIN_BLOCK, MAX_BLOCK); +// CLAMP(rotation_block_w, MIN_BLOCK, MAX_BLOCK); +// CLAMP(rotation_block_h, MIN_BLOCK, MAX_BLOCK); } -int MotionConfig::equivalent(MotionConfig &that) +int MotionHVConfig::equivalent(MotionHVConfig &that) { return global_range_w == that.global_range_w && global_range_h == that.global_range_h && rotation_range == that.rotation_range && rotation_center == that.rotation_center && action_type == that.action_type && - global == that.global && +// global == that.global && rotate == that.rotate && draw_vectors == that.draw_vectors && block_count == that.block_count && global_block_w == that.global_block_w && global_block_h == that.global_block_h && - rotation_block_w == that.rotation_block_w && - rotation_block_h == that.rotation_block_h && +// rotation_block_w == that.rotation_block_w && +// rotation_block_h == that.rotation_block_h && EQUIV(block_x, that.block_x) && EQUIV(block_y, that.block_y) && - global_positions == that.global_positions && - rotate_positions == that.rotate_positions && +// global_positions == that.global_positions && +// rotate_positions == that.rotate_positions && magnitude == that.magnitude && return_speed == that.return_speed && rotate_return_speed == that.rotate_return_speed && @@ -123,26 +123,26 @@ int MotionConfig::equivalent(MotionConfig &that) vertical_only == that.vertical_only; } -void MotionConfig::copy_from(MotionConfig &that) +void MotionHVConfig::copy_from(MotionHVConfig &that) { global_range_w = that.global_range_w; global_range_h = that.global_range_h; rotation_range = that.rotation_range; rotation_center = that.rotation_center; action_type = that.action_type; - global = that.global; +// global = that.global; rotate = that.rotate; tracking_type = that.tracking_type; draw_vectors = that.draw_vectors; block_count = that.block_count; block_x = that.block_x; block_y = that.block_y; - global_positions = that.global_positions; - rotate_positions = that.rotate_positions; +// global_positions = that.global_positions; +// rotate_positions = that.rotate_positions; global_block_w = that.global_block_w; global_block_h = that.global_block_h; - rotation_block_w = that.rotation_block_w; - rotation_block_h = that.rotation_block_h; +// rotation_block_w = that.rotation_block_w; +// rotation_block_h = that.rotation_block_h; magnitude = that.magnitude; return_speed = that.return_speed; rotate_magnitude = that.rotate_magnitude; @@ -154,14 +154,14 @@ void MotionConfig::copy_from(MotionConfig &that) vertical_only = that.vertical_only; } -void MotionConfig::interpolate(MotionConfig &prev, - MotionConfig &next, +void MotionHVConfig::interpolate(MotionHVConfig &prev, + MotionHVConfig &next, int64_t prev_frame, int64_t next_frame, int64_t current_frame) { - double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame); - double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame); + //double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame); + //double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame); this->block_x = prev.block_x; this->block_y = prev.block_y; global_range_w = prev.global_range_w; @@ -169,17 +169,17 @@ void MotionConfig::interpolate(MotionConfig &prev, rotation_range = prev.rotation_range; rotation_center = prev.rotation_center; action_type = prev.action_type; - global = prev.global; +// global = prev.global; rotate = prev.rotate; tracking_type = prev.tracking_type; draw_vectors = prev.draw_vectors; block_count = prev.block_count; - global_positions = prev.global_positions; - rotate_positions = prev.rotate_positions; +// global_positions = prev.global_positions; +// rotate_positions = prev.rotate_positions; global_block_w = prev.global_block_w; global_block_h = prev.global_block_h; - rotation_block_w = prev.rotation_block_w; - rotation_block_h = prev.rotation_block_h; +// rotation_block_w = prev.rotation_block_w; +// rotation_block_h = prev.rotation_block_h; magnitude = prev.magnitude; return_speed = prev.return_speed; rotate_magnitude = prev.rotate_magnitude; @@ -209,12 +209,12 @@ void MotionConfig::interpolate(MotionConfig &prev, -MotionMain::MotionMain(PluginServer *server) +MotionHVMain::MotionHVMain(PluginServer *server) : PluginVClient(server) { engine = 0; rotate_engine = 0; - motion_rotate = 0; +// motion_rotate = 0; total_dx = 0; total_dy = 0; total_angle = 0; @@ -235,7 +235,7 @@ MotionMain::MotionMain(PluginServer *server) rotate_target_dst = 0; } -MotionMain::~MotionMain() +MotionHVMain::~MotionHVMain() { delete engine; @@ -243,7 +243,7 @@ MotionMain::~MotionMain() delete [] search_area; delete temp_frame; delete rotate_engine; - delete motion_rotate; +// delete motion_rotate; delete prev_global_ref; @@ -257,67 +257,67 @@ MotionMain::~MotionMain() delete rotate_target_dst; } -const char* MotionMain::plugin_title() { return _("Motion"); } -int MotionMain::is_realtime() { return 1; } -int MotionMain::is_multichannel() { return 1; } +const char* MotionHVMain::plugin_title() { return _("MotionHV"); } +int MotionHVMain::is_realtime() { return 1; } +int MotionHVMain::is_multichannel() { return 1; } -NEW_WINDOW_MACRO(MotionMain, MotionWindow) +NEW_WINDOW_MACRO(MotionHVMain, MotionHVWindow) -LOAD_CONFIGURATION_MACRO(MotionMain, MotionConfig) +LOAD_CONFIGURATION_MACRO(MotionHVMain, MotionHVConfig) -void MotionMain::update_gui() +void MotionHVMain::update_gui() { if(thread) { if(load_configuration()) { - thread->window->lock_window("MotionMain::update_gui"); - - char string[BCTEXTLEN]; - sprintf(string, "%d", config.global_positions); - ((MotionWindow*)thread->window)->global_search_positions->set_text(string); - sprintf(string, "%d", config.rotate_positions); - ((MotionWindow*)thread->window)->rotation_search_positions->set_text(string); - - ((MotionWindow*)thread->window)->global_block_w->update(config.global_block_w); - ((MotionWindow*)thread->window)->global_block_h->update(config.global_block_h); - ((MotionWindow*)thread->window)->rotation_block_w->update(config.rotation_block_w); - ((MotionWindow*)thread->window)->rotation_block_h->update(config.rotation_block_h); - ((MotionWindow*)thread->window)->block_x->update(config.block_x); - ((MotionWindow*)thread->window)->block_y->update(config.block_y); - ((MotionWindow*)thread->window)->block_x_text->update((float)config.block_x); - ((MotionWindow*)thread->window)->block_y_text->update((float)config.block_y); - ((MotionWindow*)thread->window)->magnitude->update(config.magnitude); - ((MotionWindow*)thread->window)->return_speed->update(config.return_speed); - ((MotionWindow*)thread->window)->rotate_magnitude->update(config.rotate_magnitude); - ((MotionWindow*)thread->window)->rotate_return_speed->update(config.rotate_return_speed); - ((MotionWindow*)thread->window)->rotation_range->update(config.rotation_range); - ((MotionWindow*)thread->window)->rotation_center->update(config.rotation_center); - - - ((MotionWindow*)thread->window)->track_single->update(config.tracking_object == MotionScan::TRACK_SINGLE); - ((MotionWindow*)thread->window)->track_frame_number->update(config.track_frame); - ((MotionWindow*)thread->window)->track_previous->update(config.tracking_object == MotionScan::TRACK_PREVIOUS); - ((MotionWindow*)thread->window)->previous_same->update(config.tracking_object == MotionScan::PREVIOUS_SAME_BLOCK); - if(config.tracking_object != MotionScan::TRACK_SINGLE) - ((MotionWindow*)thread->window)->track_frame_number->disable(); + thread->window->lock_window("MotionHVMain::update_gui"); + +// char string[BCTEXTLEN]; +// sprintf(string, "%d", config.global_positions); +// ((MotionHVWindow*)thread->window)->global_search_positions->set_text(string); +// sprintf(string, "%d", config.rotate_positions); +// ((MotionHVWindow*)thread->window)->rotation_search_positions->set_text(string); + + ((MotionHVWindow*)thread->window)->global_block_w->update(config.global_block_w); + ((MotionHVWindow*)thread->window)->global_block_h->update(config.global_block_h); +// ((MotionHVWindow*)thread->window)->rotation_block_w->update(config.rotation_block_w); +// ((MotionHVWindow*)thread->window)->rotation_block_h->update(config.rotation_block_h); + ((MotionHVWindow*)thread->window)->block_x->update(config.block_x); + ((MotionHVWindow*)thread->window)->block_y->update(config.block_y); + ((MotionHVWindow*)thread->window)->block_x_text->update((float)config.block_x); + ((MotionHVWindow*)thread->window)->block_y_text->update((float)config.block_y); + ((MotionHVWindow*)thread->window)->magnitude->update(config.magnitude); + ((MotionHVWindow*)thread->window)->return_speed->update(config.return_speed); + ((MotionHVWindow*)thread->window)->rotate_magnitude->update(config.rotate_magnitude); + ((MotionHVWindow*)thread->window)->rotate_return_speed->update(config.rotate_return_speed); + ((MotionHVWindow*)thread->window)->rotation_range->update(config.rotation_range); + ((MotionHVWindow*)thread->window)->rotation_center->update(config.rotation_center); + + + ((MotionHVWindow*)thread->window)->track_single->update(config.tracking_object == MotionHVScan::TRACK_SINGLE); + ((MotionHVWindow*)thread->window)->track_frame_number->update(config.track_frame); + ((MotionHVWindow*)thread->window)->track_previous->update(config.tracking_object == MotionHVScan::TRACK_PREVIOUS); + ((MotionHVWindow*)thread->window)->previous_same->update(config.tracking_object == MotionHVScan::PREVIOUS_SAME_BLOCK); + if(config.tracking_object != MotionHVScan::TRACK_SINGLE) + ((MotionHVWindow*)thread->window)->track_frame_number->disable(); else - ((MotionWindow*)thread->window)->track_frame_number->enable(); + ((MotionHVWindow*)thread->window)->track_frame_number->enable(); - ((MotionWindow*)thread->window)->action_type->set_text( + ((MotionHVWindow*)thread->window)->action_type->set_text( ActionType::to_text(config.action_type)); - ((MotionWindow*)thread->window)->tracking_type->set_text( + ((MotionHVWindow*)thread->window)->tracking_type->set_text( TrackingType::to_text(config.tracking_type)); - ((MotionWindow*)thread->window)->track_direction->set_text( + ((MotionHVWindow*)thread->window)->track_direction->set_text( TrackDirection::to_text(config.horizontal_only, config.vertical_only)); - ((MotionWindow*)thread->window)->master_layer->set_text( + ((MotionHVWindow*)thread->window)->master_layer->set_text( MasterLayer::to_text(config.bottom_is_master)); - ((MotionWindow*)thread->window)->update_mode(); + ((MotionHVWindow*)thread->window)->update_mode(); thread->window->unlock_window(); } } @@ -326,7 +326,7 @@ void MotionMain::update_gui() -void MotionMain::save_data(KeyFrame *keyframe) +void MotionHVMain::save_data(KeyFrame *keyframe) { FileXML output; @@ -335,12 +335,12 @@ void MotionMain::save_data(KeyFrame *keyframe) output.tag.set_title("MOTION"); output.tag.set_property("BLOCK_COUNT", config.block_count); - output.tag.set_property("GLOBAL_POSITIONS", config.global_positions); - output.tag.set_property("ROTATE_POSITIONS", config.rotate_positions); +// output.tag.set_property("GLOBAL_POSITIONS", config.global_positions); +// output.tag.set_property("ROTATE_POSITIONS", config.rotate_positions); output.tag.set_property("GLOBAL_BLOCK_W", config.global_block_w); output.tag.set_property("GLOBAL_BLOCK_H", config.global_block_h); - output.tag.set_property("ROTATION_BLOCK_W", config.rotation_block_w); - output.tag.set_property("ROTATION_BLOCK_H", config.rotation_block_h); +// output.tag.set_property("ROTATION_BLOCK_W", config.rotation_block_w); +// output.tag.set_property("ROTATION_BLOCK_H", config.rotation_block_h); output.tag.set_property("BLOCK_X", config.block_x); output.tag.set_property("BLOCK_Y", config.block_y); output.tag.set_property("GLOBAL_RANGE_W", config.global_range_w); @@ -352,7 +352,7 @@ void MotionMain::save_data(KeyFrame *keyframe) output.tag.set_property("ROTATE_MAGNITUDE", config.rotate_magnitude); output.tag.set_property("ROTATE_RETURN_SPEED", config.rotate_return_speed); output.tag.set_property("ACTION_TYPE", config.action_type); - output.tag.set_property("GLOBAL", config.global); +// output.tag.set_property("GLOBAL", config.global); output.tag.set_property("ROTATE", config.rotate); output.tag.set_property("TRACKING_TYPE", config.tracking_type); output.tag.set_property("DRAW_VECTORS", config.draw_vectors); @@ -364,11 +364,10 @@ void MotionMain::save_data(KeyFrame *keyframe) output.append_tag(); output.tag.set_title("/MOTION"); output.append_tag(); - output.append_newline(); output.terminate_string(); } -void MotionMain::read_data(KeyFrame *keyframe) +void MotionHVMain::read_data(KeyFrame *keyframe) { FileXML input; @@ -385,12 +384,12 @@ void MotionMain::read_data(KeyFrame *keyframe) if(input.tag.title_is("MOTION")) { config.block_count = input.tag.get_property("BLOCK_COUNT", config.block_count); - config.global_positions = input.tag.get_property("GLOBAL_POSITIONS", config.global_positions); - config.rotate_positions = input.tag.get_property("ROTATE_POSITIONS", config.rotate_positions); +// config.global_positions = input.tag.get_property("GLOBAL_POSITIONS", config.global_positions); +// config.rotate_positions = input.tag.get_property("ROTATE_POSITIONS", config.rotate_positions); config.global_block_w = input.tag.get_property("GLOBAL_BLOCK_W", config.global_block_w); config.global_block_h = input.tag.get_property("GLOBAL_BLOCK_H", config.global_block_h); - config.rotation_block_w = input.tag.get_property("ROTATION_BLOCK_W", config.rotation_block_w); - config.rotation_block_h = input.tag.get_property("ROTATION_BLOCK_H", config.rotation_block_h); +// config.rotation_block_w = input.tag.get_property("ROTATION_BLOCK_W", config.rotation_block_w); +// config.rotation_block_h = input.tag.get_property("ROTATION_BLOCK_H", config.rotation_block_h); config.block_x = input.tag.get_property("BLOCK_X", config.block_x); config.block_y = input.tag.get_property("BLOCK_Y", config.block_y); config.global_range_w = input.tag.get_property("GLOBAL_RANGE_W", config.global_range_w); @@ -402,7 +401,7 @@ void MotionMain::read_data(KeyFrame *keyframe) config.rotate_magnitude = input.tag.get_property("ROTATE_MAGNITUDE", config.rotate_magnitude); config.rotate_return_speed = input.tag.get_property("ROTATE_RETURN_SPEED", config.rotate_return_speed); config.action_type = input.tag.get_property("ACTION_TYPE", config.action_type); - config.global = input.tag.get_property("GLOBAL", config.global); +// config.global = input.tag.get_property("GLOBAL", config.global); config.rotate = input.tag.get_property("ROTATE", config.rotate); config.tracking_type = input.tag.get_property("TRACKING_TYPE", config.tracking_type); config.draw_vectors = input.tag.get_property("DRAW_VECTORS", config.draw_vectors); @@ -425,7 +424,7 @@ void MotionMain::read_data(KeyFrame *keyframe) -void MotionMain::allocate_temp(int w, int h, int color_model) +void MotionHVMain::allocate_temp(int w, int h, int color_model) { if(temp_frame && (temp_frame->get_w() != w || @@ -439,46 +438,54 @@ void MotionMain::allocate_temp(int w, int h, int color_model) } -void MotionMain::process_global() +void MotionHVMain::process_global() { + int w = current_global_ref->get_w(); + int h = current_global_ref->get_h(); - if(!engine) engine = new MotionScan(PluginClient::get_project_smp() + 1, + + if(!engine) engine = new MotionHVScan(PluginClient::get_project_smp() + 1, PluginClient::get_project_smp() + 1); // Determine if frames changed +// printf("MotionHVMain::process_global %d block_y=%f total_dy=%d\n", +// __LINE__, config.block_y * h / 100, total_dy); engine->scan_frame(current_global_ref, prev_global_ref, - config.global_range_w, - config.global_range_h, - config.global_block_w, - config.global_block_h, - config.block_x, - config.block_y, + config.global_range_w * w / 100, + config.global_range_h * h / 100, + config.global_block_w * w / 100, + config.global_block_h * h / 100, + config.block_x * w / 100, + config.block_y * h / 100, config.tracking_object, config.tracking_type, config.action_type, config.horizontal_only, config.vertical_only, get_source_position(), - config.global_positions, total_dx, total_dy, 0, - 0); + 0, + 1, // do_motion + config.rotate, // do_rotate + config.rotation_center, + config.rotation_range); + current_dx = engine->dx_result; current_dy = engine->dy_result; // Add current motion vector to accumulation vector. - if(config.tracking_object != MotionScan::TRACK_SINGLE) + if(config.tracking_object != MotionHVScan::TRACK_SINGLE) { // Retract over time total_dx = (int64_t)total_dx * (100 - config.return_speed) / 100; total_dy = (int64_t)total_dy * (100 - config.return_speed) / 100; total_dx += engine->dx_result; total_dy += engine->dy_result; -// printf("MotionMain::process_global total_dx=%d engine->dx_result=%d\n", -// total_dx, -// engine->dx_result); +// printf("MotionHVMain::process_global %d total_dy=%d engine->dy_result=%d\n", +// __LINE__, total_dy, engine->dy_result); } else // Make accumulation vector current @@ -490,45 +497,29 @@ void MotionMain::process_global() // Clamp accumulation vector if(config.magnitude < 100) { - int block_w = (int64_t)config.global_block_w * - current_global_ref->get_w() / 100; - int block_h = (int64_t)config.global_block_h * - current_global_ref->get_h() / 100; - int block_x_orig = (int64_t)(config.block_x * - current_global_ref->get_w() / - 100); + //int block_w = (int64_t)config.global_block_w * w / 100; + //int block_h = (int64_t)config.global_block_h * h / 100; + int block_x_orig = (int64_t)(config.block_x * w / 100); int block_y_orig = (int64_t)(config.block_y * - current_global_ref->get_h() / - 100); + current_global_ref->get_h() / h / 100); - int max_block_x = (int64_t)(current_global_ref->get_w() - block_x_orig) * - OVERSAMPLE * - config.magnitude / - 100; - int max_block_y = (int64_t)(current_global_ref->get_h() - block_y_orig) * - OVERSAMPLE * - config.magnitude / - 100; + int max_block_x = (int64_t)(w - block_x_orig) * + OVERSAMPLE * config.magnitude / 100; + int max_block_y = (int64_t)(h - block_y_orig) * + OVERSAMPLE * config.magnitude / 100; int min_block_x = (int64_t)-block_x_orig * - OVERSAMPLE * - config.magnitude / - 100; + OVERSAMPLE * config.magnitude / 100; int min_block_y = (int64_t)-block_y_orig * - OVERSAMPLE * - config.magnitude / - 100; + OVERSAMPLE * config.magnitude / 100; CLAMP(total_dx, min_block_x, max_block_x); CLAMP(total_dy, min_block_y, max_block_y); } -#ifdef DEBUG -printf("MotionMain::process_global 2 total_dx=%.02f total_dy=%.02f\n", -(float)total_dx / OVERSAMPLE, -(float)total_dy / OVERSAMPLE); -#endif +// printf("MotionHVMain::process_global %d total_dx=%d total_dy=%d\n", +// __LINE__, total_dx, total_dy); - if(config.tracking_object != MotionScan::TRACK_SINGLE && !config.rotate) + if(config.tracking_object != MotionHVScan::TRACK_SINGLE && !config.rotate) { // Transfer current reference frame to previous reference frame and update // counter. Must wait for rotate to compare. @@ -537,31 +528,30 @@ printf("MotionMain::process_global 2 total_dx=%.02f total_dy=%.02f\n", } // Decide what to do with target based on requested operation - int interpolation; - float dx; - float dy; + int interpolation = NEAREST_NEIGHBOR; + float dx = 0.; + float dy = 0.; switch(config.action_type) { - case MotionScan::NOTHING: + case MotionHVScan::NOTHING: global_target_dst->copy_from(global_target_src); break; - case MotionScan::TRACK_PIXEL: + case MotionHVScan::TRACK_PIXEL: interpolation = NEAREST_NEIGHBOR; dx = (int)(total_dx / OVERSAMPLE); dy = (int)(total_dy / OVERSAMPLE); break; - case MotionScan::STABILIZE_PIXEL: + case MotionHVScan::STABILIZE_PIXEL: interpolation = NEAREST_NEIGHBOR; dx = -(int)(total_dx / OVERSAMPLE); dy = -(int)(total_dy / OVERSAMPLE); break; - break; - case MotionScan::TRACK: + case MotionHVScan::TRACK: interpolation = CUBIC_LINEAR; dx = (float)total_dx / OVERSAMPLE; dy = (float)total_dy / OVERSAMPLE; break; - case MotionScan::STABILIZE: + case MotionHVScan::STABILIZE: interpolation = CUBIC_LINEAR; dx = -(float)total_dx / OVERSAMPLE; dy = -(float)total_dy / OVERSAMPLE; @@ -569,7 +559,7 @@ printf("MotionMain::process_global 2 total_dx=%.02f total_dy=%.02f\n", } - if(config.action_type != MotionScan::NOTHING) + if(config.action_type != MotionHVScan::NOTHING) { if(!overlayer) overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1); @@ -592,20 +582,22 @@ printf("MotionMain::process_global 2 total_dx=%.02f total_dy=%.02f\n", -void MotionMain::process_rotation() +void MotionHVMain::process_rotation() { int block_x; int block_y; +// Always require global // Convert the previous global reference into the previous rotation reference. // Convert global target destination into rotation target source. - if(config.global) +// if(config.global) + if(1) { if(!overlayer) overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1); float dx; float dy; - if(config.tracking_object == MotionScan::TRACK_SINGLE) + if(config.tracking_object == MotionHVScan::TRACK_SINGLE) { dx = (float)total_dx / OVERSAMPLE; dy = (float)total_dy / OVERSAMPLE; @@ -644,7 +636,7 @@ void MotionMain::process_rotation() // Use the global target output as the rotation target input rotate_target_src->copy_from(global_target_dst); // Transfer current reference frame to previous reference frame for global. - if(config.tracking_object != MotionScan::TRACK_SINGLE) + if(config.tracking_object != MotionHVScan::TRACK_SINGLE) { prev_global_ref->copy_from(current_global_ref); previous_frame_number = get_source_position(); @@ -664,20 +656,20 @@ void MotionMain::process_rotation() // Get rotation - if(!motion_rotate) - motion_rotate = new RotateScan(this, - get_project_smp() + 1, - get_project_smp() + 1); - - current_angle = motion_rotate->scan_frame(prev_rotate_ref, - current_rotate_ref, - block_x, - block_y); - - +// if(!motion_rotate) +// motion_rotate = new RotateScan(this, +// get_project_smp() + 1, +// get_project_smp() + 1); +// +// current_angle = motion_rotate->scan_frame(prev_rotate_ref, +// current_rotate_ref, +// block_x, +// block_y); + + current_angle = engine->dr_result; // Add current rotation to accumulation - if(config.tracking_object != MotionScan::TRACK_SINGLE) + if(config.tracking_object != MotionHVScan::TRACK_SINGLE) { // Retract over time total_angle = total_angle * (100 - config.rotate_return_speed) / 100; @@ -690,13 +682,13 @@ void MotionMain::process_rotation() CLAMP(total_angle, -config.rotate_magnitude, config.rotate_magnitude); } - if(!config.global) - { +// if(!config.global) +// { // Transfer current reference frame to previous reference frame and update // counter. - prev_rotate_ref->copy_from(current_rotate_ref); - previous_frame_number = get_source_position(); - } +// prev_rotate_ref->copy_from(current_rotate_ref); +// previous_frame_number = get_source_position(); +// } } else { @@ -704,30 +696,30 @@ void MotionMain::process_rotation() } #ifdef DEBUG -printf("MotionMain::process_rotation total_angle=%f\n", total_angle); +printf("MotionHVMain::process_rotation total_angle=%f\n", total_angle); #endif // Calculate rotation parameters based on requested operation - float angle; + float angle = 0.; switch(config.action_type) { - case MotionScan::NOTHING: + case MotionHVScan::NOTHING: rotate_target_dst->copy_from(rotate_target_src); break; - case MotionScan::TRACK: - case MotionScan::TRACK_PIXEL: + case MotionHVScan::TRACK: + case MotionHVScan::TRACK_PIXEL: angle = total_angle; break; - case MotionScan::STABILIZE: - case MotionScan::STABILIZE_PIXEL: + case MotionHVScan::STABILIZE: + case MotionHVScan::STABILIZE_PIXEL: angle = -total_angle; break; } - if(config.action_type != MotionScan::NOTHING) + if(config.action_type != MotionHVScan::NOTHING) { if(!rotate_engine) rotate_engine = new AffineEngine(PluginClient::get_project_smp() + 1, @@ -738,17 +730,18 @@ printf("MotionMain::process_rotation total_angle=%f\n", total_angle); // Determine pivot based on a number of factors. switch(config.action_type) { - case MotionScan::TRACK: - case MotionScan::TRACK_PIXEL: + case MotionHVScan::TRACK: + case MotionHVScan::TRACK_PIXEL: // Use destination of global tracking. // rotate_engine->set_pivot(block_x, block_y); rotate_engine->set_in_pivot(block_x, block_y); rotate_engine->set_out_pivot(block_x, block_y); break; - case MotionScan::STABILIZE: - case MotionScan::STABILIZE_PIXEL: - if(config.global) + case MotionHVScan::STABILIZE: + case MotionHVScan::STABILIZE_PIXEL: +// if(config.global) + if(1) { // Use origin of global stabilize operation // rotate_engine->set_pivot((int)(rotate_target_dst->get_w() * @@ -782,6 +775,7 @@ printf("MotionMain::process_rotation total_angle=%f\n", total_angle); } +printf("MotionHVMain::process_rotation angle=%f\n", angle); rotate_engine->rotate(rotate_target_dst, rotate_target_src, angle); // overlayer->overlay(rotate_target_dst, // prev_rotate_ref, @@ -824,7 +818,7 @@ printf("MotionMain::process_rotation total_angle=%f\n", total_angle); -int MotionMain::process_buffer(VFrame **frame, +int MotionHVMain::process_buffer(VFrame **frame, int64_t start_position, double frame_rate) { @@ -835,7 +829,7 @@ int MotionMain::process_buffer(VFrame **frame, #ifdef DEBUG -printf("MotionMain::process_buffer %d start_position=%lld\n", __LINE__, start_position); +printf("MotionHVMain::process_buffer %d start_position=%lld\n", __LINE__, start_position); #endif @@ -859,7 +853,7 @@ printf("MotionMain::process_buffer %d start_position=%lld\n", __LINE__, start_po int skip_current = 0; - if(config.tracking_object == MotionScan::TRACK_SINGLE) + if(config.tracking_object == MotionHVScan::TRACK_SINGLE) { actual_previous_number = config.track_frame; if(get_direction() == PLAY_REVERSE) @@ -903,7 +897,7 @@ printf("MotionMain::process_buffer %d start_position=%lld\n", __LINE__, start_po } - if(!config.global && !config.rotate) skip_current = 1; +// if(!config.global && !config.rotate) skip_current = 1; @@ -939,7 +933,8 @@ printf("MotionMain::process_buffer %d start_position=%lld\n", __LINE__, start_po // Get the global pointers. Here we walk through the sequence of events. - if(config.global) +// if(config.global) + if(1) { // Assume global only. Global reads previous frame and compares // with current frame to get the current translation. @@ -964,17 +959,20 @@ printf("MotionMain::process_buffer %d start_position=%lld\n", __LINE__, start_po read_frame(prev_global_ref, reference_layer, previous_frame_number, - frame_rate); + frame_rate, + 0); } read_frame(current_global_ref, reference_layer, start_position, - frame_rate); + frame_rate, + 0); read_frame(global_target_src, target_layer, start_position, - frame_rate); + frame_rate, + 0); @@ -1028,16 +1026,19 @@ printf("MotionMain::process_buffer %d start_position=%lld\n", __LINE__, start_po read_frame(prev_rotate_ref, reference_layer, previous_frame_number, - frame_rate); + frame_rate, + 0); } read_frame(current_rotate_ref, reference_layer, start_position, - frame_rate); + frame_rate, + 0); read_frame(rotate_target_src, target_layer, start_position, - frame_rate); + frame_rate, + 0); } @@ -1046,13 +1047,14 @@ printf("MotionMain::process_buffer %d start_position=%lld\n", __LINE__, start_po - +//PRINT_TRACE +//printf("skip_current=%d config.global=%d\n", skip_current, config.global); if(!skip_current) { // Get position change from previous frame to current frame - if(config.global) process_global(); + /* if(config.global) */ process_global(); // Get rotation change from previous frame to current frame if(config.rotate) process_rotation(); //frame[target_layer]->copy_from(prev_rotate_ref); @@ -1082,7 +1084,8 @@ printf("MotionMain::process_buffer %d start_position=%lld\n", __LINE__, start_po read_frame(frame[target_layer], target_layer, start_position, - frame_rate); + frame_rate, + 0); } if(config.draw_vectors) @@ -1091,14 +1094,14 @@ printf("MotionMain::process_buffer %d start_position=%lld\n", __LINE__, start_po } #ifdef DEBUG -printf("MotionMain::process_buffer %d\n", __LINE__); +printf("MotionHVMain::process_buffer %d\n", __LINE__); #endif return 0; } -void MotionMain::draw_vectors(VFrame *frame) +void MotionHVMain::draw_vectors(VFrame *frame) { int w = frame->get_w(); int h = frame->get_h(); @@ -1113,16 +1116,16 @@ void MotionMain::draw_vectors(VFrame *frame) int search_w, search_h; int search_x1, search_y1; int search_x2, search_y2; - int search_x3, search_y3; - int search_x4, search_y4; - if(config.global) +// always processing global +// if(config.global) + if(1) { // Get vector // Start of vector is center of previous block. // End of vector is total accumulation. - if(config.tracking_object == MotionScan::TRACK_SINGLE) + if(config.tracking_object == MotionHVScan::TRACK_SINGLE) { global_x1 = (int64_t)(config.block_x * w / @@ -1132,12 +1135,12 @@ void MotionMain::draw_vectors(VFrame *frame) 100); global_x2 = global_x1 + total_dx / OVERSAMPLE; global_y2 = global_y1 + total_dy / OVERSAMPLE; -//printf("MotionMain::draw_vectors %d %d %d %d %d %d\n", total_dx, total_dy, global_x1, global_y1, global_x2, global_y2); +//printf("MotionHVMain::draw_vectors %d %d %d %d %d %d\n", total_dx, total_dy, global_x1, global_y1, global_x2, global_y2); } else // Start of vector is center of previous block. // End of vector is current change. - if(config.tracking_object == MotionScan::PREVIOUS_SAME_BLOCK) + if(config.tracking_object == MotionHVScan::PREVIOUS_SAME_BLOCK) { global_x1 = (int64_t)(config.block_x * w / @@ -1187,7 +1190,7 @@ void MotionMain::draw_vectors(VFrame *frame) search_x2 = block_x2 + search_w / 2; search_y2 = block_y2 + search_h / 2; -// printf("MotionMain::draw_vectors %d %d %d %d %d %d %d %d %d %d %d %d\n", +// printf("MotionHVMain::draw_vectors %d %d %d %d %d %d %d %d %d %d %d %d\n", // global_x1, // global_y1, // block_w, @@ -1201,7 +1204,7 @@ void MotionMain::draw_vectors(VFrame *frame) // search_x2, // search_y2); - MotionScan::clamp_scan(w, + MotionHVScan::clamp_scan(w, h, &block_x1, &block_y1, @@ -1242,8 +1245,8 @@ void MotionMain::draw_vectors(VFrame *frame) block_y = (int64_t)(config.block_y * h / 100); } - block_w = config.rotation_block_w * w / 100; - block_h = config.rotation_block_h * h / 100; + block_w = config.global_block_w * w / 100; + block_h = config.global_block_h * h / 100; if(config.rotate) { float angle = total_angle * 2 * M_PI / 360; @@ -1278,7 +1281,7 @@ void MotionMain::draw_vectors(VFrame *frame) -void MotionMain::draw_pixel(VFrame *frame, int x, int y) +void MotionHVMain::draw_pixel(VFrame *frame, int x, int y) { if(!(x >= 0 && y >= 0 && x < frame->get_w() && y < frame->get_h())) return; @@ -1337,11 +1340,11 @@ void MotionMain::draw_pixel(VFrame *frame, int x, int y) } -void MotionMain::draw_line(VFrame *frame, int x1, int y1, int x2, int y2) +void MotionHVMain::draw_line(VFrame *frame, int x1, int y1, int x2, int y2) { int w = labs(x2 - x1); int h = labs(y2 - y1); -//printf("MotionMain::draw_line 1 %d %d %d %d\n", x1, y1, x2, y2); +//printf("MotionHVMain::draw_line 1 %d %d %d %d\n", x1, y1, x2, y2); if(!w && !h) { @@ -1388,11 +1391,11 @@ void MotionMain::draw_line(VFrame *frame, int x1, int y1, int x2, int y2) draw_pixel(frame, x, i); } } -//printf("MotionMain::draw_line 2\n"); +//printf("MotionHVMain::draw_line 2\n"); } #define ARROW_SIZE 10 -void MotionMain::draw_arrow(VFrame *frame, int x1, int y1, int x2, int y2) +void MotionHVMain::draw_arrow(VFrame *frame, int x1, int y1, int x2, int y2) { double angle = atan((float)(y2 - y1) / (float)(x2 - x1)); double angle1 = angle + (float)145 / 360 * 2 * 3.14159265; @@ -1434,442 +1437,3 @@ void MotionMain::draw_arrow(VFrame *frame, int x1, int y1, int x2, int y2) - - - - - - - - - -RotateScanPackage::RotateScanPackage() -{ -} - - -RotateScanUnit::RotateScanUnit(RotateScan *server, MotionMain *plugin) - : LoadClient(server) -{ - this->server = server; - this->plugin = plugin; - rotater = 0; - temp = 0; -} - -RotateScanUnit::~RotateScanUnit() -{ - delete rotater; - delete temp; -} - -void RotateScanUnit::process_package(LoadPackage *package) -{ - if(server->skip) return; - RotateScanPackage *pkg = (RotateScanPackage*)package; - - if((pkg->difference = server->get_cache(pkg->angle)) < 0) - { -//printf("RotateScanUnit::process_package %d\n", __LINE__); - int color_model = server->previous_frame->get_color_model(); - int pixel_size = BC_CModels::calculate_pixelsize(color_model); - int row_bytes = server->previous_frame->get_bytes_per_line(); - - if(!rotater) - rotater = new AffineEngine(1, 1); - if(!temp) temp = new VFrame(0, - -1, - server->previous_frame->get_w(), - server->previous_frame->get_h(), - color_model, - -1); -//printf("RotateScanUnit::process_package %d\n", __LINE__); - - -// Rotate original block size -// rotater->set_viewport(server->block_x1, -// server->block_y1, -// server->block_x2 - server->block_x1, -// server->block_y2 - server->block_y1); - rotater->set_in_viewport(server->block_x1, - server->block_y1, - server->block_x2 - server->block_x1, - server->block_y2 - server->block_y1); - rotater->set_out_viewport(server->block_x1, - server->block_y1, - server->block_x2 - server->block_x1, - server->block_y2 - server->block_y1); -// rotater->set_pivot(server->block_x, server->block_y); - rotater->set_in_pivot(server->block_x, server->block_y); - rotater->set_out_pivot(server->block_x, server->block_y); -//printf("RotateScanUnit::process_package %d\n", __LINE__); - rotater->rotate(temp, - server->previous_frame, - pkg->angle); - -// Scan reduced block size -//plugin->output_frame->copy_from(server->current_frame); -//plugin->output_frame->copy_from(temp); -// printf("RotateScanUnit::process_package %d %d %d %d %d\n", -// __LINE__, -// server->scan_x, -// server->scan_y, -// server->scan_w, -// server->scan_h); -// Clamp coordinates - int x1 = server->scan_x; - int y1 = server->scan_y; - int x2 = x1 + server->scan_w; - int y2 = y1 + server->scan_h; - x2 = MIN(temp->get_w(), x2); - y2 = MIN(temp->get_h(), y2); - x2 = MIN(server->current_frame->get_w(), x2); - y2 = MIN(server->current_frame->get_h(), y2); - x1 = MAX(0, x1); - y1 = MAX(0, y1); - - if(x2 > x1 && y2 > y1) - { - pkg->difference = MotionScan::abs_diff( - temp->get_rows()[y1] + x1 * pixel_size, - server->current_frame->get_rows()[y1] + x1 * pixel_size, - row_bytes, - x2 - x1, - y2 - y1, - color_model); -//printf("RotateScanUnit::process_package %d\n", __LINE__); - server->put_cache(pkg->angle, pkg->difference); - } - -// printf("RotateScanUnit::process_package 10 x=%d y=%d w=%d h=%d block_x=%d block_y=%d angle=%f scan_w=%d scan_h=%d diff=%lld\n", -// server->block_x1, -// server->block_y1, -// server->block_x2 - server->block_x1, -// server->block_y2 - server->block_y1, -// server->block_x, -// server->block_y, -// pkg->angle, -// server->scan_w, -// server->scan_h, -// pkg->difference); - } -} - - - - - - - - - - - - - - - - - - - - - - -RotateScan::RotateScan(MotionMain *plugin, - int total_clients, - int total_packages) - : LoadServer( -//1, 1 -total_clients, total_packages -) -{ - this->plugin = plugin; - cache_lock = new Mutex("RotateScan::cache_lock"); -} - - -RotateScan::~RotateScan() -{ - delete cache_lock; -} - -void RotateScan::init_packages() -{ - for(int i = 0; i < get_total_packages(); i++) - { - RotateScanPackage *pkg = (RotateScanPackage*)get_package(i); - pkg->angle = i * - (scan_angle2 - scan_angle1) / - (total_steps - 1) + - scan_angle1; - } -} - -LoadClient* RotateScan::new_client() -{ - return new RotateScanUnit(this, plugin); -} - -LoadPackage* RotateScan::new_package() -{ - return new RotateScanPackage; -} - - -float RotateScan::scan_frame(VFrame *previous_frame, - VFrame *current_frame, - int block_x, - int block_y) -{ - skip = 0; - this->block_x = block_x; - this->block_y = block_y; - -//printf("RotateScan::scan_frame %d\n", __LINE__); - switch(plugin->config.tracking_type) - { - case MotionScan::NO_CALCULATE: - result = plugin->config.rotation_center; - skip = 1; - break; - - case MotionScan::LOAD: - { - char string[BCTEXTLEN]; - sprintf(string, "%s%06d", ROTATION_FILE, plugin->get_source_position()); - FILE *input = fopen(string, "r"); - if(input) - { - fscanf(input, "%f", &result); - fclose(input); - skip = 1; - } - else - { - perror("RotateScan::scan_frame LOAD"); - } - break; - } - } - - - - - - - - - this->previous_frame = previous_frame; - this->current_frame = current_frame; - int w = current_frame->get_w(); - int h = current_frame->get_h(); - int block_w = w * plugin->config.rotation_block_w / 100; - int block_h = h * plugin->config.rotation_block_h / 100; - - if(this->block_x - block_w / 2 < 0) block_w = this->block_x * 2; - if(this->block_y - block_h / 2 < 0) block_h = this->block_y * 2; - if(this->block_x + block_w / 2 > w) block_w = (w - this->block_x) * 2; - if(this->block_y + block_h / 2 > h) block_h = (h - this->block_y) * 2; - - block_x1 = this->block_x - block_w / 2; - block_x2 = this->block_x + block_w / 2; - block_y1 = this->block_y - block_h / 2; - block_y2 = this->block_y + block_h / 2; - - -// Calculate the maximum area available to scan after rotation. -// Must be calculated from the starting range because of cache. -// Get coords of rectangle after rotation. - double center_x = this->block_x; - double center_y = this->block_y; - double max_angle = plugin->config.rotation_range; - double base_angle1 = atan((float)block_h / block_w); - double base_angle2 = atan((float)block_w / block_h); - double target_angle1 = base_angle1 + max_angle * 2 * M_PI / 360; - double target_angle2 = base_angle2 + max_angle * 2 * M_PI / 360; - double radius = sqrt(block_w * block_w + block_h * block_h) / 2; - double x1 = center_x - cos(target_angle1) * radius; - double y1 = center_y - sin(target_angle1) * radius; - double x2 = center_x + sin(target_angle2) * radius; - double y2 = center_y - cos(target_angle2) * radius; - double x3 = center_x - sin(target_angle2) * radius; - double y3 = center_y + cos(target_angle2) * radius; - -// Track top edge to find greatest area. - double max_area1 = 0; - double max_x1 = 0; - double max_y1 = 0; - for(double x = x1; x < x2; x++) - { - double y = y1 + (y2 - y1) * (x - x1) / (x2 - x1); - if(x >= center_x && x < block_x2 && y >= block_y1 && y < center_y) - { - double area = fabs(x - center_x) * fabs(y - center_y); - if(area > max_area1) - { - max_area1 = area; - max_x1 = x; - max_y1 = y; - } - } - } - -// Track left edge to find greatest area. - double max_area2 = 0; - double max_x2 = 0; - double max_y2 = 0; - for(double y = y1; y < y3; y++) - { - double x = x1 + (x3 - x1) * (y - y1) / (y3 - y1); - if(x >= block_x1 && x < center_x && y >= block_y1 && y < center_y) - { - double area = fabs(x - center_x) * fabs(y - center_y); - if(area > max_area2) - { - max_area2 = area; - max_x2 = x; - max_y2 = y; - } - } - } - - double max_x, max_y; - max_x = max_x2; - max_y = max_y1; - -// Get reduced scan coords - scan_w = (int)(fabs(max_x - center_x) * 2); - scan_h = (int)(fabs(max_y - center_y) * 2); - scan_x = (int)(center_x - scan_w / 2); - scan_y = (int)(center_y - scan_h / 2); -// printf("RotateScan::scan_frame center=%d,%d scan=%d,%d %dx%d\n", -// this->block_x, this->block_y, scan_x, scan_y, scan_w, scan_h); -// printf(" angle_range=%f block= %d,%d,%d,%d\n", max_angle, block_x1, block_y1, block_x2, block_y2); - -// Determine min angle from size of block - double angle1 = atan((double)block_h / block_w); - double angle2 = atan((double)(block_h - 1) / (block_w + 1)); - double min_angle = fabs(angle2 - angle1) / OVERSAMPLE; - min_angle = MAX(min_angle, MIN_ANGLE); - -//printf("RotateScan::scan_frame %d min_angle=%f\n", __LINE__, min_angle * 360 / 2 / M_PI); - - cache.remove_all_objects(); - - - if(!skip) - { - if(previous_frame->data_matches(current_frame)) - { -//printf("RotateScan::scan_frame: frames match. Skipping.\n"); - result = plugin->config.rotation_center; - skip = 1; - } - } - - if(!skip) - { -// Initial search range - float angle_range = max_angle; - result = plugin->config.rotation_center; - total_steps = plugin->config.rotate_positions; - - - while(angle_range >= min_angle * total_steps) - { - scan_angle1 = result - angle_range; - scan_angle2 = result + angle_range; - - - set_package_count(total_steps); -//set_package_count(1); - process_packages(); - - int64_t min_difference = -1; - for(int i = 0; i < get_total_packages(); i++) - { - RotateScanPackage *pkg = (RotateScanPackage*)get_package(i); - if(pkg->difference < min_difference || min_difference == -1) - { - min_difference = pkg->difference; - result = pkg->angle; - } -//break; - } - - angle_range /= 2; - -//break; - } - } - -//printf("RotateScan::scan_frame %d\n", __LINE__); - - if(!skip && plugin->config.tracking_type == MotionScan::SAVE) - { - char string[BCTEXTLEN]; - sprintf(string, - "%s%06d", - ROTATION_FILE, - plugin->get_source_position()); - FILE *output = fopen(string, "w"); - if(output) - { - fprintf(output, "%f\n", result); - fclose(output); - } - else - { - perror("RotateScan::scan_frame SAVE"); - } - } - -//printf("RotateScan::scan_frame %d angle=%f\n", __LINE__, result); - - - - return result; -} - -int64_t RotateScan::get_cache(float angle) -{ - int64_t result = -1; - cache_lock->lock("RotateScan::get_cache"); - for(int i = 0; i < cache.total; i++) - { - RotateScanCache *ptr = cache.values[i]; - if(fabs(ptr->angle - angle) <= MIN_ANGLE) - { - result = ptr->difference; - break; - } - } - cache_lock->unlock(); - return result; -} - -void RotateScan::put_cache(float angle, int64_t difference) -{ - RotateScanCache *ptr = new RotateScanCache(angle, difference); - cache_lock->lock("RotateScan::put_cache"); - cache.append(ptr); - cache_lock->unlock(); -} - - - - - - - - - -RotateScanCache::RotateScanCache(float angle, int64_t difference) -{ - this->angle = angle; - this->difference = difference; -} - - - diff --git a/cinelerra-5.1/plugins/motion.new/motion.h b/cinelerra-5.1/plugins/motion-hv/motion-hv.h similarity index 69% rename from cinelerra-5.1/plugins/motion.new/motion.h rename to cinelerra-5.1/plugins/motion-hv/motion-hv.h index 9ace9613..f375fb43 100644 --- a/cinelerra-5.1/plugins/motion.new/motion.h +++ b/cinelerra-5.1/plugins/motion-hv/motion-hv.h @@ -1,7 +1,7 @@ /* * CINELERRA - * Copyright (C) 2008 Adam Williams + * Copyright (C) 2016 Adam Williams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,16 +31,14 @@ #include "filexml.inc" #include "keyframe.inc" #include "loadbalance.h" -#include "motionscan.inc" -#include "motionwindow.inc" +#include "motionscan-hv.inc" +#include "motionwindow-hv.inc" #include "overlayframe.inc" #include "pluginvclient.h" -#include "rotateframe.inc" #include "vframe.inc" -class MotionMain; -class MotionWindow; -class RotateScan; +class MotionHVMain; +class MotionHVWindow; @@ -64,17 +62,16 @@ class RotateScan; // Precision of rotation #define MIN_ANGLE 0.0001 -#define ROTATION_FILE "/tmp/rotate" -class MotionConfig +class MotionHVConfig { public: - MotionConfig(); + MotionHVConfig(); - int equivalent(MotionConfig &that); - void copy_from(MotionConfig &that); - void interpolate(MotionConfig &prev, - MotionConfig &next, + int equivalent(MotionHVConfig &that); + void copy_from(MotionHVConfig &that); + void interpolate(MotionHVConfig &prev, + MotionHVConfig &next, int64_t prev_frame, int64_t next_frame, int64_t current_frame); @@ -95,11 +92,11 @@ public: // Percent of image size int global_block_w; int global_block_h; - int rotation_block_w; - int rotation_block_h; +// int rotation_block_w; +// int rotation_block_h; // Number of search positions in each refinement of the log search - int global_positions; - int rotate_positions; +// int global_positions; +// int rotate_positions; // Block position in percentage 0 - 100 double block_x; double block_y; @@ -146,11 +143,11 @@ public: -class MotionMain : public PluginVClient +class MotionHVMain : public PluginVClient { public: - MotionMain(PluginServer *server); - ~MotionMain(); + MotionHVMain(PluginServer *server); + ~MotionHVMain(); int process_buffer(VFrame **frame, int64_t start_position, @@ -167,7 +164,7 @@ public: void calculate_pointers(VFrame **frame, VFrame **src, VFrame **dst); void allocate_temp(int w, int h, int color_model); - PLUGIN_CLASS_MEMBERS2(MotionConfig) + PLUGIN_CLASS_MEMBERS2(MotionHVConfig) static void draw_pixel(VFrame *frame, int x, int y); @@ -179,8 +176,8 @@ public: // The frame compared with the previous frame to get the motion. // It is moved to compensate for motion and copied to the previous_frame. VFrame *temp_frame; - MotionScan *engine; - RotateScan *motion_rotate; + MotionHVScan *engine; +// RotateScan *motion_rotate; OverlayFrame *overlayer; AffineEngine *rotate_engine; @@ -246,110 +243,6 @@ public: - - - - - - - - - - - - - -class RotateScanPackage : public LoadPackage -{ -public: - RotateScanPackage(); - float angle; - int64_t difference; -}; - -class RotateScanCache -{ -public: - RotateScanCache(float angle, int64_t difference); - float angle; - int64_t difference; -}; - -class RotateScanUnit : public LoadClient -{ -public: - RotateScanUnit(RotateScan *server, MotionMain *plugin); - ~RotateScanUnit(); - - void process_package(LoadPackage *package); - - RotateScan *server; - MotionMain *plugin; - AffineEngine *rotater; - VFrame *temp; -}; - -class RotateScan : public LoadServer -{ -public: - RotateScan(MotionMain *plugin, - int total_clients, - int total_packages); - ~RotateScan(); - - friend class RotateScanUnit; - - void init_packages(); - LoadClient* new_client(); - LoadPackage* new_package(); - -// Invoke the motion engine for a search -// Frame before rotation - float scan_frame(VFrame *previous_frame, -// Frame after rotation - VFrame *current_frame, -// Pivot - int block_x, - int block_y); - int64_t get_cache(float angle); - void put_cache(float angle, int64_t difference); - - -// Angle result - float result; - -private: - VFrame *previous_frame; -// Frame after motion - VFrame *current_frame; - - MotionMain *plugin; - int skip; - -// Pivot - int block_x; - int block_y; -// Block to rotate - int block_x1; - int block_x2; - int block_y1; - int block_y2; -// Area to compare - int scan_x; - int scan_y; - int scan_w; - int scan_h; -// Range of angles to compare - float scan_angle1, scan_angle2; - int total_steps; - - ArrayList cache; - Mutex *cache_lock; -}; - - - - #endif diff --git a/cinelerra-5.1/plugins/motion.new/motion.inc b/cinelerra-5.1/plugins/motion-hv/motion-hv.inc similarity index 90% rename from cinelerra-5.1/plugins/motion.new/motion.inc rename to cinelerra-5.1/plugins/motion-hv/motion-hv.inc index cf71a2a4..b8b8795d 100644 --- a/cinelerra-5.1/plugins/motion.new/motion.inc +++ b/cinelerra-5.1/plugins/motion-hv/motion-hv.inc @@ -19,12 +19,12 @@ * */ -#ifndef MOTION_INC -#define MOTION_INC +#ifndef MOTIONHV_INC +#define MOTIONHV_INC -class MotionConfig; -class MotionMain; +class MotionHVConfig; +class MotionHVMain; #endif diff --git a/cinelerra-5.1/plugins/motion-hv/motionscan-hv.C b/cinelerra-5.1/plugins/motion-hv/motionscan-hv.C new file mode 100644 index 00000000..db8617ea --- /dev/null +++ b/cinelerra-5.1/plugins/motion-hv/motionscan-hv.C @@ -0,0 +1,1936 @@ + +/* + * CINELERRA + * Copyright (C) 2016 Adam Williams + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "affine.h" +#include "bcsignals.h" +#include "clip.h" +#include "motionscan-hv.h" +#include "mutex.h" +#include "vframe.h" + + +#include +#include +#include + +// The module which does the actual scanning + +// starting level of detail +#define STARTING_DOWNSAMPLE 16 +// minimum size in each level of detail +#define MIN_DOWNSAMPLED_SIZE 16 +// minimum scan range +#define MIN_DOWNSAMPLED_SCAN 4 +// scan range for subpixel mode +#define SUBPIXEL_RANGE 4 + +MotionHVScanPackage::MotionHVScanPackage() + : LoadPackage() +{ + valid = 1; +} + + + + + + +MotionHVScanUnit::MotionHVScanUnit(MotionHVScan *server) + : LoadClient(server) +{ + this->server = server; +} + +MotionHVScanUnit::~MotionHVScanUnit() +{ +} + + +void MotionHVScanUnit::single_pixel(MotionHVScanPackage *pkg) +{ + //int w = server->current_frame->get_w(); + //int h = server->current_frame->get_h(); + int color_model = server->current_frame->get_color_model(); + int pixel_size = BC_CModels::calculate_pixelsize(color_model); + int row_bytes = server->current_frame->get_bytes_per_line(); + +// printf("MotionHVScanUnit::process_package %d search_x=%d search_y=%d scan_x1=%d scan_y1=%d scan_x2=%d scan_y2=%d x_steps=%d y_steps=%d\n", +// __LINE__, +// pkg->search_x, +// pkg->search_y, +// pkg->scan_x1, +// pkg->scan_y1, +// pkg->scan_x2, +// pkg->scan_y2, +// server->x_steps, +// server->y_steps); + +// Pointers to first pixel in each block + unsigned char *prev_ptr = server->previous_frame->get_rows()[ + pkg->search_y] + + pkg->search_x * pixel_size; + unsigned char *current_ptr = 0; + + if(server->do_rotate) + { + current_ptr = server->rotated_current[pkg->angle_step]->get_rows()[ + pkg->block_y1] + + pkg->block_x1 * pixel_size; + } + else + { + current_ptr = server->current_frame->get_rows()[ + pkg->block_y1] + + pkg->block_x1 * pixel_size; + } + +// Scan block + pkg->difference1 = MotionHVScan::abs_diff(prev_ptr, + current_ptr, + row_bytes, + pkg->block_x2 - pkg->block_x1, + pkg->block_y2 - pkg->block_y1, + color_model); + +// printf("MotionHVScanUnit::process_package %d angle_step=%d diff=%d\n", +// __LINE__, +// pkg->angle_step, +// pkg->difference1); +// printf("MotionHVScanUnit::process_package %d search_x=%d search_y=%d diff=%lld\n", +// __LINE__, server->block_x1 - pkg->search_x, server->block_y1 - pkg->search_y, pkg->difference1); +} + +void MotionHVScanUnit::subpixel(MotionHVScanPackage *pkg) +{ +//PRINT_TRACE + //int w = server->current_frame->get_w(); + //int h = server->current_frame->get_h(); + int color_model = server->current_frame->get_color_model(); + int pixel_size = BC_CModels::calculate_pixelsize(color_model); + int row_bytes = server->current_frame->get_bytes_per_line(); + unsigned char *prev_ptr = server->previous_frame->get_rows()[ + pkg->search_y] + + pkg->search_x * pixel_size; +// neglect rotation + unsigned char *current_ptr = server->current_frame->get_rows()[ + pkg->block_y1] + + pkg->block_x1 * pixel_size; + +// With subpixel, there are two ways to compare each position, one by shifting +// the previous frame and two by shifting the current frame. + pkg->difference1 = MotionHVScan::abs_diff_sub(prev_ptr, + current_ptr, + row_bytes, + pkg->block_x2 - pkg->block_x1, + pkg->block_y2 - pkg->block_y1, + color_model, + pkg->sub_x, + pkg->sub_y); + pkg->difference2 = MotionHVScan::abs_diff_sub(current_ptr, + prev_ptr, + row_bytes, + pkg->block_x2 - pkg->block_x1, + pkg->block_y2 - pkg->block_y1, + color_model, + pkg->sub_x, + pkg->sub_y); +// printf("MotionHVScanUnit::process_package sub_x=%d sub_y=%d search_x=%d search_y=%d diff1=%lld diff2=%lld\n", +// pkg->sub_x, +// pkg->sub_y, +// pkg->search_x, +// pkg->search_y, +// pkg->difference1, +// pkg->difference2); +} + +void MotionHVScanUnit::process_package(LoadPackage *package) +{ + MotionHVScanPackage *pkg = (MotionHVScanPackage*)package; + + +// Single pixel + if(!server->subpixel) + { + single_pixel(pkg); + } + else +// Sub pixel + { + subpixel(pkg); + } + + + + +} + + + + + + + + + + + + + + + + + + +MotionHVScan::MotionHVScan(int total_clients, + int total_packages) + : LoadServer( +// DEBUG +//1, 1 +total_clients, total_packages +) +{ + test_match = 1; + downsampled_previous = 0; + downsampled_current = 0; + rotated_current = 0; + rotater = 0; +} + +MotionHVScan::~MotionHVScan() +{ + delete downsampled_previous; + delete downsampled_current; + if(rotated_current) + { + for(int i = 0; i < total_rotated; i++) + { + delete rotated_current[i]; + } + + delete [] rotated_current; + } + delete rotater; +} + + +void MotionHVScan::init_packages() +{ +// Set package coords +// Total range of positions to scan with downsampling + int downsampled_scan_x1 = scan_x1 / current_downsample; + //int downsampled_scan_x2 = scan_x2 / current_downsample; + int downsampled_scan_y1 = scan_y1 / current_downsample; + //int downsampled_scan_y2 = scan_y2 / current_downsample; + int downsampled_block_x1 = block_x1 / current_downsample; + int downsampled_block_x2 = block_x2 / current_downsample; + int downsampled_block_y1 = block_y1 / current_downsample; + int downsampled_block_y2 = block_y2 / current_downsample; + + +//printf("MotionHVScan::init_packages %d %d\n", __LINE__, get_total_packages()); +// printf("MotionHVScan::init_packages %d current_downsample=%d scan_x1=%d scan_x2=%d block_x1=%d block_x2=%d\n", +// __LINE__, +// current_downsample, +// downsampled_scan_x1, +// downsampled_scan_x2, +// downsampled_block_x1, +// downsampled_block_x2); +// if(current_downsample == 8 && downsampled_scan_x1 == 47) +// { +// downsampled_previous->write_png("/tmp/previous"); +// downsampled_current->write_png("/tmp/current"); +// } + + for(int i = 0; i < get_total_packages(); i++) + { + MotionHVScanPackage *pkg = (MotionHVScanPackage*)get_package(i); + + pkg->block_x1 = downsampled_block_x1; + pkg->block_x2 = downsampled_block_x2; + pkg->block_y1 = downsampled_block_y1; + pkg->block_y2 = downsampled_block_y2; + pkg->difference1 = 0; + pkg->difference2 = 0; + pkg->dx = 0; + pkg->dy = 0; + pkg->valid = 1; + pkg->angle_step = 0; + + if(!subpixel) + { + if(rotation_pass) + { + pkg->search_x = scan_x1 / current_downsample; + pkg->search_y = scan_y1 / current_downsample; + pkg->angle_step = i; + } + else + { + + int current_x_step = (i % x_steps); + int current_y_step = (i / x_steps); + + //printf("MotionHVScan::init_packages %d i=%d x_step=%d y_step=%d angle_step=%d\n", + //__LINE__, i, current_x_step, current_y_step, current_angle_step); + pkg->search_x = downsampled_scan_x1 + current_x_step * + (scan_x2 - scan_x1) / current_downsample / x_steps; + pkg->search_y = downsampled_scan_y1 + current_y_step * + (scan_y2 - scan_y1) / current_downsample / y_steps; + + if(do_rotate) + { + pkg->angle_step = angle_steps / 2; + } + else + { + pkg->angle_step = 0; + } + } + + pkg->sub_x = 0; + pkg->sub_y = 0; + } + else + { + pkg->sub_x = i % (OVERSAMPLE * SUBPIXEL_RANGE); + pkg->sub_y = i / (OVERSAMPLE * SUBPIXEL_RANGE); + +// if(horizontal_only) +// { +// pkg->sub_y = 0; +// } +// +// if(vertical_only) +// { +// pkg->sub_x = 0; +// } + + pkg->search_x = scan_x1 + pkg->sub_x / OVERSAMPLE + 1; + pkg->search_y = scan_y1 + pkg->sub_y / OVERSAMPLE + 1; + pkg->sub_x %= OVERSAMPLE; + pkg->sub_y %= OVERSAMPLE; + + + +// printf("MotionHVScan::init_packages %d i=%d search_x=%d search_y=%d sub_x=%d sub_y=%d\n", +// __LINE__, +// i, +// pkg->search_x, +// pkg->search_y, +// pkg->sub_x, +// pkg->sub_y); + } + +// printf("MotionHVScan::init_packages %d %d,%d %d,%d %d,%d\n", +// __LINE__, +// scan_x1, +// scan_x2, +// scan_y1, +// scan_y2, +// pkg->search_x, +// pkg->search_y); + } +} + +LoadClient* MotionHVScan::new_client() +{ + return new MotionHVScanUnit(this); +} + +LoadPackage* MotionHVScan::new_package() +{ + return new MotionHVScanPackage; +} + + +void MotionHVScan::set_test_match(int value) +{ + this->test_match = value; +} + + + + +#define DOWNSAMPLE(type, temp_type, components, max) \ +{ \ + temp_type r; \ + temp_type g; \ + temp_type b; \ + temp_type a; \ + type **in_rows = (type**)src->get_rows(); \ + type **out_rows = (type**)dst->get_rows(); \ + \ + for(int i = 0; i < h; i += downsample) \ + { \ + int y1 = MAX(i, 0); \ + int y2 = MIN(i + downsample, h); \ + \ + \ + for(int j = 0; \ + j < w; \ + j += downsample) \ + { \ + int x1 = MAX(j, 0); \ + int x2 = MIN(j + downsample, w); \ + \ + temp_type scale = (x2 - x1) * (y2 - y1); \ + if(x2 > x1 && y2 > y1) \ + { \ + \ +/* Read in values */ \ + r = 0; \ + g = 0; \ + b = 0; \ + if(components == 4) a = 0; \ + \ + for(int k = y1; k < y2; k++) \ + { \ + type *row = in_rows[k] + x1 * components; \ + for(int l = x1; l < x2; l++) \ + { \ + r += *row++; \ + g += *row++; \ + b += *row++; \ + if(components == 4) a += *row++; \ + } \ + } \ + \ +/* Write average */ \ + r /= scale; \ + g /= scale; \ + b /= scale; \ + if(components == 4) a /= scale; \ + \ + type *row = out_rows[y1 / downsample] + \ + x1 / downsample * components; \ + *row++ = r; \ + *row++ = g; \ + *row++ = b; \ + if(components == 4) *row++ = a; \ + } \ + } \ +/*printf("DOWNSAMPLE 3 %d\n", i);*/ \ + } \ +} + + + + +void MotionHVScan::downsample_frame(VFrame *dst, + VFrame *src, + int downsample) +{ + int h = src->get_h(); + int w = src->get_w(); + +//PRINT_TRACE +//printf("downsample=%d w=%d h=%d dst=%d %d\n", downsample, w, h, dst->get_w(), dst->get_h()); + switch(src->get_color_model()) + { + case BC_RGB888: + DOWNSAMPLE(uint8_t, int64_t, 3, 0xff) + break; + case BC_RGB_FLOAT: + DOWNSAMPLE(float, float, 3, 1.0) + break; + case BC_RGBA8888: + DOWNSAMPLE(uint8_t, int64_t, 4, 0xff) + break; + case BC_RGBA_FLOAT: + DOWNSAMPLE(float, float, 4, 1.0) + break; + case BC_YUV888: + DOWNSAMPLE(uint8_t, int64_t, 3, 0xff) + break; + case BC_YUVA8888: + DOWNSAMPLE(uint8_t, int64_t, 4, 0xff) + break; + } +//PRINT_TRACE +} + +double MotionHVScan::step_to_angle(int step, double center) +{ + if(step < angle_steps / 2) + { + return center - angle_step * (angle_steps / 2 - step); + } + else + if(step > angle_steps / 2) + { + return center + angle_step * (step - angle_steps / 2); + } + else + { + return center; + } +} + +#ifdef STDDEV_TEST +static int compare(const void *p1, const void *p2) +{ + double value1 = *(double*)p1; + double value2 = *(double*)p2; + +//printf("compare %d value1=%f value2=%f\n", __LINE__, value1, value2); + return value1 > value2; +} +#endif + +// reject vectors based on content. It's the reason Goog can't stabilize timelapses. +//#define STDDEV_TEST + +// pixel accurate motion search +void MotionHVScan::pixel_search(int &x_result, int &y_result, double &r_result) +{ +// reduce level of detail until enough steps + while(current_downsample > 1 && + ((block_x2 - block_x1) / current_downsample < MIN_DOWNSAMPLED_SIZE || + (block_y2 - block_y1) / current_downsample < MIN_DOWNSAMPLED_SIZE + || + (scan_x2 - scan_x1) / current_downsample < MIN_DOWNSAMPLED_SCAN || + (scan_y2 - scan_y1) / current_downsample < MIN_DOWNSAMPLED_SCAN + )) + { + current_downsample /= 2; + } + + + +// create downsampled images. +// Need to keep entire frame to search for rotation. + int downsampled_prev_w = previous_frame_arg->get_w() / current_downsample; + int downsampled_prev_h = previous_frame_arg->get_h() / current_downsample; + int downsampled_current_w = current_frame_arg->get_w() / current_downsample; + int downsampled_current_h = current_frame_arg->get_h() / current_downsample; + +// printf("MotionHVScan::pixel_search %d current_downsample=%d current_frame_arg->get_w()=%d downsampled_current_w=%d\n", +// __LINE__, +// current_downsample, +// current_frame_arg->get_w(), +// downsampled_current_w); + + x_steps = (scan_x2 - scan_x1) / current_downsample; + y_steps = (scan_y2 - scan_y1) / current_downsample; + +// in rads + double test_angle1 = atan2((double)downsampled_current_h / 2 - 1, (double)downsampled_current_w / 2); + double test_angle2 = atan2((double)downsampled_current_h / 2, (double)downsampled_current_w / 2 - 1); + +// in deg + angle_step = 360.0f * fabs(test_angle1 - test_angle2) / 2 / M_PI; + +// printf("MotionHVScan::pixel_search %d test_angle1=%f test_angle2=%f angle_step=%f\n", +// __LINE__, +// 360.0f * test_angle1 / 2 / M_PI, +// 360.0f * test_angle2 / 2 / M_PI, +// angle_step); + + + if(do_rotate && angle_step < rotation_range) + { + angle_steps = 1 + (int)((scan_angle2 - scan_angle1) / angle_step + 0.5); + } + else + { + angle_steps = 1; + } + + + if(current_downsample > 1) + { + if(!downsampled_previous || + downsampled_previous->get_w() != downsampled_prev_w || + downsampled_previous->get_h() != downsampled_prev_h) + { + delete downsampled_previous; + downsampled_previous = new VFrame(); + downsampled_previous->set_use_shm(0); + downsampled_previous->reallocate(0, + -1, + 0, + 0, + 0, + downsampled_prev_w + 1, + downsampled_prev_h + 1, + previous_frame_arg->get_color_model(), + -1); + } + + if(!downsampled_current || + downsampled_current->get_w() != downsampled_current_w || + downsampled_current->get_h() != downsampled_current_h) + { + delete downsampled_current; + downsampled_current = new VFrame(); + downsampled_current->set_use_shm(0); + downsampled_current->reallocate(0, + -1, + 0, + 0, + 0, + downsampled_current_w + 1, + downsampled_current_h + 1, + current_frame_arg->get_color_model(), + -1); + } + + + downsample_frame(downsampled_previous, + previous_frame_arg, + current_downsample); + downsample_frame(downsampled_current, + current_frame_arg, + current_downsample); + previous_frame = downsampled_previous; + current_frame = downsampled_current; + + } + else + { + previous_frame = previous_frame_arg; + current_frame = current_frame_arg; + } + + + +// printf("MotionHVScan::pixel_search %d x_steps=%d y_steps=%d angle_steps=%d total_steps=%d\n", +// __LINE__, +// x_steps, +// y_steps, +// angle_steps, +// total_steps); + + + +// test variance of constant macroblock + int color_model = current_frame->get_color_model(); + int pixel_size = BC_CModels::calculate_pixelsize(color_model); + int row_bytes = current_frame->get_bytes_per_line(); + int block_w = block_x2 - block_x1; + int block_h = block_y2 - block_y1; + + unsigned char *current_ptr = + current_frame->get_rows()[block_y1 / current_downsample] + + (block_x1 / current_downsample) * pixel_size; + unsigned char *previous_ptr = + previous_frame->get_rows()[scan_y1 / current_downsample] + + (scan_x1 / current_downsample) * pixel_size; + + + +// test detail in prev & current frame + double range1 = calculate_range(current_ptr, + row_bytes, + block_w / current_downsample, + block_h / current_downsample, + color_model); + + if(range1 < 1) + { +printf("MotionHVScan::pixel_search %d range fail range1=%f\n", __LINE__, range1); + failed = 1; + return; + } + + double range2 = calculate_range(previous_ptr, + row_bytes, + block_w / current_downsample, + block_h / current_downsample, + color_model); + + if(range2 < 1) + { +printf("MotionHVScan::pixel_search %d range fail range2=%f\n", __LINE__, range2); + failed = 1; + return; + } + + +// create rotated images + if(rotated_current && + (total_rotated != angle_steps || + rotated_current[0]->get_w() != downsampled_current_w || + rotated_current[0]->get_h() != downsampled_current_h)) + { + for(int i = 0; i < total_rotated; i++) + { + delete rotated_current[i]; + } + + delete [] rotated_current; + rotated_current = 0; + total_rotated = 0; + } + + if(do_rotate) + { + total_rotated = angle_steps; + + + if(!rotated_current) + { + rotated_current = new VFrame*[total_rotated]; + bzero(rotated_current, sizeof(VFrame*) * total_rotated); + } + +// printf("MotionHVScan::pixel_search %d total_rotated=%d w=%d h=%d block_w=%d block_h=%d\n", +// __LINE__, +// total_rotated, +// downsampled_current_w, +// downsampled_current_h, +// (block_x2 - block_x1) / current_downsample, +// (block_y2 - block_y1) / current_downsample); + for(int i = 0; i < angle_steps; i++) + { + +// printf("MotionHVScan::pixel_search %d w=%d h=%d x=%d y=%d angle=%f\n", +// __LINE__, +// downsampled_current_w, +// downsampled_current_h, +// (block_x1 + block_x2) / 2 / current_downsample, +// (block_y1 + block_y2) / 2 / current_downsample, +// step_to_angle(i, r_result)); + +// printf("MotionHVScan::pixel_search %d i=%d rotated_current[i]=%p\n", +// __LINE__, +// i, +// rotated_current[i]); + if(!rotated_current[i]) + { + rotated_current[i] = new VFrame(); + rotated_current[i]->set_use_shm(0); + rotated_current[i]->reallocate(0, + -1, + 0, + 0, + 0, + downsampled_current_w + 1, + downsampled_current_h + 1, + current_frame_arg->get_color_model(), + -1); +//printf("MotionHVScan::pixel_search %d\n", __LINE__); + } + + + if(!rotater) + { + rotater = new AffineEngine(get_total_clients(), + get_total_clients()); + } + +// get smallest viewport size required for the angle + double diag = hypot((block_x2 - block_x1) / current_downsample, + (block_y2 - block_y1) / current_downsample); + double angle1 = atan2(block_y2 - block_y1, block_x2 - block_x1) + + TO_RAD(step_to_angle(i, r_result)); + double angle2 = -atan2(block_y2 - block_y1, block_x2 - block_x1) + + TO_RAD(step_to_angle(i, r_result)); + double max_horiz = MAX(abs(diag * cos(angle1)), abs(diag * cos(angle2))); + double max_vert = MAX(abs(diag * sin(angle1)), abs(diag * sin(angle2))); + int center_x = (block_x1 + block_x2) / 2 / current_downsample; + int center_y = (block_y1 + block_y2) / 2 / current_downsample; + int x1 = center_x - max_horiz / 2; + int y1 = center_y - max_vert / 2; + int x2 = x1 + max_horiz; + int y2 = y1 + max_vert; + CLAMP(x1, 0, downsampled_current_w - 1); + CLAMP(y1, 0, downsampled_current_h - 1); + CLAMP(x2, 0, downsampled_current_w - 1); + CLAMP(y2, 0, downsampled_current_h - 1); + +//printf("MotionHVScan::pixel_search %d %f %f %d %d\n", +//__LINE__, TO_DEG(angle1), TO_DEG(angle2), (int)max_horiz, (int)max_vert); + rotater->set_in_viewport(x1, + y1, + x2 - x1, + y2 - y1); + rotater->set_out_viewport(x1, + y1, + x2 - x1, + y2 - y1); + +// rotater->set_in_viewport(0, +// 0, +// downsampled_current_w, +// downsampled_current_h); +// rotater->set_out_viewport(0, +// 0, +// downsampled_current_w, +// downsampled_current_h); + + rotater->set_in_pivot(center_x, center_y); + rotater->set_out_pivot(center_x, center_y); + + rotater->rotate(rotated_current[i], + current_frame, + step_to_angle(i, r_result)); + +// rotated_current[i]->draw_rect(block_x1 / current_downsample, +// block_y1 / current_downsample, +// block_x2 / current_downsample, +// block_y2 / current_downsample); +// char string[BCTEXTLEN]; +// sprintf(string, "/tmp/rotated%d", i); +// rotated_current[i]->write_png(string); +//downsampled_previous->write_png("/tmp/previous"); +//printf("MotionHVScan::pixel_search %d\n", __LINE__); + } + } + + + + + + +// printf("MotionHVScan::pixel_search %d block x=%d y=%d w=%d h=%d\n", +// __LINE__, +// block_x1 / current_downsample, +// block_y1 / current_downsample, +// block_w / current_downsample, +// block_h / current_downsample); + + + + + + + +//exit(1); +// Test only translation of the middle rotated frame + rotation_pass = 0; + total_steps = x_steps * y_steps; + set_package_count(total_steps); + process_packages(); + + + + + + +// Get least difference + int64_t min_difference = -1; +#ifdef STDDEV_TEST + double stddev_table[get_total_packages()]; +#endif + for(int i = 0; i < get_total_packages(); i++) + { + MotionHVScanPackage *pkg = (MotionHVScanPackage*)get_package(i); + +#ifdef STDDEV_TEST + double stddev = sqrt(pkg->difference1) / + (block_w / current_downsample) / + (block_h / current_downsample) / + 3; +// printf("MotionHVScan::pixel_search %d current_downsample=%d search_x=%d search_y=%d diff1=%f\n", +// __LINE__, +// current_downsample, +// pkg->search_x, +// pkg->search_y, +// sqrt(pkg->difference1) / block_w / current_downsample / block_h / 3 /* / variance */); + +// printf("MotionHVScan::pixel_search %d range1=%f stddev=%f\n", +// __LINE__, +// range1, +// stddev); + + stddev_table[i] = stddev; +#endif // STDDEV_TEST + + if(pkg->difference1 < min_difference || i == 0) + { + min_difference = pkg->difference1; + x_result = pkg->search_x * current_downsample * OVERSAMPLE; + y_result = pkg->search_y * current_downsample * OVERSAMPLE; + +// printf("MotionHVScan::pixel_search %d x_result=%d y_result=%d angle_step=%d diff=%lld\n", +// __LINE__, +// block_x1 * OVERSAMPLE - x_result, +// block_y1 * OVERSAMPLE - y_result, +// pkg->angle_step, +// pkg->difference1); + + } + } + + +#ifdef STDDEV_TEST + qsort(stddev_table, get_total_packages(), sizeof(double), compare); + + +// reject motion vector if not similar enough +// if(stddev_table[0] > 0.2) +// { +// if(debug) +// { +// printf("MotionHVScan::pixel_search %d stddev fail min_stddev=%f\n", +// __LINE__, +// stddev_table[0]); +// } +// failed = 1; +// return; +// } + +if(debug) +{ + printf("MotionHVScan::pixel_search %d\n", __LINE__); + for(int i = 0; i < get_total_packages(); i++) + { + printf("%f\n", stddev_table[i]); + } +} + +// reject motion vector if not a sigmoid curve +// TODO: use linear interpolation + int steps = 2; + int step = get_total_packages() / steps; + double curve[steps]; + for(int i = 0; i < steps; i++) + { + int start = get_total_packages() * i / steps; + int end = get_total_packages() * (i + 1) / steps; + end = MIN(end, get_total_packages() - 1); + curve[i] = stddev_table[end] - stddev_table[start]; + } + + +// if(curve[0] < (curve[1] * 1.01) || +// curve[2] < (curve[1] * 1.01) || +// curve[0] < (curve[2] * 0.75)) +// if(curve[0] < curve[1]) +// { +// if(debug) +// { +// printf("MotionHVScan::pixel_search %d curve fail %f %f\n", +// __LINE__, +// curve[0], +// curve[1]); +// } +// failed = 1; +// return; +// } + +if(debug) +{ +printf("MotionHVScan::pixel_search %d curve=%f %f ranges=%f %f min_stddev=%f\n", +__LINE__, +curve[0], +curve[1], +range1, +range2, +stddev_table[0]); +} +#endif // STDDEV_TEST + + + + + + if(do_rotate) + { + rotation_pass = 1;; + total_steps = angle_steps; + scan_x1 = x_result / OVERSAMPLE; + scan_y1 = y_result / OVERSAMPLE; + set_package_count(total_steps); + process_packages(); + + + + min_difference = -1; + double prev_r_result = r_result; + for(int i = 0; i < get_total_packages(); i++) + { + MotionHVScanPackage *pkg = (MotionHVScanPackage*)get_package(i); + +// printf("MotionHVScan::pixel_search %d search_x=%d search_y=%d angle_step=%d sub_x=%d sub_y=%d diff1=%lld diff2=%lld\n", +// __LINE__, +// pkg->search_x, +// pkg->search_y, +// pkg->search_angle_step, +// pkg->sub_x, +// pkg->sub_y, +// pkg->difference1, +// pkg->difference2); + if(pkg->difference1 < min_difference || i == 0) + { + min_difference = pkg->difference1; + r_result = step_to_angle(i, prev_r_result); + + // printf("MotionHVScan::pixel_search %d x_result=%d y_result=%d angle_step=%d diff=%lld\n", + // __LINE__, + // block_x1 * OVERSAMPLE - x_result, + // block_y1 * OVERSAMPLE - y_result, + // pkg->angle_step, + // pkg->difference1); + } + } + } + + +// printf("MotionHVScan::scan_frame %d current_downsample=%d x_result=%f y_result=%f r_result=%f\n", +// __LINE__, +// current_downsample, +// (float)x_result / OVERSAMPLE, +// (float)y_result / OVERSAMPLE, +// r_result); + +} + + +// subpixel motion search +void MotionHVScan::subpixel_search(int &x_result, int &y_result) +{ + rotation_pass = 0; + previous_frame = previous_frame_arg; + current_frame = current_frame_arg; + +//printf("MotionHVScan::scan_frame %d %d %d\n", __LINE__, x_result, y_result); +// Scan every subpixel in a SUBPIXEL_RANGE * SUBPIXEL_RANGE square + total_steps = (SUBPIXEL_RANGE * OVERSAMPLE) * (SUBPIXEL_RANGE * OVERSAMPLE); + +// These aren't used in subpixel + x_steps = OVERSAMPLE * SUBPIXEL_RANGE; + y_steps = OVERSAMPLE * SUBPIXEL_RANGE; + angle_steps = 1; + + set_package_count(this->total_steps); + process_packages(); + +// Get least difference + int64_t min_difference = -1; + for(int i = 0; i < get_total_packages(); i++) + { + MotionHVScanPackage *pkg = (MotionHVScanPackage*)get_package(i); +//printf("MotionHVScan::scan_frame %d search_x=%d search_y=%d sub_x=%d sub_y=%d diff1=%lld diff2=%lld\n", +//__LINE__, pkg->search_x, pkg->search_y, pkg->sub_x, pkg->sub_y, pkg->difference1, pkg->difference2); + if(pkg->difference1 < min_difference || min_difference == -1) + { + min_difference = pkg->difference1; + +// The sub coords are 1 pixel up & left of the block coords + x_result = pkg->search_x * OVERSAMPLE + pkg->sub_x; + y_result = pkg->search_y * OVERSAMPLE + pkg->sub_y; + + +// Fill in results + dx_result = block_x1 * OVERSAMPLE - x_result; + dy_result = block_y1 * OVERSAMPLE - y_result; +//printf("MotionHVScan::scan_frame %d dx_result=%d dy_result=%d diff=%lld\n", +//__LINE__, dx_result, dy_result, min_difference); + } + + if(pkg->difference2 < min_difference) + { + min_difference = pkg->difference2; + + x_result = pkg->search_x * OVERSAMPLE - pkg->sub_x; + y_result = pkg->search_y * OVERSAMPLE - pkg->sub_y; + + dx_result = block_x1 * OVERSAMPLE - x_result; + dy_result = block_y1 * OVERSAMPLE - y_result; +//printf("MotionHVScan::scan_frame %d dx_result=%d dy_result=%d diff=%lld\n", +//__LINE__, dx_result, dy_result, min_difference); + } + } +} + + +void MotionHVScan::scan_frame(VFrame *previous_frame, + VFrame *current_frame, + int global_range_w, + int global_range_h, + int global_block_w, + int global_block_h, + int block_x, + int block_y, + int frame_type, + int tracking_type, + int action_type, + int horizontal_only, + int vertical_only, + int source_position, + int total_dx, + int total_dy, + int global_origin_x, + int global_origin_y, + int do_motion, + int do_rotate, + double rotation_center, + double rotation_range) +{ + this->previous_frame_arg = previous_frame; + this->current_frame_arg = current_frame; + this->horizontal_only = horizontal_only; + this->vertical_only = vertical_only; + this->previous_frame = previous_frame_arg; + this->current_frame = current_frame_arg; + this->global_origin_x = global_origin_x; + this->global_origin_y = global_origin_y; + this->action_type = action_type; + this->do_motion = do_motion; + this->do_rotate = do_rotate; + this->rotation_center = rotation_center; + this->rotation_range = rotation_range; + +//printf("MotionHVScan::scan_frame %d\n", __LINE__); + dx_result = 0; + dy_result = 0; + dr_result = 0; + failed = 0; + + subpixel = 0; +// starting level of detail +// TODO: base it on a table of resolutions + current_downsample = STARTING_DOWNSAMPLE; + angle_step = 0; + +// Single macroblock + int w = current_frame->get_w(); + int h = current_frame->get_h(); + +// Initial search parameters + scan_w = global_range_w; + scan_h = global_range_h; + + int block_w = global_block_w; + int block_h = global_block_h; + +// printf("MotionHVScan::scan_frame %d %d %d %d %d %d %d %d %d\n", +// __LINE__, +// global_range_w, +// global_range_h, +// global_block_w, +// global_block_h, +// scan_w, +// scan_h, +// block_w, +// block_h); + +// Location of block in previous frame + block_x1 = (int)(block_x - block_w / 2); + block_y1 = (int)(block_y - block_h / 2); + block_x2 = (int)(block_x + block_w / 2); + block_y2 = (int)(block_y + block_h / 2); + +// Offset to location of previous block. This offset needn't be very accurate +// since it's the offset of the previous image and current image we want. + if(frame_type == MotionHVScan::TRACK_PREVIOUS) + { + block_x1 += total_dx / OVERSAMPLE; + block_y1 += total_dy / OVERSAMPLE; + block_x2 += total_dx / OVERSAMPLE; + block_y2 += total_dy / OVERSAMPLE; + } + + skip = 0; + + switch(tracking_type) + { +// Don't calculate + case MotionHVScan::NO_CALCULATE: + dx_result = 0; + dy_result = 0; + dr_result = rotation_center; + skip = 1; + break; + + case MotionHVScan::LOAD: + { +// Load result from disk + char string[BCTEXTLEN]; + + skip = 1; + if(do_motion) + { + sprintf(string, "%s%06d", + MOTION_FILE, + source_position); +//printf("MotionHVScan::scan_frame %d %s\n", __LINE__, string); + FILE *input = fopen(string, "r"); + if(input) + { + int temp = fscanf(input, + "%d %d", + &dx_result, + &dy_result); + if( temp != 2 ) + printf("MotionHVScan::scan_frame %d %s\n", __LINE__, string); +// HACK +//dx_result *= 2; +//dy_result *= 2; +//printf("MotionHVScan::scan_frame %d %d %d\n", __LINE__, dx_result, dy_result); + fclose(input); + } + else + { + skip = 0; + } + } + + if(do_rotate) + { + sprintf(string, + "%s%06d", + ROTATION_FILE, + source_position); + FILE *input = fopen(string, "r"); + if(input) + { + int temp = fscanf(input, "%f", &dr_result); + if( temp != 1 ) + printf("MotionHVScan::scan_frame %d %s\n", __LINE__, string); +// DEBUG +//dr_result += 0.25; + fclose(input); + } + else + { + skip = 0; + } + } + break; + } + +// Scan from scratch + default: + skip = 0; + break; + } + + + + if(!skip && test_match) + { + if(previous_frame->data_matches(current_frame)) + { +printf("MotionHVScan::scan_frame: data matches. skipping.\n"); + dx_result = 0; + dy_result = 0; + dr_result = rotation_center; + skip = 1; + } + } + + +// Perform scan + if(!skip) + { +// Location of block in current frame + int origin_offset_x = this->global_origin_x; + int origin_offset_y = this->global_origin_y; + int x_result = block_x1 + origin_offset_x; + int y_result = block_y1 + origin_offset_y; + double r_result = rotation_center; + +// printf("MotionHVScan::scan_frame 1 %d %d %d %d %d %d %d %d\n", +// block_x1 + block_w / 2, +// block_y1 + block_h / 2, +// block_w, +// block_h, +// block_x1, +// block_y1, +// block_x2, +// block_y2); + + while(!failed) + { + scan_x1 = x_result - scan_w / 2; + scan_y1 = y_result - scan_h / 2; + scan_x2 = x_result + scan_w / 2; + scan_y2 = y_result + scan_h / 2; + scan_angle1 = r_result - rotation_range; + scan_angle2 = r_result + rotation_range; + + + +// Zero out requested values +// if(horizontal_only) +// { +// scan_y1 = block_y1; +// scan_y2 = block_y1 + 1; +// } +// if(vertical_only) +// { +// scan_x1 = block_x1; +// scan_x2 = block_x1 + 1; +// } + +// printf("MotionHVScan::scan_frame %d block_x1=%d block_y1=%d block_x2=%d block_y2=%d scan_x1=%d scan_y1=%d scan_x2=%d scan_y2=%d\n", +// __LINE__, +// block_x1, +// block_y1, +// block_x2, +// block_y2, +// scan_x1, +// scan_y1, +// scan_x2, +// scan_y2); + + +// Clamp the block coords before the scan so we get useful scan coords. + clamp_scan(w, + h, + &block_x1, + &block_y1, + &block_x2, + &block_y2, + &scan_x1, + &scan_y1, + &scan_x2, + &scan_y2, + 0); + + +// printf("MotionHVScan::scan_frame %d block_x1=%d block_y1=%d block_x2=%d block_y2=%d scan_x1=%d scan_y1=%d scan_x2=%d scan_y2=%d x_result=%d y_result=%d\n", +// __LINE__, +// block_x1, +// block_y1, +// block_x2, +// block_y2, +// scan_x1, +// scan_y1, +// scan_x2, +// scan_y2, +// x_result, +// y_result); +//if(y_result == 88) exit(0); + + +// Give up if invalid coords. + if(scan_y2 <= scan_y1 || + scan_x2 <= scan_x1 || + block_x2 <= block_x1 || + block_y2 <= block_y1) + { + break; + } + +// For subpixel, the top row and left column are skipped + if(subpixel) + { + + subpixel_search(x_result, y_result); +// printf("MotionHVScan::scan_frame %d x_result=%d y_result=%d\n", +// __LINE__, +// x_result / OVERSAMPLE, +// y_result / OVERSAMPLE); + + break; + } + else +// Single pixel + { + pixel_search(x_result, y_result, r_result); +//printf("MotionHVScan::scan_frame %d x_result=%d y_result=%d\n", __LINE__, x_result / OVERSAMPLE, y_result / OVERSAMPLE); + + if(failed) + { + dr_result = 0; + dx_result = 0; + dy_result = 0; + } + else + if(current_downsample <= 1) + { + // Single pixel accuracy reached. Now do exhaustive subpixel search. + if(action_type == MotionHVScan::STABILIZE || + action_type == MotionHVScan::TRACK || + action_type == MotionHVScan::NOTHING) + { + x_result /= OVERSAMPLE; + y_result /= OVERSAMPLE; +//printf("MotionHVScan::scan_frame %d x_result=%d y_result=%d\n", __LINE__, x_result, y_result); + scan_w = SUBPIXEL_RANGE; + scan_h = SUBPIXEL_RANGE; +// Final R result + dr_result = rotation_center - r_result; + subpixel = 1; + } + else + { +// Fill in results and quit + dx_result = block_x1 * OVERSAMPLE - x_result; + dy_result = block_y1 * OVERSAMPLE - y_result; + dr_result = rotation_center - r_result; + break; + } + } + else +// Reduce scan area and try again + { +// scan_w = (scan_x2 - scan_x1) / 2; +// scan_h = (scan_y2 - scan_y1) / 2; +// need slightly more than 2x downsampling factor + + if(current_downsample * 3 < scan_w && + current_downsample * 3 < scan_h) + { + scan_w = current_downsample * 3; + scan_h = current_downsample * 3; + } + + if(angle_step * 1.5 < rotation_range) + { + rotation_range = angle_step * 1.5; + } +//printf("MotionHVScan::scan_frame %d %f %f\n", __LINE__, angle_step, rotation_range); + + current_downsample /= 2; + +// convert back to pixels + x_result /= OVERSAMPLE; + y_result /= OVERSAMPLE; +// debug +//exit(1); + } + + } + } + + dx_result *= -1; + dy_result *= -1; + dr_result *= -1; + } +// printf("MotionHVScan::scan_frame %d dx=%f dy=%f dr=%f\n", +// __LINE__, +// (float)dx_result / OVERSAMPLE, +// (float)dy_result / OVERSAMPLE, +// dr_result); + + + + +// Write results + if(!skip && tracking_type == MotionHVScan::SAVE) + { + char string[BCTEXTLEN]; + + + if(do_motion) + { + sprintf(string, + "%s%06d", + MOTION_FILE, + source_position); + FILE *output = fopen(string, "w"); + if(output) + { + fprintf(output, + "%d %d\n", + dx_result, + dy_result); + fclose(output); + } + else + { + printf("MotionHVScan::scan_frame %d: save motion failed\n", __LINE__); + } + } + + if(do_rotate) + { + sprintf(string, + "%s%06d", + ROTATION_FILE, + source_position); + FILE *output = fopen(string, "w"); + if(output) + { + fprintf(output, "%f\n", dr_result); + fclose(output); + } + else + { + printf("MotionHVScan::scan_frame %d save rotation failed\n", __LINE__); + } + } + } + + + if(vertical_only) dx_result = 0; + if(horizontal_only) dy_result = 0; + +// printf("MotionHVScan::scan_frame %d dx=%d dy=%d\n", +// __LINE__, +// this->dx_result, +// this->dy_result); +} + + + + + + + + + + + + + + + + + + + +#define ABS_DIFF(type, temp_type, multiplier, components) \ +{ \ + temp_type result_temp = 0; \ + for(int i = 0; i < h; i++) \ + { \ + type *prev_row = (type*)prev_ptr; \ + type *current_row = (type*)current_ptr; \ + for(int j = 0; j < w; j++) \ + { \ + for(int k = 0; k < 3; k++) \ + { \ + temp_type difference; \ + difference = *prev_row++ - *current_row++; \ + difference *= difference; \ + result_temp += difference; \ + } \ + if(components == 4) \ + { \ + prev_row++; \ + current_row++; \ + } \ + } \ + prev_ptr += row_bytes; \ + current_ptr += row_bytes; \ + } \ + result = (int64_t)(result_temp * multiplier); \ +} + +int64_t MotionHVScan::abs_diff(unsigned char *prev_ptr, + unsigned char *current_ptr, + int row_bytes, + int w, + int h, + int color_model) +{ + int64_t result = 0; + switch(color_model) + { + case BC_RGB888: + ABS_DIFF(unsigned char, int64_t, 1, 3) + break; + case BC_RGBA8888: + ABS_DIFF(unsigned char, int64_t, 1, 4) + break; + case BC_RGB_FLOAT: + ABS_DIFF(float, double, 0x10000, 3) + break; + case BC_RGBA_FLOAT: + ABS_DIFF(float, double, 0x10000, 4) + break; + case BC_YUV888: + ABS_DIFF(unsigned char, int64_t, 1, 3) + break; + case BC_YUVA8888: + ABS_DIFF(unsigned char, int64_t, 1, 4) + break; + } + return result; +} + + + +#define ABS_DIFF_SUB(type, temp_type, multiplier, components) \ +{ \ + temp_type result_temp = 0; \ + temp_type y2_fraction = sub_y * 0x100 / OVERSAMPLE; \ + temp_type y1_fraction = 0x100 - y2_fraction; \ + temp_type x2_fraction = sub_x * 0x100 / OVERSAMPLE; \ + temp_type x1_fraction = 0x100 - x2_fraction; \ + for(int i = 0; i < h_sub; i++) \ + { \ + type *prev_row1 = (type*)prev_ptr; \ + type *prev_row2 = (type*)prev_ptr + components; \ + type *prev_row3 = (type*)(prev_ptr + row_bytes); \ + type *prev_row4 = (type*)(prev_ptr + row_bytes) + components; \ + type *current_row = (type*)current_ptr; \ + for(int j = 0; j < w_sub; j++) \ + { \ +/* Scan each component */ \ + for(int k = 0; k < 3; k++) \ + { \ + temp_type difference; \ + temp_type prev_value = \ + (*prev_row1++ * x1_fraction * y1_fraction + \ + *prev_row2++ * x2_fraction * y1_fraction + \ + *prev_row3++ * x1_fraction * y2_fraction + \ + *prev_row4++ * x2_fraction * y2_fraction) / \ + 0x100 / 0x100; \ + temp_type current_value = *current_row++; \ + difference = prev_value - current_value; \ + difference *= difference; \ + result_temp += difference; \ + } \ + \ +/* skip alpha */ \ + if(components == 4) \ + { \ + prev_row1++; \ + prev_row2++; \ + prev_row3++; \ + prev_row4++; \ + current_row++; \ + } \ + } \ + prev_ptr += row_bytes; \ + current_ptr += row_bytes; \ + } \ + result = (int64_t)(result_temp * multiplier); \ +} + + + + +int64_t MotionHVScan::abs_diff_sub(unsigned char *prev_ptr, + unsigned char *current_ptr, + int row_bytes, + int w, + int h, + int color_model, + int sub_x, + int sub_y) +{ + int h_sub = h - 1; + int w_sub = w - 1; + int64_t result = 0; + + switch(color_model) + { + case BC_RGB888: + ABS_DIFF_SUB(unsigned char, int64_t, 1, 3) + break; + case BC_RGBA8888: + ABS_DIFF_SUB(unsigned char, int64_t, 1, 4) + break; + case BC_RGB_FLOAT: + ABS_DIFF_SUB(float, double, 0x10000, 3) + break; + case BC_RGBA_FLOAT: + ABS_DIFF_SUB(float, double, 0x10000, 4) + break; + case BC_YUV888: + ABS_DIFF_SUB(unsigned char, int64_t, 1, 3) + break; + case BC_YUVA8888: + ABS_DIFF_SUB(unsigned char, int64_t, 1, 4) + break; + } + return result; +} + + +#if 0 +#define VARIANCE(type, temp_type, multiplier, components) \ +{ \ + temp_type average[3] = { 0 }; \ + temp_type variance[3] = { 0 }; \ + \ + for(int i = 0; i < h; i++) \ + { \ + type *row = (type*)current_ptr + i * row_bytes; \ + for(int j = 0; j < w; j++) \ + { \ + for(int k = 0; k < 3; k++) \ + { \ + average[k] += row[k]; \ + } \ + row += components; \ + } \ + } \ + for(int k = 0; k < 3; k++) \ + { \ + average[k] /= w * h; \ + } \ + \ + for(int i = 0; i < h; i++) \ + { \ + type *row = (type*)current_ptr + i * row_bytes; \ + for(int j = 0; j < w; j++) \ + { \ + for(int k = 0; k < 3; k++) \ + { \ + variance[k] += SQR(row[k] - average[k]); \ + } \ + row += components; \ + } \ + } \ + result = (double)multiplier * \ + sqrt((variance[0] + variance[1] + variance[2]) / w / h / 3); \ +} + +double MotionHVScan::calculate_variance(unsigned char *current_ptr, + int row_bytes, + int w, + int h, + int color_model) +{ + double result = 0; + + switch(color_model) + { + case BC_RGB888: + VARIANCE(unsigned char, int, 1, 3) + break; + case BC_RGBA8888: + VARIANCE(unsigned char, int, 1, 4) + break; + case BC_RGB_FLOAT: + VARIANCE(float, double, 255, 3) + break; + case BC_RGBA_FLOAT: + VARIANCE(float, double, 255, 4) + break; + case BC_YUV888: + VARIANCE(unsigned char, int, 1, 3) + break; + case BC_YUVA8888: + VARIANCE(unsigned char, int, 1, 4) + break; + } + + + return result; +} +#endif // 0 + + + + +#define RANGE(type, temp_type, multiplier, components) \ +{ \ + temp_type min[3]; \ + temp_type max[3]; \ + min[0] = 0x7fff; \ + min[1] = 0x7fff; \ + min[2] = 0x7fff; \ + max[0] = 0; \ + max[1] = 0; \ + max[2] = 0; \ + \ + for(int i = 0; i < h; i++) \ + { \ + type *row = (type*)current_ptr + i * row_bytes; \ + for(int j = 0; j < w; j++) \ + { \ + for(int k = 0; k < 3; k++) \ + { \ + if(row[k] > max[k]) max[k] = row[k]; \ + if(row[k] < min[k]) min[k] = row[k]; \ + } \ + row += components; \ + } \ + } \ + \ + for(int k = 0; k < 3; k++) \ + { \ + /* printf("MotionHVScan::calculate_range %d k=%d max=%d min=%d\n", __LINE__, k, max[k], min[k]); */ \ + if(max[k] - min[k] > result) result = max[k] - min[k]; \ + } \ + \ +} + +double MotionHVScan::calculate_range(unsigned char *current_ptr, + int row_bytes, + int w, + int h, + int color_model) +{ + double result = 0; + + switch(color_model) + { + case BC_RGB888: + RANGE(unsigned char, int, 1, 3) + break; + case BC_RGBA8888: + RANGE(unsigned char, int, 1, 4) + break; + case BC_RGB_FLOAT: + RANGE(float, float, 255, 3) + break; + case BC_RGBA_FLOAT: + RANGE(float, float, 255, 4) + break; + case BC_YUV888: + RANGE(unsigned char, int, 1, 3) + break; + case BC_YUVA8888: + RANGE(unsigned char, int, 1, 4) + break; + } + + + return result; +} + + +//#define CLAMP_BLOCK + +// this truncates the scan area but not the macroblock unless the macro is defined +void MotionHVScan::clamp_scan(int w, + int h, + int *block_x1, + int *block_y1, + int *block_x2, + int *block_y2, + int *scan_x1, + int *scan_y1, + int *scan_x2, + int *scan_y2, + int use_absolute) +{ +// printf("MotionHVMain::clamp_scan 1 w=%d h=%d block=%d %d %d %d scan=%d %d %d %d absolute=%d\n", +// w, +// h, +// *block_x1, +// *block_y1, +// *block_x2, +// *block_y2, +// *scan_x1, +// *scan_y1, +// *scan_x2, +// *scan_y2, +// use_absolute); + + if(use_absolute) + { +// Limit size of scan area +// Used for drawing vectors +// scan is always out of range before block. + if(*scan_x1 < 0) + { +#ifdef CLAMP_BLOCK + int difference = -*scan_x1; + *block_x1 += difference; +#endif + *scan_x1 = 0; + } + + if(*scan_y1 < 0) + { +#ifdef CLAMP_BLOCK + int difference = -*scan_y1; + *block_y1 += difference; +#endif + *scan_y1 = 0; + } + + if(*scan_x2 > w) + { + int difference = *scan_x2 - w; +#ifdef CLAMP_BLOCK + *block_x2 -= difference; +#endif + *scan_x2 -= difference; + } + + if(*scan_y2 > h) + { + int difference = *scan_y2 - h; +#ifdef CLAMP_BLOCK + *block_y2 -= difference; +#endif + *scan_y2 -= difference; + } + + CLAMP(*scan_x1, 0, w); + CLAMP(*scan_y1, 0, h); + CLAMP(*scan_x2, 0, w); + CLAMP(*scan_y2, 0, h); + } + else + { +// Limit range of upper left block coordinates +// Used for motion tracking + if(*scan_x1 < 0) + { + int difference = -*scan_x1; +#ifdef CLAMP_BLOCK + *block_x1 += difference; +#endif + *scan_x2 += difference; + *scan_x1 = 0; + } + + if(*scan_y1 < 0) + { + int difference = -*scan_y1; +#ifdef CLAMP_BLOCK + *block_y1 += difference; +#endif + *scan_y2 += difference; + *scan_y1 = 0; + } + + if(*scan_x2 - *block_x1 + *block_x2 > w) + { + int difference = *scan_x2 - *block_x1 + *block_x2 - w; + *scan_x2 -= difference; +#ifdef CLAMP_BLOCK + *block_x2 -= difference; +#endif + } + + if(*scan_y2 - *block_y1 + *block_y2 > h) + { + int difference = *scan_y2 - *block_y1 + *block_y2 - h; + *scan_y2 -= difference; +#ifdef CLAMP_BLOCK + *block_y2 -= difference; +#endif + } + +// CLAMP(*scan_x1, 0, w - (*block_x2 - *block_x1)); +// CLAMP(*scan_y1, 0, h - (*block_y2 - *block_y1)); +// CLAMP(*scan_x2, 0, w - (*block_x2 - *block_x1)); +// CLAMP(*scan_y2, 0, h - (*block_y2 - *block_y1)); + } + +// Sanity checks which break the calculation but should never happen if the +// center of the block is inside the frame. + CLAMP(*block_x1, 0, w); + CLAMP(*block_x2, 0, w); + CLAMP(*block_y1, 0, h); + CLAMP(*block_y2, 0, h); + +// printf("MotionHVMain::clamp_scan 2 w=%d h=%d block=%d %d %d %d scan=%d %d %d %d absolute=%d\n", +// w, +// h, +// *block_x1, +// *block_y1, +// *block_x2, +// *block_y2, +// *scan_x1, +// *scan_y1, +// *scan_x2, +// *scan_y2, +// use_absolute); +} + + + diff --git a/cinelerra-5.1/plugins/motion.new/motionscan.h b/cinelerra-5.1/plugins/motion-hv/motionscan-hv.h similarity index 62% rename from cinelerra-5.1/plugins/motion.new/motionscan.h rename to cinelerra-5.1/plugins/motion-hv/motionscan-hv.h index 7bee1222..7511e642 100644 --- a/cinelerra-5.1/plugins/motion.new/motionscan.h +++ b/cinelerra-5.1/plugins/motion-hv/motionscan-hv.h @@ -1,7 +1,7 @@ /* * CINELERRA - * Copyright (C) 2008 Adam Williams + * Copyright (C) 2016 Adam Williams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,27 +23,28 @@ #define MOTIONSCAN_H -#include "arraylist.h" -//#include "../downsample/downsampleengine.inc" +#include "affine.inc" #include "loadbalance.h" #include "vframe.inc" #include -class MotionScan; +class MotionHVScan; #define OVERSAMPLE 4 -#define MOTION_FILE "/tmp/motion" +#define MOTION_FILE "/tmp/m" +#define ROTATION_FILE "/tmp/r" -class MotionScanPackage : public LoadPackage +class MotionHVScanPackage : public LoadPackage { public: - MotionScanPackage(); + MotionHVScanPackage(); // For multiple blocks -// Position of stationary block +// Position of stationary block after downsampling int block_x1, block_y1, block_x2, block_y2; -// Range of positions to scan - int scan_x1, scan_y1, scan_x2, scan_y2; +// index of rotated frame + int angle_step; + int dx; int dy; int64_t max_difference; @@ -51,11 +52,9 @@ public: int64_t min_pixel; int is_border; int valid; -// For single block - int step; int64_t difference1; int64_t difference2; -// Search position to nearest pixel +// Search position of current package to nearest pixel with downsampling int search_x; int search_y; // Subpixel of search position @@ -63,38 +62,27 @@ public: int sub_y; }; -class MotionScanCache +class MotionHVScanUnit : public LoadClient { public: - MotionScanCache(int x, int y, int64_t difference); - int x, y; - int64_t difference; -}; - -class MotionScanUnit : public LoadClient -{ -public: - MotionScanUnit(MotionScan *server); - ~MotionScanUnit(); + MotionHVScanUnit(MotionHVScan *server); + ~MotionHVScanUnit(); void process_package(LoadPackage *package); - int64_t get_cache(int x, int y); - void put_cache(int x, int y, int64_t difference); - - MotionScan *server; + void subpixel(MotionHVScanPackage *pkg); + void single_pixel(MotionHVScanPackage *pkg); - ArrayList cache; - Mutex *cache_lock; + MotionHVScan *server; }; -class MotionScan : public LoadServer +class MotionHVScan : public LoadServer { public: - MotionScan(int total_clients, + MotionHVScan(int total_clients, int total_packages); - ~MotionScan(); + ~MotionHVScan(); - friend class MotionScanUnit; + friend class MotionHVScanUnit; void init_packages(); LoadClient* new_client(); @@ -105,27 +93,27 @@ public: // Invoke the motion engine for a search // Frame before motion void scan_frame(VFrame *previous_frame, -// Frame after motion VFrame *current_frame, - int global_range_w, + int global_range_w, // in pixels int global_range_h, - int global_block_w, + int global_block_w, // in pixels int global_block_h, - double block_x, - double block_y, + int block_x, // in pixels + int block_y, int frame_type, int tracking_type, int action_type, int horizontal_only, int vertical_only, int source_position, - int total_steps, - int total_dx, + int total_dx, // in pixels * OVERSAMPLE int total_dy, - int global_origin_x, - int global_origin_y); - int64_t get_cache(int x, int y); - void put_cache(int x, int y, int64_t difference); + int global_origin_x, // in pixels + int global_origin_y, + int do_motion, + int do_rotate, + double rotation_center, // in deg + double rotation_range); static int64_t abs_diff(unsigned char *prev_ptr, unsigned char *current_ptr, @@ -159,6 +147,7 @@ public: // OVERSAMPLE int dx_result; int dy_result; + float dr_result; enum { @@ -188,6 +177,27 @@ public: }; private: + void downsample_frame(VFrame *dst, + VFrame *src, + int downsample); + void pixel_search(int &x_result, int &y_result, double &r_result); + void subpixel_search(int &x_result, int &y_result); + double step_to_angle(int step, double center); + +// double calculate_variance(unsigned char *current_ptr, +// int row_bytes, +// int w, +// int h, +// int color_model); + double calculate_range(unsigned char *current_ptr, + int row_bytes, + int w, + int h, + int color_model); + + + + AffineEngine *rotater; // Pointer to downsampled frame before motion VFrame *previous_frame; // Pointer to downsampled frame after motion @@ -198,33 +208,49 @@ private: // Downsampled frames VFrame *downsampled_previous; VFrame *downsampled_current; +// rotated versions of current_frame + VFrame **rotated_current; +// allocation of rotated_current array, a copy of angle_steps + int total_rotated; // Test for identical frames before processing // Faster to skip it if the frames are usually different int test_match; int skip; +// macroblocks didn't have enough data + int failed; // For single block int block_x1; int block_x2; int block_y1; int block_y2; + int scan_w; + int scan_h; int scan_x1; int scan_y1; int scan_x2; int scan_y2; - int total_pixels; - int total_steps; - int edge_steps; + double scan_angle1, scan_angle2; int y_steps; int x_steps; + int angle_steps; +// in deg + double angle_step; int subpixel; int horizontal_only; int vertical_only; int global_origin_x; int global_origin_y; - - ArrayList cache; - Mutex *cache_lock; -// DownSampleServer *downsample; + int action_type; + int current_downsample; + int downsampled_w; + int downsampled_h; + int total_steps; + int do_motion; + int do_rotate; + int rotation_pass; +// in deg + double rotation_center; + double rotation_range; }; diff --git a/cinelerra-5.1/plugins/motion.new/motionscan.inc b/cinelerra-5.1/plugins/motion-hv/motionscan-hv.inc similarity index 92% rename from cinelerra-5.1/plugins/motion.new/motionscan.inc rename to cinelerra-5.1/plugins/motion-hv/motionscan-hv.inc index 4e8229ed..dd2e376b 100644 --- a/cinelerra-5.1/plugins/motion.new/motionscan.inc +++ b/cinelerra-5.1/plugins/motion-hv/motionscan-hv.inc @@ -19,13 +19,13 @@ * */ -#ifndef MOTIONSCAN_INC -#define MOTIONSCAN_INC +#ifndef MOTIONSCANHV_INC +#define MOTIONSCANHV_INC -class MotionScan; +class MotionHVScan; diff --git a/cinelerra-5.1/plugins/motion.new/motionwindow.C b/cinelerra-5.1/plugins/motion-hv/motionwindow-hv.C similarity index 60% rename from cinelerra-5.1/plugins/motion.new/motionwindow.C rename to cinelerra-5.1/plugins/motion-hv/motionwindow-hv.C index 26d7075a..ad536ba5 100644 --- a/cinelerra-5.1/plugins/motion.new/motionwindow.C +++ b/cinelerra-5.1/plugins/motion-hv/motionwindow-hv.C @@ -1,7 +1,7 @@ /* * CINELERRA - * Copyright (C) 2008 Adam Williams + * Copyright (C) 2012 Adam Williams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,9 +22,9 @@ #include "bcdisplayinfo.h" #include "clip.h" #include "language.h" -#include "motion.h" -#include "motionscan.h" -#include "motionwindow.h" +#include "motion-hv.h" +#include "motionscan-hv.h" +#include "motionwindow-hv.h" @@ -35,22 +35,17 @@ -MotionWindow::MotionWindow(MotionMain *plugin) - : PluginClientWindow(plugin, - 600, - 650, - 600, - 650, - 0) +MotionHVWindow::MotionHVWindow(MotionHVMain *plugin) + : PluginClientWindow(plugin, 600, 650, 600, 650, 0) { this->plugin = plugin; } -MotionWindow::~MotionWindow() +MotionHVWindow::~MotionHVWindow() { } -void MotionWindow::create_objects() +void MotionHVWindow::create_objects() { int x1 = 10, x = 10, y = 10; int x2 = 310; @@ -58,12 +53,12 @@ void MotionWindow::create_objects() - add_subwindow(global = new MotionGlobal(plugin, - this, - x1, - y)); +// add_subwindow(global = new MotionHVGlobal(plugin, +// this, +// x1, +// y)); - add_subwindow(rotate = new MotionRotate(plugin, + add_subwindow(rotate = new MotionHVRotate(plugin, this, x2, y)); @@ -101,32 +96,32 @@ void MotionWindow::create_objects() y, &plugin->config.global_block_h)); - add_subwindow(title = new BC_Title(x2, - y, - _("Rotation block size:\n(W/H Percent of image)"))); - add_subwindow(rotation_block_w = new BlockSize(plugin, - x2 + title->get_w() + 10, - y, - &plugin->config.rotation_block_w)); - add_subwindow(rotation_block_h = new BlockSize(plugin, - x2 + title->get_w() + 10 + rotation_block_w->get_w(), - y, - &plugin->config.rotation_block_h)); - - y += 50; - add_subwindow(title = new BC_Title(x1, y, _("Translation search steps:"))); - add_subwindow(global_search_positions = new GlobalSearchPositions(plugin, - x1 + title->get_w() + 10, - y, - 80)); - global_search_positions->create_objects(); - - add_subwindow(title = new BC_Title(x2, y, _("Rotation search steps:"))); - add_subwindow(rotation_search_positions = new RotationSearchPositions(plugin, - x2 + title->get_w() + 10, - y, - 80)); - rotation_search_positions->create_objects(); +// add_subwindow(title = new BC_Title(x2, +// y, +// _("Rotation block size:\n(W/H Percent of image)"))); +// add_subwindow(rotation_block_w = new BlockSize(plugin, +// x2 + title->get_w() + 10, +// y, +// &plugin->config.rotation_block_w)); +// add_subwindow(rotation_block_h = new BlockSize(plugin, +// x2 + title->get_w() + 10 + rotation_block_w->get_w(), +// y, +// &plugin->config.rotation_block_h)); + +// y += 50; +// add_subwindow(title = new BC_Title(x1, y, _("Translation search steps:"))); +// add_subwindow(global_search_positions = new GlobalSearchPositions(plugin, +// x1 + title->get_w() + 10, +// y, +// 80)); +// global_search_positions->create_objects(); +// +// add_subwindow(title = new BC_Title(x2, y, _("Rotation search steps:"))); +// add_subwindow(rotation_search_positions = new RotationSearchPositions(plugin, +// x2 + title->get_w() + 10, +// y, +// 80)); +// rotation_search_positions->create_objects(); y += 50; add_subwindow(title = new BC_Title(x, y, _("Translation direction:"))); @@ -138,11 +133,11 @@ void MotionWindow::create_objects() y += 40; add_subwindow(title = new BC_Title(x, y + 10, _("Block X:"))); - add_subwindow(block_x = new MotionBlockX(plugin, + add_subwindow(block_x = new MotionHVBlockX(plugin, this, x + title->get_w() + 10, y)); - add_subwindow(block_x_text = new MotionBlockXText(plugin, + add_subwindow(block_x_text = new MotionHVBlockXText(plugin, this, x + title->get_w() + 10 + block_x->get_w() + 10, y + 10)); @@ -157,46 +152,46 @@ void MotionWindow::create_objects() int y1 = y; y += 50; add_subwindow(title = new BC_Title(x2, y + 10, _("Maximum angle offset:"))); - add_subwindow(rotate_magnitude = new MotionRMagnitude(plugin, + add_subwindow(rotate_magnitude = new MotionHVRMagnitude(plugin, x2 + title->get_w() + 10, y)); y += 40; add_subwindow(title = new BC_Title(x2, y + 10, _("Rotation settling speed:"))); - add_subwindow(rotate_return_speed = new MotionRReturnSpeed(plugin, + add_subwindow(rotate_return_speed = new MotionHVRReturnSpeed(plugin, x2 + title->get_w() + 10, y)); - + y = y1; y += 40; add_subwindow(title = new BC_Title(x, y + 10, _("Block Y:"))); - add_subwindow(block_y = new MotionBlockY(plugin, + add_subwindow(block_y = new MotionHVBlockY(plugin, this, x + title->get_w() + 10, y)); - add_subwindow(block_y_text = new MotionBlockYText(plugin, + add_subwindow(block_y_text = new MotionHVBlockYText(plugin, this, x + title->get_w() + 10 + block_y->get_w() + 10, y + 10)); y += 50; add_subwindow(title = new BC_Title(x, y + 10, _("Maximum absolute offset:"))); - add_subwindow(magnitude = new MotionMagnitude(plugin, + add_subwindow(magnitude = new MotionHVMagnitude(plugin, x + title->get_w() + 10, y)); y += 40; - add_subwindow(title = new BC_Title(x, y + 10, _("Settling speed:"))); - add_subwindow(return_speed = new MotionReturnSpeed(plugin, + add_subwindow(title = new BC_Title(x, y + 10, _("MotionHV settling speed:"))); + add_subwindow(return_speed = new MotionHVReturnSpeed(plugin, x + title->get_w() + 10, y)); y += 40; - add_subwindow(vectors = new MotionDrawVectors(plugin, + add_subwindow(vectors = new MotionHVDrawVectors(plugin, this, x, y)); @@ -258,11 +253,10 @@ void MotionWindow::create_objects() - show_window(); - flush(); + show_window(1); } -void MotionWindow::update_mode() +void MotionHVWindow::update_mode() { global_range_w->update(plugin->config.global_range_w, MIN_RADIUS, @@ -274,7 +268,7 @@ void MotionWindow::update_mode() MIN_ROTATION, MAX_ROTATION); vectors->update(plugin->config.draw_vectors); - global->update(plugin->config.global); +// global->update(plugin->config.global); rotate->update(plugin->config.rotate); } @@ -290,7 +284,7 @@ void MotionWindow::update_mode() -GlobalRange::GlobalRange(MotionMain *plugin, +GlobalRange::GlobalRange(MotionHVMain *plugin, int x, int y, int *value) @@ -315,7 +309,7 @@ int GlobalRange::handle_event() -RotationRange::RotationRange(MotionMain *plugin, +RotationRange::RotationRange(MotionHVMain *plugin, int x, int y) : BC_IPot(x, @@ -338,7 +332,7 @@ int RotationRange::handle_event() -RotationCenter::RotationCenter(MotionMain *plugin, +RotationCenter::RotationCenter(MotionHVMain *plugin, int x, int y) : BC_IPot(x, @@ -363,7 +357,7 @@ int RotationCenter::handle_event() -BlockSize::BlockSize(MotionMain *plugin, +BlockSize::BlockSize(MotionHVMain *plugin, int x, int y, int *value) @@ -397,90 +391,90 @@ int BlockSize::handle_event() -GlobalSearchPositions::GlobalSearchPositions(MotionMain *plugin, - int x, - int y, - int w) - : BC_PopupMenu(x, - y, - w, - "", - 1) -{ - this->plugin = plugin; -} -void GlobalSearchPositions::create_objects() -{ - add_item(new BC_MenuItem("16")); - add_item(new BC_MenuItem("32")); - add_item(new BC_MenuItem("64")); - add_item(new BC_MenuItem("128")); - add_item(new BC_MenuItem("256")); - add_item(new BC_MenuItem("512")); - add_item(new BC_MenuItem("1024")); - add_item(new BC_MenuItem("2048")); - add_item(new BC_MenuItem("4096")); - add_item(new BC_MenuItem("8192")); - add_item(new BC_MenuItem("16384")); - add_item(new BC_MenuItem("32768")); - add_item(new BC_MenuItem("65536")); - add_item(new BC_MenuItem("131072")); - char string[BCTEXTLEN]; - sprintf(string, "%d", plugin->config.global_positions); - set_text(string); -} - -int GlobalSearchPositions::handle_event() -{ - plugin->config.global_positions = atoi(get_text()); - plugin->send_configure_change(); - return 1; -} - - - - - - - -RotationSearchPositions::RotationSearchPositions(MotionMain *plugin, - int x, - int y, - int w) - : BC_PopupMenu(x, - y, - w, - "", - 1) -{ - this->plugin = plugin; -} -void RotationSearchPositions::create_objects() -{ - add_item(new BC_MenuItem("4")); - add_item(new BC_MenuItem("8")); - add_item(new BC_MenuItem("16")); - add_item(new BC_MenuItem("32")); - char string[BCTEXTLEN]; - sprintf(string, "%d", plugin->config.rotate_positions); - set_text(string); -} - -int RotationSearchPositions::handle_event() -{ - plugin->config.rotate_positions = atoi(get_text()); - plugin->send_configure_change(); - return 1; -} - - - - - - - - -MotionMagnitude::MotionMagnitude(MotionMain *plugin, +// GlobalSearchPositions::GlobalSearchPositions(MotionHVMain *plugin, +// int x, +// int y, +// int w) +// : BC_PopupMenu(x, +// y, +// w, +// "", +// 1) +// { +// this->plugin = plugin; +// } +// void GlobalSearchPositions::create_objects() +// { +// add_item(new BC_MenuItem("16")); +// add_item(new BC_MenuItem("32")); +// add_item(new BC_MenuItem("64")); +// add_item(new BC_MenuItem("128")); +// add_item(new BC_MenuItem("256")); +// add_item(new BC_MenuItem("512")); +// add_item(new BC_MenuItem("1024")); +// add_item(new BC_MenuItem("2048")); +// add_item(new BC_MenuItem("4096")); +// add_item(new BC_MenuItem("8192")); +// add_item(new BC_MenuItem("16384")); +// add_item(new BC_MenuItem("32768")); +// add_item(new BC_MenuItem("65536")); +// add_item(new BC_MenuItem("131072")); +// char string[BCTEXTLEN]; +// sprintf(string, "%d", plugin->config.global_positions); +// set_text(string); +// } +// +// int GlobalSearchPositions::handle_event() +// { +// plugin->config.global_positions = atoi(get_text()); +// plugin->send_configure_change(); +// return 1; +// } +// +// +// +// +// +// +// +// RotationSearchPositions::RotationSearchPositions(MotionHVMain *plugin, +// int x, +// int y, +// int w) +// : BC_PopupMenu(x, +// y, +// w, +// "", +// 1) +// { +// this->plugin = plugin; +// } +// void RotationSearchPositions::create_objects() +// { +// add_item(new BC_MenuItem("4")); +// add_item(new BC_MenuItem("8")); +// add_item(new BC_MenuItem("16")); +// add_item(new BC_MenuItem("32")); +// char string[BCTEXTLEN]; +// sprintf(string, "%d", plugin->config.rotate_positions); +// set_text(string); +// } +// +// int RotationSearchPositions::handle_event() +// { +// plugin->config.rotate_positions = atoi(get_text()); +// plugin->send_configure_change(); +// return 1; +// } + + + + + + + + +MotionHVMagnitude::MotionHVMagnitude(MotionHVMain *plugin, int x, int y) : BC_IPot(x, @@ -492,7 +486,7 @@ MotionMagnitude::MotionMagnitude(MotionMain *plugin, this->plugin = plugin; } -int MotionMagnitude::handle_event() +int MotionHVMagnitude::handle_event() { plugin->config.magnitude = (int)get_value(); plugin->send_configure_change(); @@ -500,7 +494,7 @@ int MotionMagnitude::handle_event() } -MotionReturnSpeed::MotionReturnSpeed(MotionMain *plugin, +MotionHVReturnSpeed::MotionHVReturnSpeed(MotionHVMain *plugin, int x, int y) : BC_IPot(x, @@ -512,7 +506,7 @@ MotionReturnSpeed::MotionReturnSpeed(MotionMain *plugin, this->plugin = plugin; } -int MotionReturnSpeed::handle_event() +int MotionHVReturnSpeed::handle_event() { plugin->config.return_speed = (int)get_value(); plugin->send_configure_change(); @@ -521,7 +515,7 @@ int MotionReturnSpeed::handle_event() -MotionRMagnitude::MotionRMagnitude(MotionMain *plugin, +MotionHVRMagnitude::MotionHVRMagnitude(MotionHVMain *plugin, int x, int y) : BC_IPot(x, @@ -533,7 +527,7 @@ MotionRMagnitude::MotionRMagnitude(MotionMain *plugin, this->plugin = plugin; } -int MotionRMagnitude::handle_event() +int MotionHVRMagnitude::handle_event() { plugin->config.rotate_magnitude = (int)get_value(); plugin->send_configure_change(); @@ -542,7 +536,7 @@ int MotionRMagnitude::handle_event() -MotionRReturnSpeed::MotionRReturnSpeed(MotionMain *plugin, +MotionHVRReturnSpeed::MotionHVRReturnSpeed(MotionHVMain *plugin, int x, int y) : BC_IPot(x, @@ -554,7 +548,7 @@ MotionRReturnSpeed::MotionRReturnSpeed(MotionMain *plugin, this->plugin = plugin; } -int MotionRReturnSpeed::handle_event() +int MotionHVRReturnSpeed::handle_event() { plugin->config.rotate_return_speed = (int)get_value(); plugin->send_configure_change(); @@ -565,28 +559,28 @@ int MotionRReturnSpeed::handle_event() -MotionGlobal::MotionGlobal(MotionMain *plugin, - MotionWindow *gui, - int x, - int y) - : BC_CheckBox(x, - y, - plugin->config.global, - _("Track translation")) -{ - this->plugin = plugin; - this->gui = gui; -} +// MotionHVGlobal::MotionHVGlobal(MotionHVMain *plugin, +// MotionHVWindow *gui, +// int x, +// int y) +// : BC_CheckBox(x, +// y, +// plugin->config.global, +// _("Track translation")) +// { +// this->plugin = plugin; +// this->gui = gui; +// } +// +// int MotionHVGlobal::handle_event() +// { +// plugin->config.global = get_value(); +// plugin->send_configure_change(); +// return 1; +// } -int MotionGlobal::handle_event() -{ - plugin->config.global = get_value(); - plugin->send_configure_change(); - return 1; -} - -MotionRotate::MotionRotate(MotionMain *plugin, - MotionWindow *gui, +MotionHVRotate::MotionHVRotate(MotionHVMain *plugin, + MotionHVWindow *gui, int x, int y) : BC_CheckBox(x, @@ -598,7 +592,7 @@ MotionRotate::MotionRotate(MotionMain *plugin, this->gui = gui; } -int MotionRotate::handle_event() +int MotionHVRotate::handle_event() { plugin->config.rotate = get_value(); plugin->send_configure_change(); @@ -609,8 +603,8 @@ int MotionRotate::handle_event() -MotionBlockX::MotionBlockX(MotionMain *plugin, - MotionWindow *gui, +MotionHVBlockX::MotionHVBlockX(MotionHVMain *plugin, + MotionHVWindow *gui, int x, int y) : BC_FPot(x, @@ -623,7 +617,7 @@ MotionBlockX::MotionBlockX(MotionMain *plugin, this->gui = gui; } -int MotionBlockX::handle_event() +int MotionHVBlockX::handle_event() { plugin->config.block_x = get_value(); gui->block_x_text->update((float)plugin->config.block_x); @@ -634,8 +628,8 @@ int MotionBlockX::handle_event() -MotionBlockY::MotionBlockY(MotionMain *plugin, - MotionWindow *gui, +MotionHVBlockY::MotionHVBlockY(MotionHVMain *plugin, + MotionHVWindow *gui, int x, int y) : BC_FPot(x, @@ -648,7 +642,7 @@ MotionBlockY::MotionBlockY(MotionMain *plugin, this->gui = gui; } -int MotionBlockY::handle_event() +int MotionHVBlockY::handle_event() { plugin->config.block_y = get_value(); gui->block_y_text->update((float)plugin->config.block_y); @@ -656,8 +650,8 @@ int MotionBlockY::handle_event() return 1; } -MotionBlockXText::MotionBlockXText(MotionMain *plugin, - MotionWindow *gui, +MotionHVBlockXText::MotionHVBlockXText(MotionHVMain *plugin, + MotionHVWindow *gui, int x, int y) : BC_TextBox(x, @@ -671,7 +665,7 @@ MotionBlockXText::MotionBlockXText(MotionMain *plugin, set_precision(4); } -int MotionBlockXText::handle_event() +int MotionHVBlockXText::handle_event() { plugin->config.block_x = atof(get_text()); gui->block_x->update(plugin->config.block_x); @@ -682,8 +676,8 @@ int MotionBlockXText::handle_event() -MotionBlockYText::MotionBlockYText(MotionMain *plugin, - MotionWindow *gui, +MotionHVBlockYText::MotionHVBlockYText(MotionHVMain *plugin, + MotionHVWindow *gui, int x, int y) : BC_TextBox(x, @@ -697,7 +691,7 @@ MotionBlockYText::MotionBlockYText(MotionMain *plugin, set_precision(4); } -int MotionBlockYText::handle_event() +int MotionHVBlockYText::handle_event() { plugin->config.block_y = atof(get_text()); gui->block_y->update(plugin->config.block_y); @@ -720,8 +714,8 @@ int MotionBlockYText::handle_event() -MotionDrawVectors::MotionDrawVectors(MotionMain *plugin, - MotionWindow *gui, +MotionHVDrawVectors::MotionHVDrawVectors(MotionHVMain *plugin, + MotionHVWindow *gui, int x, int y) : BC_CheckBox(x, @@ -733,7 +727,7 @@ MotionDrawVectors::MotionDrawVectors(MotionMain *plugin, this->plugin = plugin; } -int MotionDrawVectors::handle_event() +int MotionHVDrawVectors::handle_event() { plugin->config.draw_vectors = get_value(); plugin->send_configure_change(); @@ -747,13 +741,13 @@ int MotionDrawVectors::handle_event() -TrackSingleFrame::TrackSingleFrame(MotionMain *plugin, - MotionWindow *gui, +TrackSingleFrame::TrackSingleFrame(MotionHVMain *plugin, + MotionHVWindow *gui, int x, int y) : BC_Radial(x, y, - plugin->config.tracking_object == MotionScan::TRACK_SINGLE, + plugin->config.tracking_object == MotionHVScan::TRACK_SINGLE, _("Track single frame")) { this->plugin = plugin; @@ -762,7 +756,7 @@ TrackSingleFrame::TrackSingleFrame(MotionMain *plugin, int TrackSingleFrame::handle_event() { - plugin->config.tracking_object = MotionScan::TRACK_SINGLE; + plugin->config.tracking_object = MotionHVScan::TRACK_SINGLE; gui->track_previous->update(0); gui->previous_same->update(0); gui->track_frame_number->enable(); @@ -777,15 +771,15 @@ int TrackSingleFrame::handle_event() -TrackFrameNumber::TrackFrameNumber(MotionMain *plugin, - MotionWindow *gui, +TrackFrameNumber::TrackFrameNumber(MotionHVMain *plugin, + MotionHVWindow *gui, int x, int y) : BC_TextBox(x, y, 100, 1, plugin->config.track_frame) { this->plugin = plugin; this->gui = gui; - if(plugin->config.tracking_object != MotionScan::TRACK_SINGLE) disable(); + if(plugin->config.tracking_object != MotionHVScan::TRACK_SINGLE) disable(); } int TrackFrameNumber::handle_event() @@ -801,13 +795,13 @@ int TrackFrameNumber::handle_event() -TrackPreviousFrame::TrackPreviousFrame(MotionMain *plugin, - MotionWindow *gui, +TrackPreviousFrame::TrackPreviousFrame(MotionHVMain *plugin, + MotionHVWindow *gui, int x, int y) : BC_Radial(x, y, - plugin->config.tracking_object == MotionScan::TRACK_PREVIOUS, + plugin->config.tracking_object == MotionHVScan::TRACK_PREVIOUS, _("Track previous frame")) { this->plugin = plugin; @@ -815,7 +809,7 @@ TrackPreviousFrame::TrackPreviousFrame(MotionMain *plugin, } int TrackPreviousFrame::handle_event() { - plugin->config.tracking_object = MotionScan::TRACK_PREVIOUS; + plugin->config.tracking_object = MotionHVScan::TRACK_PREVIOUS; gui->track_single->update(0); gui->previous_same->update(0); gui->track_frame_number->disable(); @@ -830,13 +824,13 @@ int TrackPreviousFrame::handle_event() -PreviousFrameSameBlock::PreviousFrameSameBlock(MotionMain *plugin, - MotionWindow *gui, +PreviousFrameSameBlock::PreviousFrameSameBlock(MotionHVMain *plugin, + MotionHVWindow *gui, int x, int y) : BC_Radial(x, y, - plugin->config.tracking_object == MotionScan::PREVIOUS_SAME_BLOCK, + plugin->config.tracking_object == MotionHVScan::PREVIOUS_SAME_BLOCK, _("Previous frame same block")) { this->plugin = plugin; @@ -844,7 +838,7 @@ PreviousFrameSameBlock::PreviousFrameSameBlock(MotionMain *plugin, } int PreviousFrameSameBlock::handle_event() { - plugin->config.tracking_object = MotionScan::PREVIOUS_SAME_BLOCK; + plugin->config.tracking_object = MotionHVScan::PREVIOUS_SAME_BLOCK; gui->track_single->update(0); gui->track_previous->update(0); gui->track_frame_number->disable(); @@ -859,7 +853,7 @@ int PreviousFrameSameBlock::handle_event() -MasterLayer::MasterLayer(MotionMain *plugin, MotionWindow *gui, int x, int y) +MasterLayer::MasterLayer(MotionHVMain *plugin, MotionHVWindow *gui, int x, int y) : BC_PopupMenu(x, y, calculate_w(gui), @@ -893,7 +887,7 @@ char* MasterLayer::to_text(int mode) return mode ? _("Bottom") : _("Top"); } -int MasterLayer::calculate_w(MotionWindow *gui) +int MasterLayer::calculate_w(MotionHVWindow *gui) { int result = 0; result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(0))); @@ -908,7 +902,7 @@ int MasterLayer::calculate_w(MotionWindow *gui) -ActionType::ActionType(MotionMain *plugin, MotionWindow *gui, int x, int y) +ActionType::ActionType(MotionHVMain *plugin, MotionHVWindow *gui, int x, int y) : BC_PopupMenu(x, y, calculate_w(gui), @@ -927,52 +921,49 @@ int ActionType::handle_event() void ActionType::create_objects() { - add_item(new BC_MenuItem(to_text(MotionScan::TRACK))); - add_item(new BC_MenuItem(to_text(MotionScan::TRACK_PIXEL))); - add_item(new BC_MenuItem(to_text(MotionScan::STABILIZE))); - add_item(new BC_MenuItem(to_text(MotionScan::STABILIZE_PIXEL))); - add_item(new BC_MenuItem(to_text(MotionScan::NOTHING))); + add_item(new BC_MenuItem(to_text(MotionHVScan::TRACK))); + add_item(new BC_MenuItem(to_text(MotionHVScan::TRACK_PIXEL))); + add_item(new BC_MenuItem(to_text(MotionHVScan::STABILIZE))); + add_item(new BC_MenuItem(to_text(MotionHVScan::STABILIZE_PIXEL))); + add_item(new BC_MenuItem(to_text(MotionHVScan::NOTHING))); } int ActionType::from_text(char *text) { - if(!strcmp(text, _("Track Subpixel"))) return MotionScan::TRACK; - if(!strcmp(text, _("Track Pixel"))) return MotionScan::TRACK_PIXEL; - if(!strcmp(text, _("Stabilize Subpixel"))) return MotionScan::STABILIZE; - if(!strcmp(text, _("Stabilize Pixel"))) return MotionScan::STABILIZE_PIXEL; - if(!strcmp(text, _("Do Nothing"))) return MotionScan::NOTHING; + if(!strcmp(text, _("Track Subpixel"))) return MotionHVScan::TRACK; + if(!strcmp(text, _("Track Pixel"))) return MotionHVScan::TRACK_PIXEL; + if(!strcmp(text, _("Stabilize Subpixel"))) return MotionHVScan::STABILIZE; + if(!strcmp(text, _("Stabilize Pixel"))) return MotionHVScan::STABILIZE_PIXEL; + //if(!strcmp(text, _("Do Nothing"))) return MotionHVScan::NOTHING; + return MotionHVScan::NOTHING; } char* ActionType::to_text(int mode) { switch(mode) { - case MotionScan::TRACK: + case MotionHVScan::TRACK: return _("Track Subpixel"); - break; - case MotionScan::TRACK_PIXEL: + case MotionHVScan::TRACK_PIXEL: return _("Track Pixel"); - break; - case MotionScan::STABILIZE: + case MotionHVScan::STABILIZE: return _("Stabilize Subpixel"); - break; - case MotionScan::STABILIZE_PIXEL: + case MotionHVScan::STABILIZE_PIXEL: return _("Stabilize Pixel"); - break; - case MotionScan::NOTHING: + default: + case MotionHVScan::NOTHING: return _("Do Nothing"); - break; } } -int ActionType::calculate_w(MotionWindow *gui) +int ActionType::calculate_w(MotionHVWindow *gui) { int result = 0; - result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(MotionScan::TRACK))); - result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(MotionScan::TRACK_PIXEL))); - result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(MotionScan::STABILIZE))); - result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(MotionScan::STABILIZE_PIXEL))); - result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(MotionScan::NOTHING))); + result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(MotionHVScan::TRACK))); + result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(MotionHVScan::TRACK_PIXEL))); + result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(MotionHVScan::STABILIZE))); + result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(MotionHVScan::STABILIZE_PIXEL))); + result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(MotionHVScan::NOTHING))); return result + 50; } @@ -980,7 +971,7 @@ int ActionType::calculate_w(MotionWindow *gui) -TrackingType::TrackingType(MotionMain *plugin, MotionWindow *gui, int x, int y) +TrackingType::TrackingType(MotionHVMain *plugin, MotionHVWindow *gui, int x, int y) : BC_PopupMenu(x, y, calculate_w(gui), @@ -999,46 +990,44 @@ int TrackingType::handle_event() void TrackingType::create_objects() { - add_item(new BC_MenuItem(to_text(MotionScan::NO_CALCULATE))); - add_item(new BC_MenuItem(to_text(MotionScan::CALCULATE))); - add_item(new BC_MenuItem(to_text(MotionScan::SAVE))); - add_item(new BC_MenuItem(to_text(MotionScan::LOAD))); + add_item(new BC_MenuItem(to_text(MotionHVScan::NO_CALCULATE))); + add_item(new BC_MenuItem(to_text(MotionHVScan::CALCULATE))); + add_item(new BC_MenuItem(to_text(MotionHVScan::SAVE))); + add_item(new BC_MenuItem(to_text(MotionHVScan::LOAD))); } int TrackingType::from_text(char *text) { - if(!strcmp(text, _("Don't Calculate"))) return MotionScan::NO_CALCULATE; - if(!strcmp(text, _("Recalculate"))) return MotionScan::CALCULATE; - if(!strcmp(text, _("Save coords to /tmp"))) return MotionScan::SAVE; - if(!strcmp(text, _("Load coords from /tmp"))) return MotionScan::LOAD; + if(!strcmp(text, _("Save coords to /tmp"))) return MotionHVScan::SAVE; + if(!strcmp(text, _("Load coords from /tmp"))) return MotionHVScan::LOAD; + if(!strcmp(text, _("Recalculate"))) return MotionHVScan::CALCULATE; + //if(!strcmp(text, _("Don't Calculate"))) return MotionHVScan::NO_CALCULATE; + return MotionHVScan::NO_CALCULATE; } char* TrackingType::to_text(int mode) { switch(mode) { - case MotionScan::NO_CALCULATE: - return _("Don't Calculate"); - break; - case MotionScan::CALCULATE: - return _("Recalculate"); - break; - case MotionScan::SAVE: + case MotionHVScan::SAVE: return _("Save coords to /tmp"); - break; - case MotionScan::LOAD: + case MotionHVScan::LOAD: return _("Load coords from /tmp"); - break; + case MotionHVScan::CALCULATE: + return _("Recalculate"); + default: + case MotionHVScan::NO_CALCULATE: + return _("Don't Calculate"); } } -int TrackingType::calculate_w(MotionWindow *gui) +int TrackingType::calculate_w(MotionHVWindow *gui) { int result = 0; - result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(MotionScan::NO_CALCULATE))); - result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(MotionScan::CALCULATE))); - result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(MotionScan::SAVE))); - result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(MotionScan::LOAD))); + result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(MotionHVScan::NO_CALCULATE))); + result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(MotionHVScan::CALCULATE))); + result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(MotionHVScan::SAVE))); + result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(MotionHVScan::LOAD))); return result + 50; } @@ -1051,7 +1040,7 @@ int TrackingType::calculate_w(MotionWindow *gui) -TrackDirection::TrackDirection(MotionMain *plugin, MotionWindow *gui, int x, int y) +TrackDirection::TrackDirection(MotionHVMain *plugin, MotionHVWindow *gui, int x, int y) : BC_PopupMenu(x, y, calculate_w(gui), @@ -1090,7 +1079,7 @@ char* TrackDirection::to_text(int horizontal_only, int vertical_only) return _("Both"); } -int TrackDirection::calculate_w(MotionWindow *gui) +int TrackDirection::calculate_w(MotionHVWindow *gui) { int result = 0; result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(1, 0))); diff --git a/cinelerra-5.1/plugins/motion-hv/motionwindow-hv.h b/cinelerra-5.1/plugins/motion-hv/motionwindow-hv.h new file mode 100644 index 00000000..220b7426 --- /dev/null +++ b/cinelerra-5.1/plugins/motion-hv/motionwindow-hv.h @@ -0,0 +1,385 @@ + +/* + * CINELERRA + * Copyright (C) 2008 Adam Williams + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "guicast.h" +#include "motion-hv.inc" + +class MasterLayer : public BC_PopupMenu +{ +public: + MasterLayer(MotionHVMain *plugin, MotionHVWindow *gui, int x, int y); + int handle_event(); + void create_objects(); + static int calculate_w(MotionHVWindow *gui); + static int from_text(char *text); + static char* to_text(int mode); + MotionHVMain *plugin; + MotionHVWindow *gui; +}; + +class ActionType : public BC_PopupMenu +{ +public: + ActionType(MotionHVMain *plugin, MotionHVWindow *gui, int x, int y); + int handle_event(); + void create_objects(); + static int calculate_w(MotionHVWindow *gui); + static int from_text(char *text); + static char* to_text(int mode); + MotionHVMain *plugin; + MotionHVWindow *gui; +}; + +class TrackingType : public BC_PopupMenu +{ +public: + TrackingType(MotionHVMain *plugin, MotionHVWindow *gui, int x, int y); + int handle_event(); + void create_objects(); + static int calculate_w(MotionHVWindow *gui); + static int from_text(char *text); + static char* to_text(int mode); + MotionHVMain *plugin; + MotionHVWindow *gui; +}; + +class TrackDirection : public BC_PopupMenu +{ +public: + TrackDirection(MotionHVMain *plugin, MotionHVWindow *gui, int x, int y); + int handle_event(); + void create_objects(); + static int calculate_w(MotionHVWindow *gui); + static void from_text(int *horizontal_only, int *vertical_only, char *text); + static char* to_text(int horizontal_only, int vertical_only); + MotionHVMain *plugin; + MotionHVWindow *gui; +}; + + +class TrackSingleFrame : public BC_Radial +{ +public: + TrackSingleFrame(MotionHVMain *plugin, + MotionHVWindow *gui, + int x, + int y); + int handle_event(); + MotionHVMain *plugin; + MotionHVWindow *gui; +}; + +class TrackFrameNumber : public BC_TextBox +{ +public: + TrackFrameNumber(MotionHVMain *plugin, + MotionHVWindow *gui, + int x, + int y); + int handle_event(); + MotionHVMain *plugin; + MotionHVWindow *gui; +}; + +class TrackPreviousFrame : public BC_Radial +{ +public: + TrackPreviousFrame(MotionHVMain *plugin, + MotionHVWindow *gui, + int x, + int y); + int handle_event(); + MotionHVMain *plugin; + MotionHVWindow *gui; +}; + +class PreviousFrameSameBlock : public BC_Radial +{ +public: + PreviousFrameSameBlock(MotionHVMain *plugin, + MotionHVWindow *gui, + int x, + int y); + int handle_event(); + MotionHVMain *plugin; + MotionHVWindow *gui; +}; + +class GlobalRange : public BC_IPot +{ +public: + GlobalRange(MotionHVMain *plugin, + int x, + int y, + int *value); + int handle_event(); + MotionHVMain *plugin; + int *value; +}; + +class RotationRange : public BC_IPot +{ +public: + RotationRange(MotionHVMain *plugin, + int x, + int y); + int handle_event(); + MotionHVMain *plugin; +}; + +class RotationCenter : public BC_IPot +{ +public: + RotationCenter(MotionHVMain *plugin, + int x, + int y); + int handle_event(); + MotionHVMain *plugin; +}; + +class BlockSize : public BC_IPot +{ +public: + BlockSize(MotionHVMain *plugin, + int x, + int y, + int *value); + int handle_event(); + MotionHVMain *plugin; + int *value; +}; + +class MotionHVBlockX : public BC_FPot +{ +public: + MotionHVBlockX(MotionHVMain *plugin, + MotionHVWindow *gui, + int x, + int y); + int handle_event(); + MotionHVWindow *gui; + MotionHVMain *plugin; +}; + +class MotionHVBlockY : public BC_FPot +{ +public: + MotionHVBlockY(MotionHVMain *plugin, + MotionHVWindow *gui, + int x, + int y); + int handle_event(); + MotionHVWindow *gui; + MotionHVMain *plugin; +}; + +class MotionHVBlockXText : public BC_TextBox +{ +public: + MotionHVBlockXText(MotionHVMain *plugin, + MotionHVWindow *gui, + int x, + int y); + int handle_event(); + MotionHVWindow *gui; + MotionHVMain *plugin; +}; + +class MotionHVBlockYText : public BC_TextBox +{ +public: + MotionHVBlockYText(MotionHVMain *plugin, + MotionHVWindow *gui, + int x, + int y); + int handle_event(); + MotionHVWindow *gui; + MotionHVMain *plugin; +}; + +// class GlobalSearchPositions : public BC_PopupMenu +// { +// public: +// GlobalSearchPositions(MotionHVMain *plugin, +// int x, +// int y, +// int w); +// void create_objects(); +// int handle_event(); +// MotionHVMain *plugin; +// }; +// +// class RotationSearchPositions : public BC_PopupMenu +// { +// public: +// RotationSearchPositions(MotionHVMain *plugin, +// int x, +// int y, +// int w); +// void create_objects(); +// int handle_event(); +// MotionHVMain *plugin; +// }; + +class MotionHVMagnitude : public BC_IPot +{ +public: + MotionHVMagnitude(MotionHVMain *plugin, + int x, + int y); + int handle_event(); + MotionHVMain *plugin; +}; + +class MotionHVRMagnitude : public BC_IPot +{ +public: + MotionHVRMagnitude(MotionHVMain *plugin, + int x, + int y); + int handle_event(); + MotionHVMain *plugin; +}; + +class MotionHVReturnSpeed : public BC_IPot +{ +public: + MotionHVReturnSpeed(MotionHVMain *plugin, + int x, + int y); + int handle_event(); + MotionHVMain *plugin; +}; + + +class MotionHVRReturnSpeed : public BC_IPot +{ +public: + MotionHVRReturnSpeed(MotionHVMain *plugin, + int x, + int y); + int handle_event(); + MotionHVMain *plugin; +}; + + +class MotionHVDrawVectors : public BC_CheckBox +{ +public: + MotionHVDrawVectors(MotionHVMain *plugin, + MotionHVWindow *gui, + int x, + int y); + int handle_event(); + MotionHVMain *plugin; + MotionHVWindow *gui; +}; + +class AddTrackedFrameOffset : public BC_CheckBox +{ +public: + AddTrackedFrameOffset(MotionHVMain *plugin, + MotionHVWindow *gui, + int x, + int y); + int handle_event(); + MotionHVWindow *gui; + MotionHVMain *plugin; +}; + +// class MotionHVGlobal : public BC_CheckBox +// { +// public: +// MotionHVGlobal(MotionHVMain *plugin, +// MotionHVWindow *gui, +// int x, +// int y); +// int handle_event(); +// MotionHVWindow *gui; +// MotionHVMain *plugin; +// }; + +class MotionHVRotate : public BC_CheckBox +{ +public: + MotionHVRotate(MotionHVMain *plugin, + MotionHVWindow *gui, + int x, + int y); + int handle_event(); + MotionHVWindow *gui; + MotionHVMain *plugin; +}; + + + +class MotionHVWindow : public PluginClientWindow +{ +public: + MotionHVWindow(MotionHVMain *plugin); + ~MotionHVWindow(); + + void create_objects(); + void update_mode(); + char* get_radius_title(); + + GlobalRange *global_range_w; + GlobalRange *global_range_h; + RotationRange *rotation_range; + RotationCenter *rotation_center; + BlockSize *global_block_w; + BlockSize *global_block_h; + BlockSize *rotation_block_w; + BlockSize *rotation_block_h; + MotionHVBlockX *block_x; + MotionHVBlockY *block_y; + MotionHVBlockXText *block_x_text; + MotionHVBlockYText *block_y_text; +// GlobalSearchPositions *global_search_positions; +// RotationSearchPositions *rotation_search_positions; + MotionHVMagnitude *magnitude; + MotionHVRMagnitude *rotate_magnitude; + MotionHVReturnSpeed *return_speed; + MotionHVRReturnSpeed *rotate_return_speed; + ActionType *action_type; + MotionHVDrawVectors *vectors; +// MotionHVGlobal *global; + MotionHVRotate *rotate; + AddTrackedFrameOffset *addtrackedframeoffset; + TrackSingleFrame *track_single; + TrackFrameNumber *track_frame_number; + TrackPreviousFrame *track_previous; + PreviousFrameSameBlock *previous_same; + MasterLayer *master_layer; + TrackingType *tracking_type; + TrackDirection *track_direction; + + MotionHVMain *plugin; +}; + + + + + + + + + diff --git a/cinelerra-5.1/plugins/motion.new/motionwindow.inc b/cinelerra-5.1/plugins/motion-hv/motionwindow-hv.inc similarity index 89% rename from cinelerra-5.1/plugins/motion.new/motionwindow.inc rename to cinelerra-5.1/plugins/motion-hv/motionwindow-hv.inc index 8fbef467..160075ba 100644 --- a/cinelerra-5.1/plugins/motion.new/motionwindow.inc +++ b/cinelerra-5.1/plugins/motion-hv/motionwindow-hv.inc @@ -19,10 +19,10 @@ * */ -#ifndef MOTIONWINDOW_INC -#define MOTIONWINDOW_INC +#ifndef MOTIONWINDOWHV_INC +#define MOTIONWINDOWHV_INC -class MotionWindow; -class MotionThread; +class MotionHVWindow; +class MotionHVThread; #endif diff --git a/cinelerra-5.1/plugins/motion-hv/picon.png b/cinelerra-5.1/plugins/motion-hv/picon.png new file mode 100644 index 0000000000000000000000000000000000000000..01b282a69004b22338c7ef8beb2c0dfae1a79b91 GIT binary patch literal 4152 zcmV-85XbL{P)A(O055-AD zK~!ko-I{rHT}OS#KQs5fO?ppy+BaL4Ez6Rz6WiESP88zJKve#Hoy6ajT<)_eut9ecP#zR0kY>Hl#=G=RV-P$gsQ4aKwyl) z7>zN^D(mcXetF@njRCDOkSl94W6=gpwy!q1@96w@=uBNQnPOykgyE4r4in7L(RFaQR!rCi&5{pXI$jY4aK)|ybTh*-=+BH4%53Z*pK zD74Zjt*G?-CC;DgVqjosE+E^s*>V59w5)Dn<;s-^A%1IQVvIp4h0+R3 z_&E3WAlI#5ic$)#RIW{_3?QWy%a<=_WMqWr#S0`;DJI4zX98+%{XHrwDrjnIde0^* z5Fi$8$+an^5ZPbm87v{e0wDxKNTiWiQsYXAN+|%!%F5WhxfRE8IQqtMj4{)Inp;-U zxUzA^XC233a9|LfK}tEl*q%z!NR$(&-sGbn*+wWBnqJ{q zlbv0j(}%hBCBfVNw=6Yy^U6tS4Y_;`$s>mH)*=2GJEK8_y$8Rg}d zC@XE_p$7%MeQz-`GD2lV1upL5dML-X0+d!+(do|Mk!u1pPoI`m+exeQoOttPZfN}! zZrVd2Fhnd`gx?n;5*a5Lsvw>iz~j}dU9*$n(HH3I{s}9V?Lg`AEMtmmv=jum%Ei?# zN~=uy3i(R?Hmu)*t85M({w3?Ly91Bc#j+)?>!-VKAMw;->Xt}eKl*JPx1V^dlweUm zB}KQARxZ5*&#`jFCy-LkD?k?oBrMPe+$)A7`QyYj$0g%VCLlKA*neerd^a#xO(OS80}KOw1LsFL2TQiqP&dDeI6#FhjHC> zj#n`nt(mbfuO8k{QK$&V9bwhVb;J^v7#r2hhsa)xe}v2k2BqC=mu}g*rj@Iysc7KF zLN7fmCt<<%+a93*(ipZ3v$=9L8*f=o!ZAeChEzc%?L#p>y=5mx8oFE!z`4$0nwu*a z8r(u*z%V$Nrgp3tt^KT7y@exh9J>ObDU#DJTC16pW-ge80F{PoNX9F8D=ryx6@@-Y z*ym?xFb>it;+5cf5K<8I2}lUiNsSa1;|Yx`3 zy`8SwW=i+`GvD#VI6czO$mlTN_|hRR^`BuR`X-@xHI-#cDG0_$B;uTHKZ5JJ?|_Hq z%PO;jQ_qcQ#Z9il<7Rn4yA-8e)}#%I3mxcCB|#!2$41!Hkl+<}GoG-*Q>^fZ6&@48 zq!sRkQ(S-M3_~LWd~EAoOiWG?EU4qH&clR)^^}y#tCw=Qql1K@kpUvHNtAYZ@`umz zaPs>ZkBR7q0XWSYAtE;t(%fC}A?~WvIT5 zue{K~7k~K%uIk0M6t(^cA8RO~t*nk4*8M37_biSR=g4nf!%0?9TWim&hpxy&XV0C^ z*?Lo{tEUsc&yQ`{SeAv95-BY#%i;)`23`cos;6}DRSkIdCk1`=$gaE5JOj+waY-_xiQRf&wv4I~y^9Xls--*ZT zWqe|YaJcx|0SP%}i0WK@Pu~S3up$L-d%+bmx4d=KB9&6O1+cV1pkg6n9TB8$@c-A} zVSj!P&gNZgZvRVOUHczY1(W!sVQDGBoANxu?B;@=+g9<*rCdA~V;&4v_uN15Z`DscT)>nCOCgKDu1OtFd z877K~krfrh3nfbq{u|G4`2rs-vZ#IXui@66yj65FCO$%OVT{UhKk43XtePc^$1L{# z=s|`Hz3ja6n_L=xp8lS$=|kex0Lpc>vU&EgLBPNWeEC8IkAQV0u*oN>mJXG+kB_%c zqJX{Sg7Ju;!GUCBBb#1)5g`OMH8uEOxrfId+fVJ5&#+}n9nG$b<2a0ur#W=~BwOxo zq5ogLPVxR9^YQHuVH)nFdw3t;+w&;5-*7hzc?j%i--S%a_~C(Fw0EA#o1eHklFQ%7DM0VnlEQ$+Q=~H+=fJvU zf;P*~%a>Ceifh{AK#?TvID90K0kn6}u(j9_5Fk=1FoxZ`cLVV7!w&<{*jUV}Ri!wN z%gK`iJp1fLPMjE~rp8Z$>LLB&FW7PRAaSE+kpDtX9C ztSI1?TPoSKsfxNlKkJR}zr3R8gIr)@%c zVxfNm5^=*oxE$-In{xG$C99D|rHuaSIHeWK*m}!{=;^!6>!;51+I1U90Bu5ZYWsH5 zOS+wm>=zd%Yz6{fIs}BPxJJF7x=|<2RVN1H~=5nv=MRfEWcV>i&b5Vw|*H` zVIlF34&1&TJOzSZ2hx12%wfW_fS+tV(uOFuKpT5F}zWBE^G%RD!6MJwRhZQT9v-RtbG5pkC;;+7hH8D#1)L|^G8T1;u z!-_YpGzY58f)mc2)!vbmlTctwTm|W(X~+G99cDuNNR2wA;+|x9#vu(BX5-5`1lm!*exeF3-#>6vWk= zJ|Yj0UkHrXrsQMW_^Ss>NTpKv{eG<1PZ1i4k{B4}(D9c@rBc|ojn*1#*Pk%?_#O&7 z+qrpgkPq#+lfsr3f~!_B_=E4!|K@Q91~1_eaoz|ho~=;xnplgf6T~~S6kKUQXoM{h z5~L*%5(b->5jxp{^YTkn-LM&HWlahJR$&17sr!iRdj@s-G@`GQX!}WqdwLNb%}~HF zY{6k)iW8+QPF@SmPjHRL2azGjxY2}##5gGsOcEg2n2Gv z#iY|%@4udb>zf!o-%0xW|3k4i%@M!LtJQ`{@4LL&5aL>#B-4sDYH4h(iJm+`!ELu8 zvtXqMVOi(q}Pv_r%nW7JTfN*Oova}Q(kCC|C$K}^wA$s~GvdH0xQ5>o;*S;f* zDF15~+x;O}G6@BPm(i&tmd`iMi?*b%bv5zMekyt{6F+!>vHyJ<7;2+3pww@?uE z3l0YydP46K$m|^wT$@lpg0i8@=dv~hL(e=-<+g2zf`UBLMKIY^%UDgAK=%Nii@kU+ zbmQ7Cr%DyQ0Yh&Qq~&`ECMOuKVTX*F7N3J5=u?U%u`vdI@pH=C+VE9Z=QNfv1|9Iw zpB^JU)`clkbl7S7f{QD`cY{c94Lqcco^u3mFEzv_U6!R2jP831d;w%Ih%6{TMIz`( z1Phm#SI`k~nee~IAm-zTtJxu~b>_BHNc`RaB&ORgg<&-9vMeG9h$Q}K3Qu$dM?%7H zhzLzru|fYo-#6Qqhk)OY(rR%)Q@7CqK~;4%H#TqOsbfFLxq<-_J~-<$fI=GZxRCN( z*~WUWQi-pU+R|l|l$6rb+r2O#$8ks|lLP{O(gs$pXyn83+gZPSOU9XKbS@wwQ_dX9 zbbg=&nWJ`ofJT11Hb8!WhSu3&i|o4*K&Gsx#%Bg)#%ri3ui(Qs-$F8#KxdEjv3MLe z?al-=G&IEI#3Wvy7p>9=A=$8gBaY)_24;b1oxS*-vP#8lMQ3tlk*70em@B!{rpRgh z-1r5abN>P+qmv8{4dhrPGC4^+9-j&5*wN$E)zwj4l5yT?l}4#JI!lH+7yOXPvoDFS zbk={b($ua@rBkO)kxZsYrJR|7&Yo>&?R9IhZJVa%COla~a~g!s-r=Y@FP-o9`?nE& za^}n#PMCgG#2X7*gNMKnOfr3Dm)C^iHw9fJf2BS0HRAb1I;Ow}B zTuj0h`|2419aHsFF+j}f8;wR88yh1Yi*x2oJITBNCNWf18 zK*fxRk{Rtf8=PTs!AUtF=S@8#EBEpOWI^rN#sV2pWJ0^z+9CjSQqT^^E+!%O!70000!lvI6;RN#5=* z4F5rJ!QSPQfg+p*9+AZi4BSE>%y{W;-5;PJdx@v7EBi%eAueHtBXusTfI|MBE{-7; zx87bf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "clip.h" -//#include "../downsample/downsampleengine.h" -//#include "motion.h" -#include "motionscan.h" -#include "mutex.h" -#include "vframe.h" - -#include - -// The module which does the actual scanning - -MotionScanPackage::MotionScanPackage() - : LoadPackage() -{ - valid = 1; -} - - - - - - -MotionScanUnit::MotionScanUnit(MotionScan *server) - : LoadClient(server) -{ - this->server = server; - cache_lock = new Mutex("MotionScanUnit::cache_lock"); -} - -MotionScanUnit::~MotionScanUnit() -{ - delete cache_lock; -} - - - -void MotionScanUnit::process_package(LoadPackage *package) -{ - MotionScanPackage *pkg = (MotionScanPackage*)package; - int w = server->current_frame->get_w(); - int h = server->current_frame->get_h(); - int color_model = server->current_frame->get_color_model(); - int pixel_size = BC_CModels::calculate_pixelsize(color_model); - int row_bytes = server->current_frame->get_bytes_per_line(); - - - - - - - - - - - - -// Single pixel - if(!server->subpixel) - { -// Try cache - pkg->difference1 = server->get_cache(pkg->search_x, pkg->search_y); - if(pkg->difference1 < 0) - { -//printf("MotionScanUnit::process_package 1 search_x=%d search_y=%d scan_x1=%d scan_y1=%d scan_x2=%d scan_y2=%d x_steps=%d y_steps=%d\n", -//pkg->search_x, pkg->search_y, pkg->scan_x1, pkg->scan_y1, pkg->scan_x2, pkg->scan_y2, server->x_steps, server->y_steps); -// Pointers to first pixel in each block - unsigned char *prev_ptr = server->previous_frame->get_rows()[ - pkg->search_y] + - pkg->search_x * pixel_size; - unsigned char *current_ptr = server->current_frame->get_rows()[ - pkg->block_y1] + - pkg->block_x1 * pixel_size; - -// Scan block - pkg->difference1 = MotionScan::abs_diff(prev_ptr, - current_ptr, - row_bytes, - pkg->block_x2 - pkg->block_x1, - pkg->block_y2 - pkg->block_y1, - color_model); - -// printf("MotionScanUnit::process_package %d search_x=%d search_y=%d diff=%lld\n", -// __LINE__, server->block_x1 - pkg->search_x, server->block_y1 - pkg->search_y, pkg->difference1); - server->put_cache(pkg->search_x, pkg->search_y, pkg->difference1); - } - } - - - - - - - - else - - - - - - - - -// Sub pixel - { - unsigned char *prev_ptr = server->previous_frame->get_rows()[ - pkg->search_y] + - pkg->search_x * pixel_size; - unsigned char *current_ptr = server->current_frame->get_rows()[ - pkg->block_y1] + - pkg->block_x1 * pixel_size; - -// With subpixel, there are two ways to compare each position, one by shifting -// the previous frame and two by shifting the current frame. - pkg->difference1 = MotionScan::abs_diff_sub(prev_ptr, - current_ptr, - row_bytes, - pkg->block_x2 - pkg->block_x1, - pkg->block_y2 - pkg->block_y1, - color_model, - pkg->sub_x, - pkg->sub_y); - pkg->difference2 = MotionScan::abs_diff_sub(current_ptr, - prev_ptr, - row_bytes, - pkg->block_x2 - pkg->block_x1, - pkg->block_y2 - pkg->block_y1, - color_model, - pkg->sub_x, - pkg->sub_y); -// printf("MotionScanUnit::process_package sub_x=%d sub_y=%d search_x=%d search_y=%d diff1=%lld diff2=%lld\n", -// sub_x, -// sub_y, -// search_x, -// search_y, -// pkg->difference1, -// pkg->difference2); - } - - - - -} - - - - - - - - - - -int64_t MotionScanUnit::get_cache(int x, int y) -{ - int64_t result = -1; - cache_lock->lock("MotionScanUnit::get_cache"); - for(int i = 0; i < cache.total; i++) - { - MotionScanCache *ptr = cache.values[i]; - if(ptr->x == x && ptr->y == y) - { - result = ptr->difference; - break; - } - } - cache_lock->unlock(); - return result; -} - -void MotionScanUnit::put_cache(int x, int y, int64_t difference) -{ - MotionScanCache *ptr = new MotionScanCache(x, y, difference); - cache_lock->lock("MotionScanUnit::put_cache"); - cache.append(ptr); - cache_lock->unlock(); -} - - - - - - - - - - - -MotionScan::MotionScan(int total_clients, - int total_packages) - : LoadServer( -//1, 1 -total_clients, total_packages -) -{ - test_match = 1; - cache_lock = new Mutex("MotionScan::cache_lock"); - downsampled_previous = 0; - downsampled_current = 0; -// downsample = 0; -} - -MotionScan::~MotionScan() -{ - delete cache_lock; - delete downsampled_previous; - delete downsampled_current; -// delete downsample; -} - - -void MotionScan::init_packages() -{ -// Set package coords -//printf("MotionScan::init_packages %d %d\n", __LINE__, get_total_packages()); - for(int i = 0; i < get_total_packages(); i++) - { - MotionScanPackage *pkg = (MotionScanPackage*)get_package(i); - - pkg->block_x1 = block_x1; - pkg->block_x2 = block_x2; - pkg->block_y1 = block_y1; - pkg->block_y2 = block_y2; - pkg->scan_x1 = scan_x1; - pkg->scan_x2 = scan_x2; - pkg->scan_y1 = scan_y1; - pkg->scan_y2 = scan_y2; - pkg->step = i; - pkg->difference1 = 0; - pkg->difference2 = 0; - pkg->dx = 0; - pkg->dy = 0; - pkg->valid = 1; - - if(!subpixel) - { - pkg->search_x = pkg->scan_x1 + (pkg->step % x_steps) * - (scan_x2 - scan_x1) / x_steps; - pkg->search_y = pkg->scan_y1 + (pkg->step / x_steps) * - (scan_y2 - scan_y1) / y_steps; - pkg->sub_x = 0; - pkg->sub_y = 0; - } - else - { - pkg->sub_x = pkg->step % (OVERSAMPLE * 2); - pkg->sub_y = pkg->step / (OVERSAMPLE * 2); - - if(horizontal_only) - { - pkg->sub_y = 0; - } - - if(vertical_only) - { - pkg->sub_x = 0; - } - - pkg->search_x = pkg->scan_x1 + pkg->sub_x / OVERSAMPLE + 1; - pkg->search_y = pkg->scan_y1 + pkg->sub_y / OVERSAMPLE + 1; - pkg->sub_x %= OVERSAMPLE; - pkg->sub_y %= OVERSAMPLE; - - - -// printf("MotionScan::init_packages %d i=%d search_x=%d search_y=%d sub_x=%d sub_y=%d\n", -// __LINE__, -// i, -// pkg->search_x, -// pkg->search_y, -// pkg->sub_x, -// pkg->sub_y); - } - -// printf("MotionScan::init_packages %d %d,%d %d,%d %d,%d\n", -// __LINE__, -// scan_x1, -// scan_x2, -// scan_y1, -// scan_y2, -// pkg->search_x, -// pkg->search_y); - } -} - -LoadClient* MotionScan::new_client() -{ - return new MotionScanUnit(this); -} - -LoadPackage* MotionScan::new_package() -{ - return new MotionScanPackage; -} - - -void MotionScan::set_test_match(int value) -{ - this->test_match = value; -} - -void MotionScan::scan_frame(VFrame *previous_frame, - VFrame *current_frame, - int global_range_w, - int global_range_h, - int global_block_w, - int global_block_h, - double block_x, - double block_y, - int frame_type, - int tracking_type, - int action_type, - int horizontal_only, - int vertical_only, - int source_position, - int total_steps, - int total_dx, - int total_dy, - int global_origin_x, - int global_origin_y) -{ - this->previous_frame_arg = previous_frame; - this->current_frame_arg = current_frame; - this->horizontal_only = horizontal_only; - this->vertical_only = vertical_only; - this->previous_frame = previous_frame_arg; - this->current_frame = current_frame_arg; - this->global_origin_x = global_origin_x; - this->global_origin_y = global_origin_y; - subpixel = 0; - - cache.remove_all_objects(); - -// Single macroblock - int w = current_frame->get_w(); - int h = current_frame->get_h(); - -// Initial search parameters - int scan_w = w * global_range_w / 100; - int scan_h = h * global_range_h / 100; - int block_w = w * global_block_w / 100; - int block_h = h * global_block_h / 100; - -// Location of block in previous frame - block_x1 = (int)(w * block_x / 100 - block_w / 2); - block_y1 = (int)(h * block_y / 100 - block_h / 2); - block_x2 = (int)(w * block_x / 100 + block_w / 2); - block_y2 = (int)(h * block_y / 100 + block_h / 2); - -// Offset to location of previous block. This offset needn't be very accurate -// since it's the offset of the previous image and current image we want. - if(frame_type == MotionScan::TRACK_PREVIOUS) - { - block_x1 += total_dx / OVERSAMPLE; - block_y1 += total_dy / OVERSAMPLE; - block_x2 += total_dx / OVERSAMPLE; - block_y2 += total_dy / OVERSAMPLE; - } - - skip = 0; - - switch(tracking_type) - { -// Don't calculate - case MotionScan::NO_CALCULATE: - dx_result = 0; - dy_result = 0; - skip = 1; - break; - - case MotionScan::LOAD: - { -//printf("MotionScan::scan_frame %d\n", __LINE__); -// Load result from disk - char string[BCTEXTLEN]; - sprintf(string, "%s%06d", - MOTION_FILE, - source_position); - FILE *input = fopen(string, "r"); - if(input) - { - fscanf(input, - "%d %d", - &dx_result, - &dy_result); - fclose(input); - skip = 1; - } - break; - } - -// Scan from scratch - default: - skip = 0; - break; - } - - if(!skip && test_match) - { - if(previous_frame->data_matches(current_frame)) - { -printf("MotionScan::scan_frame: data matches. skipping.\n"); - dx_result = 0; - dy_result = 0; - skip = 1; - } - } - -// Perform scan - if(!skip) - { -//printf("MotionScan::scan_frame %d\n", __LINE__); -// Location of block in current frame - int origin_offset_x = this->global_origin_x * w / 100; - int origin_offset_y = this->global_origin_y * h / 100; - int x_result = block_x1 + origin_offset_x; - int y_result = block_y1 + origin_offset_y; - -// printf("MotionScan::scan_frame 1 %d %d %d %d %d %d %d %d\n", -// block_x1 + block_w / 2, -// block_y1 + block_h / 2, -// block_w, -// block_h, -// block_x1, -// block_y1, -// block_x2, -// block_y2); - - while(1) - { -// Cache needs to be cleared if downsampling is used because the sums of -// different downsamplings can't be compared. -// Subpixel never uses the cache. -// cache.remove_all_objects(); - scan_x1 = x_result - scan_w / 2; - scan_y1 = y_result - scan_h / 2; - scan_x2 = x_result + scan_w / 2; - scan_y2 = y_result + scan_h / 2; - - - -// Zero out requested values - if(horizontal_only) - { - scan_y1 = block_y1; - scan_y2 = block_y1 + 1; - } - if(vertical_only) - { - scan_x1 = block_x1; - scan_x2 = block_x1 + 1; - } - -// printf("MotionScan::scan_frame 1 %d %d %d %d %d %d %d %d\n", -// block_x1, -// block_y1, -// block_x2, -// block_y2, -// scan_x1, -// scan_y1, -// scan_x2, -// scan_y2); -// Clamp the block coords before the scan so we get useful scan coords. - clamp_scan(w, - h, - &block_x1, - &block_y1, - &block_x2, - &block_y2, - &scan_x1, - &scan_y1, - &scan_x2, - &scan_y2, - 0); -// printf("MotionScan::scan_frame 1 %d block_x1=%d block_y1=%d block_x2=%d block_y2=%d\n scan_x1=%d scan_y1=%d scan_x2=%d scan_y2=%d\n x_result=%d y_result=%d\n", -// __LINE__, -// block_x1, -// block_y1, -// block_x2, -// block_y2, -// scan_x1, -// scan_y1, -// scan_x2, -// scan_y2, -// x_result, -// y_result); - - -// Give up if invalid coords. - if(scan_y2 <= scan_y1 || - scan_x2 <= scan_x1 || - block_x2 <= block_x1 || - block_y2 <= block_y1) - break; - -// For subpixel, the top row and left column are skipped - if(subpixel) - { - -//printf("MotionScan::scan_frame %d %d %d\n", __LINE__, x_result, y_result); -// Scan every subpixel in a 2 pixel * 2 pixel square - total_pixels = (2 * OVERSAMPLE) * (2 * OVERSAMPLE); - - this->total_steps = total_pixels; -// These aren't used in subpixel - this->x_steps = OVERSAMPLE * 2; - this->y_steps = OVERSAMPLE * 2; - - set_package_count(this->total_steps); - process_packages(); - -// Get least difference - int64_t min_difference = -1; - for(int i = 0; i < get_total_packages(); i++) - { - MotionScanPackage *pkg = (MotionScanPackage*)get_package(i); -//printf("MotionScan::scan_frame %d search_x=%d search_y=%d sub_x=%d sub_y=%d diff1=%lld diff2=%lld\n", -//__LINE__, pkg->search_x, pkg->search_y, pkg->sub_x, pkg->sub_y, pkg->difference1, pkg->difference2); - if(pkg->difference1 < min_difference || min_difference == -1) - { - min_difference = pkg->difference1; - -// The sub coords are 1 pixel up & left of the block coords - x_result = pkg->search_x * OVERSAMPLE + pkg->sub_x; - y_result = pkg->search_y * OVERSAMPLE + pkg->sub_y; - - -// Fill in results - dx_result = block_x1 * OVERSAMPLE - x_result; - dy_result = block_y1 * OVERSAMPLE - y_result; -//printf("MotionScan::scan_frame %d dx_result=%d dy_result=%d diff=%lld\n", -//__LINE__, dx_result, dy_result, min_difference); - } - - if(pkg->difference2 < min_difference) - { - min_difference = pkg->difference2; - - x_result = pkg->search_x * OVERSAMPLE - pkg->sub_x; - y_result = pkg->search_y * OVERSAMPLE - pkg->sub_y; - - dx_result = block_x1 * OVERSAMPLE - x_result; - dy_result = block_y1 * OVERSAMPLE - y_result; -//printf("MotionScan::scan_frame %d dx_result=%d dy_result=%d diff=%lld\n", -//__LINE__, dx_result, dy_result, min_difference); - } - } - - break; - } - else -// Single pixel - { - total_pixels = (scan_x2 - scan_x1) * (scan_y2 - scan_y1); - this->total_steps = MIN(total_steps, total_pixels); - - if(this->total_steps == total_pixels) - { - x_steps = scan_x2 - scan_x1; - y_steps = scan_y2 - scan_y1; - } - else - { - x_steps = (int)sqrt(this->total_steps); - y_steps = (int)sqrt(this->total_steps); - } - -// Use downsampled images -// if(scan_x2 - scan_x1 > x_steps * 4 || -// scan_y2 - scan_y1 > y_steps * 4) -// { -// printf("MotionScan::scan_frame %d total_pixels=%d total_steps=%d x_steps=%d y_steps=%d x y steps=%d\n", -// __LINE__, -// total_pixels, -// total_steps, -// x_steps, -// y_steps, -// x_steps * y_steps); -// -// if(!downsampled_previous || -// !downsampled_previous->equivalent(previous_frame_arg)) -// { -// delete downsampled_previous; -// downsampled_previous = new VFrame(*previous_frame_arg); -// } -// -// if(!downsampled_current || -// !downsampled_current->equivalent(current_frame_arg)) -// { -// delete downsampled_current; -// downsampled_current = new VFrame(*current_frame_arg); -// } -// -// -// if(!downsample) -// downsample = new DownSampleServer(get_total_clients(), -// get_total_clients()); -// downsample->process_frame(downsampled_previous, -// previous_frame_arg, -// 1, -// 1, -// 1, -// 1, -// (scan_y2 - scan_y1) / y_steps, -// (scan_x2 - scan_x1) / x_steps, -// 0, -// 0); -// downsample->process_frame(downsampled_current, -// current_frame_arg, -// 1, -// 1, -// 1, -// 1, -// (scan_y2 - scan_y1) / y_steps, -// (scan_x2 - scan_x1) / x_steps, -// 0, -// 0); -// this->previous_frame = downsampled_previous; -// this->current_frame = downsampled_current; -// } - - - - - -// printf("MotionScan::scan_frame %d this->total_steps=%d\n", -// __LINE__, -// this->total_steps); - - - set_package_count(this->total_steps); - process_packages(); - -// Get least difference - int64_t min_difference = -1; - for(int i = 0; i < get_total_packages(); i++) - { - MotionScanPackage *pkg = (MotionScanPackage*)get_package(i); -//printf("MotionScan::scan_frame %d search_x=%d search_y=%d sub_x=%d sub_y=%d diff1=%lld diff2=%lld\n", -//__LINE__, pkg->search_x, pkg->search_y, pkg->sub_x, pkg->sub_y, pkg->difference1, pkg->difference2); - if(pkg->difference1 < min_difference || min_difference == -1) - { - min_difference = pkg->difference1; - x_result = pkg->search_x; - y_result = pkg->search_y; - x_result *= OVERSAMPLE; - y_result *= OVERSAMPLE; -//printf("MotionScan::scan_frame %d x_result=%d y_result=%d diff=%lld\n", -//__LINE__, block_x1 * OVERSAMPLE - x_result, block_y1 * OVERSAMPLE - y_result, pkg->difference1); - } - } - - -// If a new search is required, rescale results back to pixels. - if(this->total_steps >= total_pixels) - { -// Single pixel accuracy reached. Now do exhaustive subpixel search. - if(action_type == MotionScan::STABILIZE || - action_type == MotionScan::TRACK || - action_type == MotionScan::NOTHING) - { -//printf("MotionScan::scan_frame %d %d %d\n", __LINE__, x_result, y_result); - x_result /= OVERSAMPLE; - y_result /= OVERSAMPLE; - scan_w = 2; - scan_h = 2; - subpixel = 1; - } - else - { -// Fill in results and quit - dx_result = block_x1 * OVERSAMPLE - x_result; - dy_result = block_y1 * OVERSAMPLE - y_result; -//printf("MotionScan::scan_frame %d %d %d\n", __LINE__, dx_result, dy_result); - break; - } - } - else -// Reduce scan area and try again - { - scan_w = (scan_x2 - scan_x1) / 2; - scan_h = (scan_y2 - scan_y1) / 2; - x_result /= OVERSAMPLE; - y_result /= OVERSAMPLE; - } - } - } - - dx_result *= -1; - dy_result *= -1; - } -//printf("MotionScan::scan_frame %d\n", __LINE__); - - - if(vertical_only) dx_result = 0; - if(horizontal_only) dy_result = 0; - - - -// Write results - if(tracking_type == MotionScan::SAVE) - { - char string[BCTEXTLEN]; - sprintf(string, - "%s%06d", - MOTION_FILE, - source_position); - FILE *output = fopen(string, "w"); - if(output) - { - fprintf(output, - "%d %d\n", - dx_result, - dy_result); - fclose(output); - } - else - { - printf("MotionScan::scan_frame %d: save coordinate failed", __LINE__); - } - } - -// printf("MotionScan::scan_frame %d dx=%.2f dy=%.2f\n", -// __LINE__, -// (float)this->dx_result / OVERSAMPLE, -// (float)this->dy_result / OVERSAMPLE); -} - - - - - - - - - - - - - - - - - -int64_t MotionScan::get_cache(int x, int y) -{ - int64_t result = -1; - cache_lock->lock("MotionScan::get_cache"); - for(int i = 0; i < cache.total; i++) - { - MotionScanCache *ptr = cache.values[i]; - if(ptr->x == x && ptr->y == y) - { - result = ptr->difference; - break; - } - } - cache_lock->unlock(); - return result; -} - -void MotionScan::put_cache(int x, int y, int64_t difference) -{ - MotionScanCache *ptr = new MotionScanCache(x, y, difference); - cache_lock->lock("MotionScan::put_cache"); - cache.append(ptr); - cache_lock->unlock(); -} - - - -#define ABS_DIFF(type, temp_type, multiplier, components) \ -{ \ - temp_type result_temp = 0; \ - for(int i = 0; i < h; i++) \ - { \ - type *prev_row = (type*)prev_ptr; \ - type *current_row = (type*)current_ptr; \ - for(int j = 0; j < w; j++) \ - { \ - for(int k = 0; k < 3; k++) \ - { \ - temp_type difference; \ - difference = *prev_row++ - *current_row++; \ - if(difference < 0) \ - result_temp -= difference; \ - else \ - result_temp += difference; \ - } \ - if(components == 4) \ - { \ - prev_row++; \ - current_row++; \ - } \ - } \ - prev_ptr += row_bytes; \ - current_ptr += row_bytes; \ - } \ - result = (int64_t)(result_temp * multiplier); \ -} - -int64_t MotionScan::abs_diff(unsigned char *prev_ptr, - unsigned char *current_ptr, - int row_bytes, - int w, - int h, - int color_model) -{ - int64_t result = 0; - switch(color_model) - { - case BC_RGB888: - ABS_DIFF(unsigned char, int64_t, 1, 3) - break; - case BC_RGBA8888: - ABS_DIFF(unsigned char, int64_t, 1, 4) - break; - case BC_RGB_FLOAT: - ABS_DIFF(float, double, 0x10000, 3) - break; - case BC_RGBA_FLOAT: - ABS_DIFF(float, double, 0x10000, 4) - break; - case BC_YUV888: - ABS_DIFF(unsigned char, int64_t, 1, 3) - break; - case BC_YUVA8888: - ABS_DIFF(unsigned char, int64_t, 1, 4) - break; - case BC_YUV161616: - ABS_DIFF(uint16_t, int64_t, 1, 3) - break; - case BC_YUVA16161616: - ABS_DIFF(uint16_t, int64_t, 1, 4) - break; - } - return result; -} - - - -#define ABS_DIFF_SUB(type, temp_type, multiplier, components) \ -{ \ - temp_type result_temp = 0; \ - temp_type y2_fraction = sub_y * 0x100 / OVERSAMPLE; \ - temp_type y1_fraction = 0x100 - y2_fraction; \ - temp_type x2_fraction = sub_x * 0x100 / OVERSAMPLE; \ - temp_type x1_fraction = 0x100 - x2_fraction; \ - for(int i = 0; i < h_sub; i++) \ - { \ - type *prev_row1 = (type*)prev_ptr; \ - type *prev_row2 = (type*)prev_ptr + components; \ - type *prev_row3 = (type*)(prev_ptr + row_bytes); \ - type *prev_row4 = (type*)(prev_ptr + row_bytes) + components; \ - type *current_row = (type*)current_ptr; \ - for(int j = 0; j < w_sub; j++) \ - { \ -/* Scan each component */ \ - for(int k = 0; k < 3; k++) \ - { \ - temp_type difference; \ - temp_type prev_value = \ - (*prev_row1++ * x1_fraction * y1_fraction + \ - *prev_row2++ * x2_fraction * y1_fraction + \ - *prev_row3++ * x1_fraction * y2_fraction + \ - *prev_row4++ * x2_fraction * y2_fraction) / \ - 0x100 / 0x100; \ - temp_type current_value = *current_row++; \ - difference = prev_value - current_value; \ - if(difference < 0) \ - result_temp -= difference; \ - else \ - result_temp += difference; \ - } \ - \ -/* skip alpha */ \ - if(components == 4) \ - { \ - prev_row1++; \ - prev_row2++; \ - prev_row3++; \ - prev_row4++; \ - current_row++; \ - } \ - } \ - prev_ptr += row_bytes; \ - current_ptr += row_bytes; \ - } \ - result = (int64_t)(result_temp * multiplier); \ -} - - - - -int64_t MotionScan::abs_diff_sub(unsigned char *prev_ptr, - unsigned char *current_ptr, - int row_bytes, - int w, - int h, - int color_model, - int sub_x, - int sub_y) -{ - int h_sub = h - 1; - int w_sub = w - 1; - int64_t result = 0; - - switch(color_model) - { - case BC_RGB888: - ABS_DIFF_SUB(unsigned char, int64_t, 1, 3) - break; - case BC_RGBA8888: - ABS_DIFF_SUB(unsigned char, int64_t, 1, 4) - break; - case BC_RGB_FLOAT: - ABS_DIFF_SUB(float, double, 0x10000, 3) - break; - case BC_RGBA_FLOAT: - ABS_DIFF_SUB(float, double, 0x10000, 4) - break; - case BC_YUV888: - ABS_DIFF_SUB(unsigned char, int64_t, 1, 3) - break; - case BC_YUVA8888: - ABS_DIFF_SUB(unsigned char, int64_t, 1, 4) - break; - case BC_YUV161616: - ABS_DIFF_SUB(uint16_t, int64_t, 1, 3) - break; - case BC_YUVA16161616: - ABS_DIFF_SUB(uint16_t, int64_t, 1, 4) - break; - } - return result; -} - - - - - -MotionScanCache::MotionScanCache(int x, int y, int64_t difference) -{ - this->x = x; - this->y = y; - this->difference = difference; -} - - - -void MotionScan::clamp_scan(int w, - int h, - int *block_x1, - int *block_y1, - int *block_x2, - int *block_y2, - int *scan_x1, - int *scan_y1, - int *scan_x2, - int *scan_y2, - int use_absolute) -{ -// printf("MotionMain::clamp_scan 1 w=%d h=%d block=%d %d %d %d scan=%d %d %d %d absolute=%d\n", -// w, -// h, -// *block_x1, -// *block_y1, -// *block_x2, -// *block_y2, -// *scan_x1, -// *scan_y1, -// *scan_x2, -// *scan_y2, -// use_absolute); - - if(use_absolute) - { -// scan is always out of range before block. - if(*scan_x1 < 0) - { - int difference = -*scan_x1; - *block_x1 += difference; - *scan_x1 = 0; - } - - if(*scan_y1 < 0) - { - int difference = -*scan_y1; - *block_y1 += difference; - *scan_y1 = 0; - } - - if(*scan_x2 > w) - { - int difference = *scan_x2 - w; - *block_x2 -= difference; - *scan_x2 -= difference; - } - - if(*scan_y2 > h) - { - int difference = *scan_y2 - h; - *block_y2 -= difference; - *scan_y2 -= difference; - } - - CLAMP(*scan_x1, 0, w); - CLAMP(*scan_y1, 0, h); - CLAMP(*scan_x2, 0, w); - CLAMP(*scan_y2, 0, h); - } - else - { - if(*scan_x1 < 0) - { - int difference = -*scan_x1; - *block_x1 += difference; - *scan_x2 += difference; - *scan_x1 = 0; - } - - if(*scan_y1 < 0) - { - int difference = -*scan_y1; - *block_y1 += difference; - *scan_y2 += difference; - *scan_y1 = 0; - } - - if(*scan_x2 - *block_x1 + *block_x2 > w) - { - int difference = *scan_x2 - *block_x1 + *block_x2 - w; - *block_x2 -= difference; - } - - if(*scan_y2 - *block_y1 + *block_y2 > h) - { - int difference = *scan_y2 - *block_y1 + *block_y2 - h; - *block_y2 -= difference; - } - -// CLAMP(*scan_x1, 0, w - (*block_x2 - *block_x1)); -// CLAMP(*scan_y1, 0, h - (*block_y2 - *block_y1)); -// CLAMP(*scan_x2, 0, w - (*block_x2 - *block_x1)); -// CLAMP(*scan_y2, 0, h - (*block_y2 - *block_y1)); - } - -// Sanity checks which break the calculation but should never happen if the -// center of the block is inside the frame. - CLAMP(*block_x1, 0, w); - CLAMP(*block_x2, 0, w); - CLAMP(*block_y1, 0, h); - CLAMP(*block_y2, 0, h); - -// printf("MotionMain::clamp_scan 2 w=%d h=%d block=%d %d %d %d scan=%d %d %d %d absolute=%d\n", -// w, -// h, -// *block_x1, -// *block_y1, -// *block_x2, -// *block_y2, -// *scan_x1, -// *scan_y1, -// *scan_x2, -// *scan_y2, -// use_absolute); -} - - - diff --git a/cinelerra-5.1/plugins/motion.new/motionwindow.h b/cinelerra-5.1/plugins/motion.new/motionwindow.h deleted file mode 100644 index 02aa13b0..00000000 --- a/cinelerra-5.1/plugins/motion.new/motionwindow.h +++ /dev/null @@ -1,373 +0,0 @@ - -/* - * CINELERRA - * Copyright (C) 2008 Adam Williams - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "guicast.h" -#include "motion.inc" - -class MasterLayer : public BC_PopupMenu -{ -public: - MasterLayer(MotionMain *plugin, MotionWindow *gui, int x, int y); - int handle_event(); - void create_objects(); - static int calculate_w(MotionWindow *gui); - static int from_text(char *text); - static char* to_text(int mode); - MotionMain *plugin; - MotionWindow *gui; -}; - -class ActionType : public BC_PopupMenu -{ -public: - ActionType(MotionMain *plugin, MotionWindow *gui, int x, int y); - int handle_event(); - void create_objects(); - static int calculate_w(MotionWindow *gui); - static int from_text(char *text); - static char* to_text(int mode); - MotionMain *plugin; - MotionWindow *gui; -}; - -class TrackingType : public BC_PopupMenu -{ -public: - TrackingType(MotionMain *plugin, MotionWindow *gui, int x, int y); - int handle_event(); - void create_objects(); - static int calculate_w(MotionWindow *gui); - static int from_text(char *text); - static char* to_text(int mode); - MotionMain *plugin; - MotionWindow *gui; -}; - -class TrackDirection : public BC_PopupMenu -{ -public: - TrackDirection(MotionMain *plugin, MotionWindow *gui, int x, int y); - int handle_event(); - void create_objects(); - static int calculate_w(MotionWindow *gui); - static void from_text(int *horizontal_only, int *vertical_only, char *text); - static char* to_text(int horizontal_only, int vertical_only); - MotionMain *plugin; - MotionWindow *gui; -}; - - -class TrackSingleFrame : public BC_Radial -{ -public: - TrackSingleFrame(MotionMain *plugin, - MotionWindow *gui, - int x, - int y); - int handle_event(); - MotionMain *plugin; - MotionWindow *gui; -}; - -class TrackFrameNumber : public BC_TextBox -{ -public: - TrackFrameNumber(MotionMain *plugin, - MotionWindow *gui, - int x, - int y); - int handle_event(); - MotionMain *plugin; - MotionWindow *gui; -}; - -class TrackPreviousFrame : public BC_Radial -{ -public: - TrackPreviousFrame(MotionMain *plugin, - MotionWindow *gui, - int x, - int y); - int handle_event(); - MotionMain *plugin; - MotionWindow *gui; -}; - -class PreviousFrameSameBlock : public BC_Radial -{ -public: - PreviousFrameSameBlock(MotionMain *plugin, - MotionWindow *gui, - int x, - int y); - int handle_event(); - MotionMain *plugin; - MotionWindow *gui; -}; - -class GlobalRange : public BC_IPot -{ -public: - GlobalRange(MotionMain *plugin, - int x, - int y, - int *value); - int handle_event(); - MotionMain *plugin; - int *value; -}; - -class RotationRange : public BC_IPot -{ -public: - RotationRange(MotionMain *plugin, - int x, - int y); - int handle_event(); - MotionMain *plugin; -}; - -class RotationCenter : public BC_IPot -{ -public: - RotationCenter(MotionMain *plugin, - int x, - int y); - int handle_event(); - MotionMain *plugin; -}; - -class BlockSize : public BC_IPot -{ -public: - BlockSize(MotionMain *plugin, - int x, - int y, - int *value); - int handle_event(); - MotionMain *plugin; - int *value; -}; - -class MotionBlockX : public BC_FPot -{ -public: - MotionBlockX(MotionMain *plugin, - MotionWindow *gui, - int x, - int y); - int handle_event(); - MotionWindow *gui; - MotionMain *plugin; -}; - -class MotionBlockY : public BC_FPot -{ -public: - MotionBlockY(MotionMain *plugin, - MotionWindow *gui, - int x, - int y); - int handle_event(); - MotionWindow *gui; - MotionMain *plugin; -}; - -class MotionBlockXText : public BC_TextBox -{ -public: - MotionBlockXText(MotionMain *plugin, - MotionWindow *gui, - int x, - int y); - int handle_event(); - MotionWindow *gui; - MotionMain *plugin; -}; - -class MotionBlockYText : public BC_TextBox -{ -public: - MotionBlockYText(MotionMain *plugin, - MotionWindow *gui, - int x, - int y); - int handle_event(); - MotionWindow *gui; - MotionMain *plugin; -}; - -class GlobalSearchPositions : public BC_PopupMenu -{ -public: - GlobalSearchPositions(MotionMain *plugin, - int x, - int y, - int w); - void create_objects(); - int handle_event(); - MotionMain *plugin; -}; - -class RotationSearchPositions : public BC_PopupMenu -{ -public: - RotationSearchPositions(MotionMain *plugin, - int x, - int y, - int w); - void create_objects(); - int handle_event(); - MotionMain *plugin; -}; - -class MotionMagnitude : public BC_IPot -{ -public: - MotionMagnitude(MotionMain *plugin, - int x, - int y); - int handle_event(); - MotionMain *plugin; -}; - -class MotionRMagnitude : public BC_IPot -{ -public: - MotionRMagnitude(MotionMain *plugin, - int x, - int y); - int handle_event(); - MotionMain *plugin; -}; - -class MotionReturnSpeed : public BC_IPot -{ -public: - MotionReturnSpeed(MotionMain *plugin, - int x, - int y); - int handle_event(); - MotionMain *plugin; -}; - - -class MotionRReturnSpeed : public BC_IPot -{ -public: - MotionRReturnSpeed(MotionMain *plugin, - int x, - int y); - int handle_event(); - MotionMain *plugin; -}; - - -class MotionDrawVectors : public BC_CheckBox -{ -public: - MotionDrawVectors(MotionMain *plugin, - MotionWindow *gui, - int x, - int y); - int handle_event(); - MotionMain *plugin; - MotionWindow *gui; -}; - - -class MotionGlobal : public BC_CheckBox -{ -public: - MotionGlobal(MotionMain *plugin, - MotionWindow *gui, - int x, - int y); - int handle_event(); - MotionWindow *gui; - MotionMain *plugin; -}; - -class MotionRotate : public BC_CheckBox -{ -public: - MotionRotate(MotionMain *plugin, - MotionWindow *gui, - int x, - int y); - int handle_event(); - MotionWindow *gui; - MotionMain *plugin; -}; - - - -class MotionWindow : public PluginClientWindow -{ -public: - MotionWindow(MotionMain *plugin); - ~MotionWindow(); - - void create_objects(); - void update_mode(); - char* get_radius_title(); - - GlobalRange *global_range_w; - GlobalRange *global_range_h; - RotationRange *rotation_range; - RotationCenter *rotation_center; - BlockSize *global_block_w; - BlockSize *global_block_h; - BlockSize *rotation_block_w; - BlockSize *rotation_block_h; - MotionBlockX *block_x; - MotionBlockY *block_y; - MotionBlockXText *block_x_text; - MotionBlockYText *block_y_text; - GlobalSearchPositions *global_search_positions; - RotationSearchPositions *rotation_search_positions; - MotionMagnitude *magnitude; - MotionRMagnitude *rotate_magnitude; - MotionReturnSpeed *return_speed; - MotionRReturnSpeed *rotate_return_speed; - ActionType *action_type; - MotionDrawVectors *vectors; - MotionGlobal *global; - MotionRotate *rotate; - TrackSingleFrame *track_single; - TrackFrameNumber *track_frame_number; - TrackPreviousFrame *track_previous; - PreviousFrameSameBlock *previous_same; - MasterLayer *master_layer; - TrackingType *tracking_type; - TrackDirection *track_direction; - - MotionMain *plugin; -}; - - - - - - - - - diff --git a/cinelerra-5.1/plugins/motion/motion.C b/cinelerra-5.1/plugins/motion/motion.C index 7d660b35..ed676803 100644 --- a/cinelerra-5.1/plugins/motion/motion.C +++ b/cinelerra-5.1/plugins/motion/motion.C @@ -1,7 +1,7 @@ /* * CINELERRA - * Copyright (C) 2016 Adam Williams + * Copyright (C) 2012 Adam Williams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,7 +33,6 @@ #include "mutex.h" #include "overlayframe.h" #include "rotateframe.h" -#include "rotatescan.h" #include "transportque.h" @@ -42,6 +41,7 @@ REGISTER_PLUGIN(MotionMain) + #undef DEBUG // #ifndef DEBUG @@ -52,35 +52,48 @@ REGISTER_PLUGIN(MotionMain) MotionConfig::MotionConfig() { - global_range_w = 5; - global_range_h = 5; + global_range_w = 10; + global_range_h = 10; rotation_range = 5; rotation_center = 0; block_count = 1; - global_block_w = MIN_BLOCK; - global_block_h = MIN_BLOCK; + global_block_w = 50; // MIN_BLOCK; + global_block_h = 50; // MIN_BLOCK; // rotation_block_w = MIN_BLOCK; // rotation_block_h = MIN_BLOCK; block_x = 50; block_y = 50; -// global_positions = 256; -// rotate_positions = 4; - magnitude = 100; - rotate_magnitude = 90; - return_speed = 0; - rotate_return_speed = 0; - action_type = MotionScan::STABILIZE; -// global = 1; + global_positions = 256; + rotate_positions = 4; + magnitude = 25; + rotate_magnitude = 30; + return_speed = 8; + rotate_return_speed = 8; + action_type = MotionScan::STABILIZE_PIXEL; + global = 1; rotate = 1; - tracking_type = MotionScan::NO_CALCULATE; - draw_vectors = 1; - tracking_object = MotionScan::TRACK_SINGLE; + addtrackedframeoffset = 0; + tracking_type = MotionScan::CALCULATE; + draw_vectors = 0; + tracking_object = MotionScan::TRACK_PREVIOUS; track_frame = 0; bottom_is_master = 1; horizontal_only = 0; vertical_only = 0; } +void MotionConfig::set_cpus(int cpus) +{ + int gpos = 64, gpos_limit = 16 * cpus; + if( gpos_limit > 131072 ) gpos_limit = 131072; + while( gpos < gpos_limit ) gpos *= 2; + global_positions = gpos; + int rpos = 4, rpos_limit = cpus / 4; + if( rpos_limit > 32 ) gpos_limit = 32; + while( rpos < rpos_limit ) rpos *= 2; + rotate_positions = rpos; +} + void MotionConfig::boundaries() { CLAMP(global_range_w, MIN_RADIUS, MAX_RADIUS); @@ -101,8 +114,9 @@ int MotionConfig::equivalent(MotionConfig &that) rotation_range == that.rotation_range && rotation_center == that.rotation_center && action_type == that.action_type && -// global == that.global && + global == that.global && rotate == that.rotate && + addtrackedframeoffset == that.addtrackedframeoffset && draw_vectors == that.draw_vectors && block_count == that.block_count && global_block_w == that.global_block_w && @@ -111,8 +125,8 @@ int MotionConfig::equivalent(MotionConfig &that) // rotation_block_h == that.rotation_block_h && EQUIV(block_x, that.block_x) && EQUIV(block_y, that.block_y) && -// global_positions == that.global_positions && -// rotate_positions == that.rotate_positions && + global_positions == that.global_positions && + rotate_positions == that.rotate_positions && magnitude == that.magnitude && return_speed == that.return_speed && rotate_return_speed == that.rotate_return_speed && @@ -131,15 +145,16 @@ void MotionConfig::copy_from(MotionConfig &that) rotation_range = that.rotation_range; rotation_center = that.rotation_center; action_type = that.action_type; -// global = that.global; + global = that.global; rotate = that.rotate; + addtrackedframeoffset = that.addtrackedframeoffset; tracking_type = that.tracking_type; draw_vectors = that.draw_vectors; block_count = that.block_count; block_x = that.block_x; block_y = that.block_y; -// global_positions = that.global_positions; -// rotate_positions = that.rotate_positions; + global_positions = that.global_positions; + rotate_positions = that.rotate_positions; global_block_w = that.global_block_w; global_block_h = that.global_block_h; // rotation_block_w = that.rotation_block_w; @@ -170,13 +185,14 @@ void MotionConfig::interpolate(MotionConfig &prev, rotation_range = prev.rotation_range; rotation_center = prev.rotation_center; action_type = prev.action_type; -// global = prev.global; + global = prev.global; rotate = prev.rotate; + addtrackedframeoffset = prev.addtrackedframeoffset; tracking_type = prev.tracking_type; draw_vectors = prev.draw_vectors; block_count = prev.block_count; -// global_positions = prev.global_positions; -// rotate_positions = prev.rotate_positions; + global_positions = prev.global_positions; + rotate_positions = prev.rotate_positions; global_block_w = prev.global_block_w; global_block_h = prev.global_block_h; // rotation_block_w = prev.rotation_block_w; @@ -215,7 +231,7 @@ MotionMain::MotionMain(PluginServer *server) { engine = 0; rotate_engine = 0; -// motion_rotate = 0; + motion_rotate = 0; total_dx = 0; total_dy = 0; total_angle = 0; @@ -234,6 +250,8 @@ MotionMain::MotionMain(PluginServer *server) current_rotate_ref = 0; rotate_target_src = 0; rotate_target_dst = 0; + + config.set_cpus(get_project_smp() + 1); } MotionMain::~MotionMain() @@ -244,7 +262,7 @@ MotionMain::~MotionMain() delete [] search_area; delete temp_frame; delete rotate_engine; -// delete motion_rotate; + delete motion_rotate; delete prev_global_ref; @@ -277,11 +295,11 @@ void MotionMain::update_gui() { thread->window->lock_window("MotionMain::update_gui"); -// char string[BCTEXTLEN]; -// sprintf(string, "%d", config.global_positions); -// ((MotionWindow*)thread->window)->global_search_positions->set_text(string); -// sprintf(string, "%d", config.rotate_positions); -// ((MotionWindow*)thread->window)->rotation_search_positions->set_text(string); + char string[BCTEXTLEN]; + sprintf(string, "%d", config.global_positions); + ((MotionWindow*)thread->window)->global_search_positions->set_text(string); + sprintf(string, "%d", config.rotate_positions); + ((MotionWindow*)thread->window)->rotation_search_positions->set_text(string); ((MotionWindow*)thread->window)->global_block_w->update(config.global_block_w); ((MotionWindow*)thread->window)->global_block_h->update(config.global_block_h); @@ -336,8 +354,8 @@ void MotionMain::save_data(KeyFrame *keyframe) output.tag.set_title("MOTION"); output.tag.set_property("BLOCK_COUNT", config.block_count); -// output.tag.set_property("GLOBAL_POSITIONS", config.global_positions); -// output.tag.set_property("ROTATE_POSITIONS", config.rotate_positions); + output.tag.set_property("GLOBAL_POSITIONS", config.global_positions); + output.tag.set_property("ROTATE_POSITIONS", config.rotate_positions); output.tag.set_property("GLOBAL_BLOCK_W", config.global_block_w); output.tag.set_property("GLOBAL_BLOCK_H", config.global_block_h); // output.tag.set_property("ROTATION_BLOCK_W", config.rotation_block_w); @@ -353,8 +371,9 @@ void MotionMain::save_data(KeyFrame *keyframe) output.tag.set_property("ROTATE_MAGNITUDE", config.rotate_magnitude); output.tag.set_property("ROTATE_RETURN_SPEED", config.rotate_return_speed); output.tag.set_property("ACTION_TYPE", config.action_type); -// output.tag.set_property("GLOBAL", config.global); + output.tag.set_property("GLOBAL", config.global); output.tag.set_property("ROTATE", config.rotate); + output.tag.set_property("ADDTRACKEDFRAMEOFFSET", config.addtrackedframeoffset); output.tag.set_property("TRACKING_TYPE", config.tracking_type); output.tag.set_property("DRAW_VECTORS", config.draw_vectors); output.tag.set_property("TRACKING_OBJECT", config.tracking_object); @@ -385,8 +404,8 @@ void MotionMain::read_data(KeyFrame *keyframe) if(input.tag.title_is("MOTION")) { config.block_count = input.tag.get_property("BLOCK_COUNT", config.block_count); -// config.global_positions = input.tag.get_property("GLOBAL_POSITIONS", config.global_positions); -// config.rotate_positions = input.tag.get_property("ROTATE_POSITIONS", config.rotate_positions); + config.global_positions = input.tag.get_property("GLOBAL_POSITIONS", config.global_positions); + config.rotate_positions = input.tag.get_property("ROTATE_POSITIONS", config.rotate_positions); config.global_block_w = input.tag.get_property("GLOBAL_BLOCK_W", config.global_block_w); config.global_block_h = input.tag.get_property("GLOBAL_BLOCK_H", config.global_block_h); // config.rotation_block_w = input.tag.get_property("ROTATION_BLOCK_W", config.rotation_block_w); @@ -402,8 +421,9 @@ void MotionMain::read_data(KeyFrame *keyframe) config.rotate_magnitude = input.tag.get_property("ROTATE_MAGNITUDE", config.rotate_magnitude); config.rotate_return_speed = input.tag.get_property("ROTATE_RETURN_SPEED", config.rotate_return_speed); config.action_type = input.tag.get_property("ACTION_TYPE", config.action_type); -// config.global = input.tag.get_property("GLOBAL", config.global); + config.global = input.tag.get_property("GLOBAL", config.global); config.rotate = input.tag.get_property("ROTATE", config.rotate); + config.addtrackedframeoffset = input.tag.get_property("ADDTRACKEDFRAMEOFFSET", config.addtrackedframeoffset); config.tracking_type = input.tag.get_property("TRACKING_TYPE", config.tracking_type); config.draw_vectors = input.tag.get_property("DRAW_VECTORS", config.draw_vectors); config.tracking_object = input.tag.get_property("TRACKING_OBJECT", config.tracking_object); @@ -441,39 +461,30 @@ void MotionMain::allocate_temp(int w, int h, int color_model) void MotionMain::process_global() { - int w = current_global_ref->get_w(); - int h = current_global_ref->get_h(); - if(!engine) engine = new MotionScan(PluginClient::get_project_smp() + 1, PluginClient::get_project_smp() + 1); // Determine if frames changed -// printf("MotionMain::process_global %d block_y=%f total_dy=%d\n", -// __LINE__, config.block_y * h / 100, total_dy); engine->scan_frame(current_global_ref, prev_global_ref, - config.global_range_w * w / 100, - config.global_range_h * h / 100, - config.global_block_w * w / 100, - config.global_block_h * h / 100, - config.block_x * w / 100, - config.block_y * h / 100, + config.global_range_w, + config.global_range_h, + config.global_block_w, + config.global_block_h, + config.block_x, + config.block_y, config.tracking_object, config.tracking_type, config.action_type, config.horizontal_only, config.vertical_only, get_source_position(), + config.global_positions, total_dx, total_dy, 0, - 0, - 1, // do_motion - config.rotate, // do_rotate - config.rotation_center, - config.rotation_range); - + 0); current_dx = engine->dx_result; current_dy = engine->dy_result; @@ -485,8 +496,9 @@ void MotionMain::process_global() total_dy = (int64_t)total_dy * (100 - config.return_speed) / 100; total_dx += engine->dx_result; total_dy += engine->dy_result; -// printf("MotionMain::process_global %d total_dy=%d engine->dy_result=%d\n", -// __LINE__, total_dy, engine->dy_result); +// printf("MotionMain::process_global total_dx=%d engine->dx_result=%d\n", +// total_dx, +// engine->dx_result); } else // Make accumulation vector current @@ -498,27 +510,43 @@ void MotionMain::process_global() // Clamp accumulation vector if(config.magnitude < 100) { - //int block_w = (int64_t)config.global_block_w * w / 100; - //int block_h = (int64_t)config.global_block_h * h / 100; - int block_x_orig = (int64_t)(config.block_x * w / 100); + //int block_w = (int64_t)config.global_block_w * + // current_global_ref->get_w() / 100; + //int block_h = (int64_t)config.global_block_h * + // current_global_ref->get_h() / 100; + int block_x_orig = (int64_t)(config.block_x * + current_global_ref->get_w() / + 100); int block_y_orig = (int64_t)(config.block_y * - current_global_ref->get_h() / h / 100); + current_global_ref->get_h() / + 100); - int max_block_x = (int64_t)(w - block_x_orig) * - OVERSAMPLE * config.magnitude / 100; - int max_block_y = (int64_t)(h - block_y_orig) * - OVERSAMPLE * config.magnitude / 100; + int max_block_x = (int64_t)(current_global_ref->get_w() - block_x_orig) * + OVERSAMPLE * + config.magnitude / + 100; + int max_block_y = (int64_t)(current_global_ref->get_h() - block_y_orig) * + OVERSAMPLE * + config.magnitude / + 100; int min_block_x = (int64_t)-block_x_orig * - OVERSAMPLE * config.magnitude / 100; + OVERSAMPLE * + config.magnitude / + 100; int min_block_y = (int64_t)-block_y_orig * - OVERSAMPLE * config.magnitude / 100; + OVERSAMPLE * + config.magnitude / + 100; CLAMP(total_dx, min_block_x, max_block_x); CLAMP(total_dy, min_block_y, max_block_y); } -// printf("MotionMain::process_global %d total_dx=%d total_dy=%d\n", -// __LINE__, total_dx, total_dy); +#ifdef DEBUG +printf("MotionMain::process_global 2 total_dx=%.02f total_dy=%.02f\n", +(float)total_dx / OVERSAMPLE, +(float)total_dy / OVERSAMPLE); +#endif if(config.tracking_object != MotionScan::TRACK_SINGLE && !config.rotate) { @@ -547,6 +575,7 @@ void MotionMain::process_global() dx = -(int)(total_dx / OVERSAMPLE); dy = -(int)(total_dy / OVERSAMPLE); break; + break; case MotionScan::TRACK: interpolation = CUBIC_LINEAR; dx = (float)total_dx / OVERSAMPLE; @@ -588,11 +617,9 @@ void MotionMain::process_rotation() int block_x; int block_y; -// Always require global // Convert the previous global reference into the previous rotation reference. // Convert global target destination into rotation target source. -// if(config.global) - if(1) + if(config.global) { if(!overlayer) overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1); @@ -657,17 +684,17 @@ void MotionMain::process_rotation() // Get rotation -// if(!motion_rotate) -// motion_rotate = new RotateScan(this, -// get_project_smp() + 1, -// get_project_smp() + 1); -// -// current_angle = motion_rotate->scan_frame(prev_rotate_ref, -// current_rotate_ref, -// block_x, -// block_y); - - current_angle = engine->dr_result; + if(!motion_rotate) + motion_rotate = new RotateScan(this, + get_project_smp() + 1, + get_project_smp() + 1); + + current_angle = motion_rotate->scan_frame(prev_rotate_ref, + current_rotate_ref, + block_x, + block_y); + + // Add current rotation to accumulation if(config.tracking_object != MotionScan::TRACK_SINGLE) @@ -683,13 +710,13 @@ void MotionMain::process_rotation() CLAMP(total_angle, -config.rotate_magnitude, config.rotate_magnitude); } -// if(!config.global) -// { + if(!config.global) + { // Transfer current reference frame to previous reference frame and update // counter. -// prev_rotate_ref->copy_from(current_rotate_ref); -// previous_frame_number = get_source_position(); -// } + prev_rotate_ref->copy_from(current_rotate_ref); + previous_frame_number = get_source_position(); + } } else { @@ -741,8 +768,7 @@ printf("MotionMain::process_rotation total_angle=%f\n", total_angle); case MotionScan::STABILIZE: case MotionScan::STABILIZE_PIXEL: -// if(config.global) - if(1) + if(config.global) { // Use origin of global stabilize operation // rotate_engine->set_pivot((int)(rotate_target_dst->get_w() * @@ -776,7 +802,6 @@ printf("MotionMain::process_rotation total_angle=%f\n", total_angle); } -printf("MotionMain::process_rotation angle=%f\n", angle); rotate_engine->rotate(rotate_target_dst, rotate_target_src, angle); // overlayer->overlay(rotate_target_dst, // prev_rotate_ref, @@ -898,7 +923,7 @@ printf("MotionMain::process_buffer %d start_position=%lld\n", __LINE__, start_po } -// if(!config.global && !config.rotate) skip_current = 1; + if(!config.global && !config.rotate) skip_current = 1; @@ -934,8 +959,7 @@ printf("MotionMain::process_buffer %d start_position=%lld\n", __LINE__, start_po // Get the global pointers. Here we walk through the sequence of events. -// if(config.global) - if(1) + if(config.global) { // Assume global only. Global reads previous frame and compares // with current frame to get the current translation. @@ -1048,14 +1072,13 @@ printf("MotionMain::process_buffer %d start_position=%lld\n", __LINE__, start_po -//PRINT_TRACE -//printf("skip_current=%d config.global=%d\n", skip_current, config.global); + if(!skip_current) { // Get position change from previous frame to current frame - /* if(config.global) */ process_global(); + if(config.global) process_global(); // Get rotation change from previous frame to current frame if(config.rotate) process_rotation(); //frame[target_layer]->copy_from(prev_rotate_ref); @@ -1119,9 +1142,7 @@ void MotionMain::draw_vectors(VFrame *frame) int search_x2, search_y2; -// always processing global -// if(config.global) - if(1) + if(config.global) { // Get vector // Start of vector is center of previous block. @@ -1438,3 +1459,439 @@ void MotionMain::draw_arrow(VFrame *frame, int x1, int y1, int x2, int y2) + + + + + + + + + +RotateScanPackage::RotateScanPackage() +{ +} + + +RotateScanUnit::RotateScanUnit(RotateScan *server, MotionMain *plugin) + : LoadClient(server) +{ + this->server = server; + this->plugin = plugin; + rotater = 0; + temp = 0; +} + +RotateScanUnit::~RotateScanUnit() +{ + delete rotater; + delete temp; +} + +void RotateScanUnit::process_package(LoadPackage *package) +{ + if(server->skip) return; + RotateScanPackage *pkg = (RotateScanPackage*)package; + + if((pkg->difference = server->get_cache(pkg->angle)) < 0) + { +//printf("RotateScanUnit::process_package %d\n", __LINE__); + int color_model = server->previous_frame->get_color_model(); + int pixel_size = BC_CModels::calculate_pixelsize(color_model); + int row_bytes = server->previous_frame->get_bytes_per_line(); + + if(!rotater) + rotater = new AffineEngine(1, 1); + if(!temp) temp = new VFrame(0, + -1, + server->previous_frame->get_w(), + server->previous_frame->get_h(), + color_model, + -1); +//printf("RotateScanUnit::process_package %d\n", __LINE__); + + +// Rotate original block size +// rotater->set_viewport(server->block_x1, +// server->block_y1, +// server->block_x2 - server->block_x1, +// server->block_y2 - server->block_y1); + rotater->set_in_viewport(server->block_x1, + server->block_y1, + server->block_x2 - server->block_x1, + server->block_y2 - server->block_y1); + rotater->set_out_viewport(server->block_x1, + server->block_y1, + server->block_x2 - server->block_x1, + server->block_y2 - server->block_y1); +// rotater->set_pivot(server->block_x, server->block_y); + rotater->set_in_pivot(server->block_x, server->block_y); + rotater->set_out_pivot(server->block_x, server->block_y); +//printf("RotateScanUnit::process_package %d\n", __LINE__); + rotater->rotate(temp, + server->previous_frame, + pkg->angle); + +// Scan reduced block size +//plugin->output_frame->copy_from(server->current_frame); +//plugin->output_frame->copy_from(temp); +// printf("RotateScanUnit::process_package %d %d %d %d %d\n", +// __LINE__, +// server->scan_x, +// server->scan_y, +// server->scan_w, +// server->scan_h); +// Clamp coordinates + int x1 = server->scan_x; + int y1 = server->scan_y; + int x2 = x1 + server->scan_w; + int y2 = y1 + server->scan_h; + x2 = MIN(temp->get_w(), x2); + y2 = MIN(temp->get_h(), y2); + x2 = MIN(server->current_frame->get_w(), x2); + y2 = MIN(server->current_frame->get_h(), y2); + x1 = MAX(0, x1); + y1 = MAX(0, y1); + + if(x2 > x1 && y2 > y1) + { + pkg->difference = MotionScan::abs_diff( + temp->get_rows()[y1] + x1 * pixel_size, + server->current_frame->get_rows()[y1] + x1 * pixel_size, + row_bytes, + x2 - x1, + y2 - y1, + color_model); +//printf("RotateScanUnit::process_package %d\n", __LINE__); + server->put_cache(pkg->angle, pkg->difference); + } +#if 0 + VFrame png(x2-x1, y2-y1, BC_RGB888, -1); + png.transfer_from(temp, 0, x1, y1, x2-x1, y2-y1); + char fn[64]; + sprintf(fn,"%s%f.png","/tmp/temp",pkg->angle); png.write_png(fn); + png.transfer_from(server->current_frame, 0, x1, y1, x2-x1, y2-y1); + sprintf(fn,"%s%f.png","/tmp/curr",pkg->angle); png.write_png(fn); +printf("RotateScanUnit::process_package 10 x=%d y=%d w=%d h=%d block_x=%d block_y=%d angle=%f scan_w=%d scan_h=%d diff=%jd\n", + server->block_x1, server->block_y1, server->block_x2 - server->block_x1, server->block_y2 - server->block_y1, + server->block_x, server->block_y, pkg->angle, server->scan_w, server->scan_h, pkg->difference); +#endif + } +} + + + + + + + + + + + + + + + + + + + + + + +RotateScan::RotateScan(MotionMain *plugin, + int total_clients, + int total_packages) + : LoadServer( +//1, 1 +total_clients, total_packages +) +{ + this->plugin = plugin; + cache_lock = new Mutex("RotateScan::cache_lock"); +} + + +RotateScan::~RotateScan() +{ + delete cache_lock; +} + +void RotateScan::init_packages() +{ + for(int i = 0; i < get_total_packages(); i++) + { + RotateScanPackage *pkg = (RotateScanPackage*)get_package(i); + pkg->angle = i * + (scan_angle2 - scan_angle1) / + (total_steps - 1) + + scan_angle1; + } +} + +LoadClient* RotateScan::new_client() +{ + return new RotateScanUnit(this, plugin); +} + +LoadPackage* RotateScan::new_package() +{ + return new RotateScanPackage; +} + + +float RotateScan::scan_frame(VFrame *previous_frame, + VFrame *current_frame, + int block_x, + int block_y) +{ + skip = 0; + this->block_x = block_x; + this->block_y = block_y; + +//printf("RotateScan::scan_frame %d\n", __LINE__); + switch(plugin->config.tracking_type) + { + case MotionScan::NO_CALCULATE: + result = plugin->config.rotation_center; + skip = 1; + break; + + case MotionScan::LOAD: + { + char string[BCTEXTLEN]; + sprintf(string, "%s%06jd", + ROTATION_FILE, plugin->get_source_position()); + FILE *input = fopen(string, "r"); + if(input) + { + fscanf(input, "%f", &result); + fclose(input); + skip = 1; + } + else + { + perror("RotateScan::scan_frame LOAD"); + } + break; + } + } + + + + + + + + + this->previous_frame = previous_frame; + this->current_frame = current_frame; + int w = current_frame->get_w(); + int h = current_frame->get_h(); + int block_w = w * plugin->config.global_block_w / 100; + int block_h = h * plugin->config.global_block_h / 100; + + if(this->block_x - block_w / 2 < 0) block_w = this->block_x * 2; + if(this->block_y - block_h / 2 < 0) block_h = this->block_y * 2; + if(this->block_x + block_w / 2 > w) block_w = (w - this->block_x) * 2; + if(this->block_y + block_h / 2 > h) block_h = (h - this->block_y) * 2; + + block_x1 = this->block_x - block_w / 2; + block_x2 = this->block_x + block_w / 2; + block_y1 = this->block_y - block_h / 2; + block_y2 = this->block_y + block_h / 2; + +// Calculate the maximum area available to scan after rotation. +// Must be calculated from the starting range because of cache. +// Get coords of rectangle after rotation. + double center_x = this->block_x; + double center_y = this->block_y; + double max_angle = plugin->config.rotation_range; + double base_angle1 = atan((float)block_h / block_w); + double base_angle2 = atan((float)block_w / block_h); + double target_angle1 = base_angle1 + max_angle * 2 * M_PI / 360; + double target_angle2 = base_angle2 + max_angle * 2 * M_PI / 360; + double radius = sqrt(block_w * block_w + block_h * block_h) / 2; + double x1 = center_x - cos(target_angle1) * radius; + double y1 = center_y - sin(target_angle1) * radius; + double x2 = center_x + sin(target_angle2) * radius; + double y2 = center_y - cos(target_angle2) * radius; + double x3 = center_x - sin(target_angle2) * radius; + double y3 = center_y + cos(target_angle2) * radius; + +// Track top edge to find greatest area. + double max_area1 = 0; + //double max_x1 = 0; + double max_y1 = 0; + for(double x = x1; x < x2; x++) + { + double y = y1 + (y2 - y1) * (x - x1) / (x2 - x1); + if(x >= center_x && x < block_x2 && y >= block_y1 && y < center_y) + { + double area = fabs(x - center_x) * fabs(y - center_y); + if(area > max_area1) + { + max_area1 = area; + //max_x1 = x; + max_y1 = y; + } + } + } + +// Track left edge to find greatest area. + double max_area2 = 0; + double max_x2 = 0; + //double max_y2 = 0; + for(double y = y1; y < y3; y++) + { + double x = x1 + (x3 - x1) * (y - y1) / (y3 - y1); + if(x >= block_x1 && x < center_x && y >= block_y1 && y < center_y) + { + double area = fabs(x - center_x) * fabs(y - center_y); + if(area > max_area2) + { + max_area2 = area; + max_x2 = x; + //max_y2 = y; + } + } + } + + double max_x, max_y; + max_x = max_x2; + max_y = max_y1; + +// Get reduced scan coords + scan_w = (int)(fabs(max_x - center_x) * 2); + scan_h = (int)(fabs(max_y - center_y) * 2); + scan_x = (int)(center_x - scan_w / 2); + scan_y = (int)(center_y - scan_h / 2); +// printf("RotateScan::scan_frame center=%d,%d scan=%d,%d %dx%d\n", +// this->block_x, this->block_y, scan_x, scan_y, scan_w, scan_h); +// printf(" angle_range=%f block= %d,%d,%d,%d\n", max_angle, block_x1, block_y1, block_x2, block_y2); + +// Determine min angle from size of block + double angle1 = atan((double)block_h / block_w); + double angle2 = atan((double)(block_h - 1) / (block_w + 1)); + double min_angle = fabs(angle2 - angle1) / OVERSAMPLE; + min_angle = MAX(min_angle, MIN_ANGLE); + +//printf("RotateScan::scan_frame %d min_angle=%f\n", __LINE__, min_angle * 360 / 2 / M_PI); + + cache.remove_all_objects(); + + + if(!skip) + { + if(previous_frame->data_matches(current_frame)) + { +//printf("RotateScan::scan_frame: frames match. Skipping.\n"); + result = plugin->config.rotation_center; + skip = 1; + } + } + + if(!skip) + { +// Initial search range + float angle_range = max_angle; + result = plugin->config.rotation_center; + total_steps = plugin->config.rotate_positions; + + + while(angle_range >= min_angle * total_steps) + { + scan_angle1 = result - angle_range; + scan_angle2 = result + angle_range; + + + set_package_count(total_steps); +//set_package_count(1); + process_packages(); + + int64_t min_difference = -1; + for(int i = 0; i < get_total_packages(); i++) + { + RotateScanPackage *pkg = (RotateScanPackage*)get_package(i); + if(pkg->difference < min_difference || min_difference == -1) + { + min_difference = pkg->difference; + result = pkg->angle; + } +//break; + } + + angle_range /= 2; + +//break; + } + } + +//printf("RotateScan::scan_frame %d\n", __LINE__); + + if(!skip && plugin->config.tracking_type == MotionScan::SAVE) + { + char string[BCTEXTLEN]; + sprintf(string, "%s%06jd", + ROTATION_FILE, plugin->get_source_position()); + FILE *output = fopen(string, "w"); + if(output) + { + fprintf(output, "%f\n", result); + fclose(output); + } + else + { + perror("RotateScan::scan_frame SAVE"); + } + } + +//printf("RotateScan::scan_frame %d angle=%f\n", __LINE__, result); + + + + return result; +} + +int64_t RotateScan::get_cache(float angle) +{ + int64_t result = -1; + cache_lock->lock("RotateScan::get_cache"); + for(int i = 0; i < cache.total; i++) + { + RotateScanCache *ptr = cache.values[i]; + if(fabs(ptr->angle - angle) <= MIN_ANGLE) + { + result = ptr->difference; + break; + } + } + cache_lock->unlock(); + return result; +} + +void RotateScan::put_cache(float angle, int64_t difference) +{ + RotateScanCache *ptr = new RotateScanCache(angle, difference); + cache_lock->lock("RotateScan::put_cache"); + cache.append(ptr); + cache_lock->unlock(); +} + + + + + + + + + +RotateScanCache::RotateScanCache(float angle, int64_t difference) +{ + this->angle = angle; + this->difference = difference; +} + + + diff --git a/cinelerra-5.1/plugins/motion/motion.h b/cinelerra-5.1/plugins/motion/motion.h index 5b359f4b..e0011d75 100644 --- a/cinelerra-5.1/plugins/motion/motion.h +++ b/cinelerra-5.1/plugins/motion/motion.h @@ -1,7 +1,7 @@ /* * CINELERRA - * Copyright (C) 2016 Adam Williams + * Copyright (C) 2008 Adam Williams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,11 +35,12 @@ #include "motionwindow.inc" #include "overlayframe.inc" #include "pluginvclient.h" -#include "rotatescan.inc" +#include "rotateframe.inc" #include "vframe.inc" class MotionMain; class MotionWindow; +class RotateScan; @@ -63,6 +64,7 @@ class MotionWindow; // Precision of rotation #define MIN_ANGLE 0.0001 +#define ROTATION_FILE "/tmp/r" class MotionConfig { @@ -77,6 +79,7 @@ public: int64_t next_frame, int64_t current_frame); void boundaries(); + void set_cpus(int cpus); int block_count; int global_range_w; @@ -96,8 +99,8 @@ public: // int rotation_block_w; // int rotation_block_h; // Number of search positions in each refinement of the log search -// int global_positions; -// int rotate_positions; + int global_positions; + int rotate_positions; // Block position in percentage 0 - 100 double block_x; double block_y; @@ -106,6 +109,7 @@ public: int vertical_only; int global; int rotate; + int addtrackedframeoffset; // Track or stabilize, single pixel, scan only, or nothing int action_type; // Recalculate, no calculate, save, or load coordinates from disk @@ -178,7 +182,7 @@ public: // It is moved to compensate for motion and copied to the previous_frame. VFrame *temp_frame; MotionScan *engine; -// RotateScan *motion_rotate; + RotateScan *motion_rotate; OverlayFrame *overlayer; AffineEngine *rotate_engine; @@ -244,6 +248,110 @@ public: + + + + + + + + + + + + + +class RotateScanPackage : public LoadPackage +{ +public: + RotateScanPackage(); + float angle; + int64_t difference; +}; + +class RotateScanCache +{ +public: + RotateScanCache(float angle, int64_t difference); + float angle; + int64_t difference; +}; + +class RotateScanUnit : public LoadClient +{ +public: + RotateScanUnit(RotateScan *server, MotionMain *plugin); + ~RotateScanUnit(); + + void process_package(LoadPackage *package); + + RotateScan *server; + MotionMain *plugin; + AffineEngine *rotater; + VFrame *temp; +}; + +class RotateScan : public LoadServer +{ +public: + RotateScan(MotionMain *plugin, + int total_clients, + int total_packages); + ~RotateScan(); + + friend class RotateScanUnit; + + void init_packages(); + LoadClient* new_client(); + LoadPackage* new_package(); + +// Invoke the motion engine for a search +// Frame before rotation + float scan_frame(VFrame *previous_frame, +// Frame after rotation + VFrame *current_frame, +// Pivot + int block_x, + int block_y); + int64_t get_cache(float angle); + void put_cache(float angle, int64_t difference); + + +// Angle result + float result; + +private: + VFrame *previous_frame; +// Frame after motion + VFrame *current_frame; + + MotionMain *plugin; + int skip; + +// Pivot + int block_x; + int block_y; +// Block to rotate + int block_x1; + int block_x2; + int block_y1; + int block_y2; +// Area to compare + int scan_x; + int scan_y; + int scan_w; + int scan_h; +// Range of angles to compare + float scan_angle1, scan_angle2; + int total_steps; + + ArrayList cache; + Mutex *cache_lock; +}; + + + + #endif diff --git a/cinelerra-5.1/plugins/motion/motion.inc b/cinelerra-5.1/plugins/motion/motion.inc index cf71a2a4..01c35947 100644 --- a/cinelerra-5.1/plugins/motion/motion.inc +++ b/cinelerra-5.1/plugins/motion/motion.inc @@ -2,21 +2,21 @@ /* * CINELERRA * Copyright (C) 2008 Adam Williams - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * */ #ifndef MOTION_INC diff --git a/cinelerra-5.1/plugins/motion/motionscan.C b/cinelerra-5.1/plugins/motion/motionscan.C index 67e8460e..b1a533ef 100644 --- a/cinelerra-5.1/plugins/motion/motionscan.C +++ b/cinelerra-5.1/plugins/motion/motionscan.C @@ -1,46 +1,38 @@ /* * CINELERRA - * Copyright (C) 2016 Adam Williams - * + * Copyright (C) 2012 Adam Williams + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * */ -#include "affine.h" -#include "bcsignals.h" #include "clip.h" +//#include "../downsample/downsampleengine.h" +//#include "motion.h" #include "motionscan.h" #include "mutex.h" #include "vframe.h" - #include -#include -#include // The module which does the actual scanning -// starting level of detail -#define STARTING_DOWNSAMPLE 16 -// minimum size in each level of detail -#define MIN_DOWNSAMPLED_SIZE 16 -// minimum scan range -#define MIN_DOWNSAMPLED_SCAN 4 -// scan range for subpixel mode -#define SUBPIXEL_RANGE 4 + + + MotionScanPackage::MotionScanPackage() : LoadPackage() @@ -57,124 +49,116 @@ MotionScanUnit::MotionScanUnit(MotionScan *server) : LoadClient(server) { this->server = server; + cache_lock = new Mutex("MotionScanUnit::cache_lock"); } MotionScanUnit::~MotionScanUnit() { + delete cache_lock; } -void MotionScanUnit::single_pixel(MotionScanPackage *pkg) + +void MotionScanUnit::process_package(LoadPackage *package) { + MotionScanPackage *pkg = (MotionScanPackage*)package; //int w = server->current_frame->get_w(); //int h = server->current_frame->get_h(); int color_model = server->current_frame->get_color_model(); int pixel_size = BC_CModels::calculate_pixelsize(color_model); int row_bytes = server->current_frame->get_bytes_per_line(); -// printf("MotionScanUnit::process_package %d search_x=%d search_y=%d scan_x1=%d scan_y1=%d scan_x2=%d scan_y2=%d x_steps=%d y_steps=%d\n", -// __LINE__, -// pkg->search_x, -// pkg->search_y, -// pkg->scan_x1, -// pkg->scan_y1, -// pkg->scan_x2, -// pkg->scan_y2, -// server->x_steps, -// server->y_steps); -// Pointers to first pixel in each block - unsigned char *prev_ptr = server->previous_frame->get_rows()[ - pkg->search_y] + - pkg->search_x * pixel_size; - unsigned char *current_ptr = 0; - if(server->do_rotate) - { - current_ptr = server->rotated_current[pkg->angle_step]->get_rows()[ - pkg->block_y1] + - pkg->block_x1 * pixel_size; - } - else - { - current_ptr = server->current_frame->get_rows()[ - pkg->block_y1] + - pkg->block_x1 * pixel_size; - } -// Scan block - pkg->difference1 = MotionScan::abs_diff(prev_ptr, - current_ptr, - row_bytes, - pkg->block_x2 - pkg->block_x1, - pkg->block_y2 - pkg->block_y1, - color_model); - -// printf("MotionScanUnit::process_package %d angle_step=%d diff=%d\n", -// __LINE__, -// pkg->angle_step, -// pkg->difference1); -// printf("MotionScanUnit::process_package %d search_x=%d search_y=%d diff=%lld\n", -// __LINE__, server->block_x1 - pkg->search_x, server->block_y1 - pkg->search_y, pkg->difference1); -} -void MotionScanUnit::subpixel(MotionScanPackage *pkg) -{ -//PRINT_TRACE - //int w = server->current_frame->get_w(); - //int h = server->current_frame->get_h(); - int color_model = server->current_frame->get_color_model(); - int pixel_size = BC_CModels::calculate_pixelsize(color_model); - int row_bytes = server->current_frame->get_bytes_per_line(); - unsigned char *prev_ptr = server->previous_frame->get_rows()[ - pkg->search_y] + - pkg->search_x * pixel_size; -// neglect rotation - unsigned char *current_ptr = server->current_frame->get_rows()[ - pkg->block_y1] + - pkg->block_x1 * pixel_size; -// With subpixel, there are two ways to compare each position, one by shifting -// the previous frame and two by shifting the current frame. - pkg->difference1 = MotionScan::abs_diff_sub(prev_ptr, - current_ptr, - row_bytes, - pkg->block_x2 - pkg->block_x1, - pkg->block_y2 - pkg->block_y1, - color_model, - pkg->sub_x, - pkg->sub_y); - pkg->difference2 = MotionScan::abs_diff_sub(current_ptr, - prev_ptr, - row_bytes, - pkg->block_x2 - pkg->block_x1, - pkg->block_y2 - pkg->block_y1, - color_model, - pkg->sub_x, - pkg->sub_y); -// printf("MotionScanUnit::process_package sub_x=%d sub_y=%d search_x=%d search_y=%d diff1=%lld diff2=%lld\n", -// pkg->sub_x, -// pkg->sub_y, -// pkg->search_x, -// pkg->search_y, -// pkg->difference1, -// pkg->difference2); -} -void MotionScanUnit::process_package(LoadPackage *package) -{ - MotionScanPackage *pkg = (MotionScanPackage*)package; + + + // Single pixel if(!server->subpixel) { - single_pixel(pkg); +// Try cache + pkg->difference1 = server->get_cache(pkg->search_x, pkg->search_y); + if(pkg->difference1 < 0) + { +//printf("MotionScanUnit::process_package 1 search_x=%d search_y=%d scan_x1=%d scan_y1=%d scan_x2=%d scan_y2=%d x_steps=%d y_steps=%d\n", +//pkg->search_x, pkg->search_y, pkg->scan_x1, pkg->scan_y1, pkg->scan_x2, pkg->scan_y2, server->x_steps, server->y_steps); +// Pointers to first pixel in each block + unsigned char *prev_ptr = server->previous_frame->get_rows()[ + pkg->search_y] + + pkg->search_x * pixel_size; + unsigned char *current_ptr = server->current_frame->get_rows()[ + pkg->block_y1] + + pkg->block_x1 * pixel_size; + +// Scan block + pkg->difference1 = MotionScan::abs_diff(prev_ptr, + current_ptr, + row_bytes, + pkg->block_x2 - pkg->block_x1, + pkg->block_y2 - pkg->block_y1, + color_model); + +// printf("MotionScanUnit::process_package %d search_x=%d search_y=%d diff=%lld\n", +// __LINE__, server->block_x1 - pkg->search_x, server->block_y1 - pkg->search_y, pkg->difference1); + server->put_cache(pkg->search_x, pkg->search_y, pkg->difference1); + } } + + + + + + + else + + + + + + + + // Sub pixel { - subpixel(pkg); + unsigned char *prev_ptr = server->previous_frame->get_rows()[ + pkg->search_y] + + pkg->search_x * pixel_size; + unsigned char *current_ptr = server->current_frame->get_rows()[ + pkg->block_y1] + + pkg->block_x1 * pixel_size; + +// With subpixel, there are two ways to compare each position, one by shifting +// the previous frame and two by shifting the current frame. + pkg->difference1 = MotionScan::abs_diff_sub(prev_ptr, + current_ptr, + row_bytes, + pkg->block_x2 - pkg->block_x1, + pkg->block_y2 - pkg->block_y1, + color_model, + pkg->sub_x, + pkg->sub_y); + pkg->difference2 = MotionScan::abs_diff_sub(current_ptr, + prev_ptr, + row_bytes, + pkg->block_x2 - pkg->block_x1, + pkg->block_y2 - pkg->block_y1, + color_model, + pkg->sub_x, + pkg->sub_y); +// printf("MotionScanUnit::process_package sub_x=%d sub_y=%d search_x=%d search_y=%d diff1=%lld diff2=%lld\n", +// sub_x, +// sub_y, +// search_x, +// search_y, +// pkg->difference1, +// pkg->difference2); } @@ -191,6 +175,33 @@ void MotionScanUnit::process_package(LoadPackage *package) +int64_t MotionScanUnit::get_cache(int x, int y) +{ + int64_t result = -1; + cache_lock->lock("MotionScanUnit::get_cache"); + for(int i = 0; i < cache.total; i++) + { + MotionScanCache *ptr = cache.values[i]; + if(ptr->x == x && ptr->y == y) + { + result = ptr->difference; + break; + } + } + cache_lock->unlock(); + return result; +} + +void MotionScanUnit::put_cache(int x, int y, int64_t difference) +{ + MotionScanCache *ptr = new MotionScanCache(x, y, difference); + cache_lock->lock("MotionScanUnit::put_cache"); + cache.append(ptr); + cache_lock->unlock(); +} + + + @@ -202,880 +213,135 @@ void MotionScanUnit::process_package(LoadPackage *package) MotionScan::MotionScan(int total_clients, int total_packages) : LoadServer( -// DEBUG -//1, 1 -total_clients, total_packages +//1, 1 +total_clients, total_packages ) { test_match = 1; + cache_lock = new Mutex("MotionScan::cache_lock"); downsampled_previous = 0; downsampled_current = 0; - rotated_current = 0; - rotater = 0; +// downsample = 0; } MotionScan::~MotionScan() { + delete cache_lock; delete downsampled_previous; delete downsampled_current; - if(rotated_current) - { - for(int i = 0; i < total_rotated; i++) - { - delete rotated_current[i]; - } - - delete [] rotated_current; - } - delete rotater; +// delete downsample; } void MotionScan::init_packages() { // Set package coords -// Total range of positions to scan with downsampling - int downsampled_scan_x1 = scan_x1 / current_downsample; - //int downsampled_scan_x2 = scan_x2 / current_downsample; - int downsampled_scan_y1 = scan_y1 / current_downsample; - //int downsampled_scan_y2 = scan_y2 / current_downsample; - int downsampled_block_x1 = block_x1 / current_downsample; - int downsampled_block_x2 = block_x2 / current_downsample; - int downsampled_block_y1 = block_y1 / current_downsample; - int downsampled_block_y2 = block_y2 / current_downsample; - - //printf("MotionScan::init_packages %d %d\n", __LINE__, get_total_packages()); -// printf("MotionScan::init_packages %d current_downsample=%d scan_x1=%d scan_x2=%d block_x1=%d block_x2=%d\n", -// __LINE__, -// current_downsample, -// downsampled_scan_x1, -// downsampled_scan_x2, -// downsampled_block_x1, -// downsampled_block_x2); -// if(current_downsample == 8 && downsampled_scan_x1 == 47) -// { -// downsampled_previous->write_png("/tmp/previous"); -// downsampled_current->write_png("/tmp/current"); -// } - for(int i = 0; i < get_total_packages(); i++) { MotionScanPackage *pkg = (MotionScanPackage*)get_package(i); - pkg->block_x1 = downsampled_block_x1; - pkg->block_x2 = downsampled_block_x2; - pkg->block_y1 = downsampled_block_y1; - pkg->block_y2 = downsampled_block_y2; + pkg->block_x1 = block_x1; + pkg->block_x2 = block_x2; + pkg->block_y1 = block_y1; + pkg->block_y2 = block_y2; + pkg->scan_x1 = scan_x1; + pkg->scan_x2 = scan_x2; + pkg->scan_y1 = scan_y1; + pkg->scan_y2 = scan_y2; + pkg->step = i; pkg->difference1 = 0; pkg->difference2 = 0; pkg->dx = 0; pkg->dy = 0; pkg->valid = 1; - pkg->angle_step = 0; - + if(!subpixel) { - if(rotation_pass) - { - pkg->search_x = scan_x1 / current_downsample; - pkg->search_y = scan_y1 / current_downsample; - pkg->angle_step = i; - } - else - { - - int current_x_step = (i % x_steps); - int current_y_step = (i / x_steps); - - //printf("MotionScan::init_packages %d i=%d x_step=%d y_step=%d angle_step=%d\n", - //__LINE__, i, current_x_step, current_y_step, current_angle_step); - pkg->search_x = downsampled_scan_x1 + current_x_step * - (scan_x2 - scan_x1) / current_downsample / x_steps; - pkg->search_y = downsampled_scan_y1 + current_y_step * - (scan_y2 - scan_y1) / current_downsample / y_steps; - - if(do_rotate) - { - pkg->angle_step = angle_steps / 2; - } - else - { - pkg->angle_step = 0; - } - } - + pkg->search_x = pkg->scan_x1 + (pkg->step % x_steps) * + (scan_x2 - scan_x1) / x_steps; + pkg->search_y = pkg->scan_y1 + (pkg->step / x_steps) * + (scan_y2 - scan_y1) / y_steps; pkg->sub_x = 0; pkg->sub_y = 0; } else { - pkg->sub_x = i % (OVERSAMPLE * SUBPIXEL_RANGE); - pkg->sub_y = i / (OVERSAMPLE * SUBPIXEL_RANGE); - -// if(horizontal_only) -// { -// pkg->sub_y = 0; -// } -// -// if(vertical_only) -// { -// pkg->sub_x = 0; -// } - - pkg->search_x = scan_x1 + pkg->sub_x / OVERSAMPLE + 1; - pkg->search_y = scan_y1 + pkg->sub_y / OVERSAMPLE + 1; - pkg->sub_x %= OVERSAMPLE; - pkg->sub_y %= OVERSAMPLE; - - - -// printf("MotionScan::init_packages %d i=%d search_x=%d search_y=%d sub_x=%d sub_y=%d\n", -// __LINE__, -// i, -// pkg->search_x, -// pkg->search_y, -// pkg->sub_x, -// pkg->sub_y); - } - -// printf("MotionScan::init_packages %d %d,%d %d,%d %d,%d\n", -// __LINE__, -// scan_x1, -// scan_x2, -// scan_y1, -// scan_y2, -// pkg->search_x, -// pkg->search_y); - } -} - -LoadClient* MotionScan::new_client() -{ - return new MotionScanUnit(this); -} - -LoadPackage* MotionScan::new_package() -{ - return new MotionScanPackage; -} - - -void MotionScan::set_test_match(int value) -{ - this->test_match = value; -} - - - - -#define DOWNSAMPLE(type, temp_type, components, max) \ -{ \ - temp_type r; \ - temp_type g; \ - temp_type b; \ - temp_type a; \ - type **in_rows = (type**)src->get_rows(); \ - type **out_rows = (type**)dst->get_rows(); \ - \ - for(int i = 0; i < h; i += downsample) \ - { \ - int y1 = MAX(i, 0); \ - int y2 = MIN(i + downsample, h); \ - \ - \ - for(int j = 0; \ - j < w; \ - j += downsample) \ - { \ - int x1 = MAX(j, 0); \ - int x2 = MIN(j + downsample, w); \ - \ - temp_type scale = (x2 - x1) * (y2 - y1); \ - if(x2 > x1 && y2 > y1) \ - { \ - \ -/* Read in values */ \ - r = 0; \ - g = 0; \ - b = 0; \ - if(components == 4) a = 0; \ - \ - for(int k = y1; k < y2; k++) \ - { \ - type *row = in_rows[k] + x1 * components; \ - for(int l = x1; l < x2; l++) \ - { \ - r += *row++; \ - g += *row++; \ - b += *row++; \ - if(components == 4) a += *row++; \ - } \ - } \ - \ -/* Write average */ \ - r /= scale; \ - g /= scale; \ - b /= scale; \ - if(components == 4) a /= scale; \ - \ - type *row = out_rows[y1 / downsample] + \ - x1 / downsample * components; \ - *row++ = r; \ - *row++ = g; \ - *row++ = b; \ - if(components == 4) *row++ = a; \ - } \ - } \ -/*printf("DOWNSAMPLE 3 %d\n", i);*/ \ - } \ -} - - - - -void MotionScan::downsample_frame(VFrame *dst, - VFrame *src, - int downsample) -{ - int h = src->get_h(); - int w = src->get_w(); - -//PRINT_TRACE -//printf("downsample=%d w=%d h=%d dst=%d %d\n", downsample, w, h, dst->get_w(), dst->get_h()); - switch(src->get_color_model()) - { - case BC_RGB888: - DOWNSAMPLE(uint8_t, int64_t, 3, 0xff) - break; - case BC_RGB_FLOAT: - DOWNSAMPLE(float, float, 3, 1.0) - break; - case BC_RGBA8888: - DOWNSAMPLE(uint8_t, int64_t, 4, 0xff) - break; - case BC_RGBA_FLOAT: - DOWNSAMPLE(float, float, 4, 1.0) - break; - case BC_YUV888: - DOWNSAMPLE(uint8_t, int64_t, 3, 0xff) - break; - case BC_YUVA8888: - DOWNSAMPLE(uint8_t, int64_t, 4, 0xff) - break; - } -//PRINT_TRACE -} + pkg->sub_x = pkg->step % (OVERSAMPLE * 2); + pkg->sub_y = pkg->step / (OVERSAMPLE * 2); -double MotionScan::step_to_angle(int step, double center) -{ - if(step < angle_steps / 2) - { - return center - angle_step * (angle_steps / 2 - step); - } - else - if(step > angle_steps / 2) - { - return center + angle_step * (step - angle_steps / 2); - } - else - { - return center; - } -} - -#ifdef STDDEV_TEST -static int compare(const void *p1, const void *p2) -{ - double value1 = *(double*)p1; - double value2 = *(double*)p2; - -//printf("compare %d value1=%f value2=%f\n", __LINE__, value1, value2); - return value1 > value2; -} -#endif - -// reject vectors based on content. It's the reason Goog can't stabilize timelapses. -//#define STDDEV_TEST - -// pixel accurate motion search -void MotionScan::pixel_search(int &x_result, int &y_result, double &r_result) -{ -// reduce level of detail until enough steps - while(current_downsample > 1 && - ((block_x2 - block_x1) / current_downsample < MIN_DOWNSAMPLED_SIZE || - (block_y2 - block_y1) / current_downsample < MIN_DOWNSAMPLED_SIZE - || - (scan_x2 - scan_x1) / current_downsample < MIN_DOWNSAMPLED_SCAN || - (scan_y2 - scan_y1) / current_downsample < MIN_DOWNSAMPLED_SCAN - )) - { - current_downsample /= 2; - } - - - -// create downsampled images. -// Need to keep entire frame to search for rotation. - int downsampled_prev_w = previous_frame_arg->get_w() / current_downsample; - int downsampled_prev_h = previous_frame_arg->get_h() / current_downsample; - int downsampled_current_w = current_frame_arg->get_w() / current_downsample; - int downsampled_current_h = current_frame_arg->get_h() / current_downsample; - -// printf("MotionScan::pixel_search %d current_downsample=%d current_frame_arg->get_w()=%d downsampled_current_w=%d\n", -// __LINE__, -// current_downsample, -// current_frame_arg->get_w(), -// downsampled_current_w); - - x_steps = (scan_x2 - scan_x1) / current_downsample; - y_steps = (scan_y2 - scan_y1) / current_downsample; - -// in rads - double test_angle1 = atan2((double)downsampled_current_h / 2 - 1, (double)downsampled_current_w / 2); - double test_angle2 = atan2((double)downsampled_current_h / 2, (double)downsampled_current_w / 2 - 1); - -// in deg - angle_step = 360.0f * fabs(test_angle1 - test_angle2) / 2 / M_PI; - -// printf("MotionScan::pixel_search %d test_angle1=%f test_angle2=%f angle_step=%f\n", -// __LINE__, -// 360.0f * test_angle1 / 2 / M_PI, -// 360.0f * test_angle2 / 2 / M_PI, -// angle_step); - - - if(do_rotate && angle_step < rotation_range) - { - angle_steps = 1 + (int)((scan_angle2 - scan_angle1) / angle_step + 0.5); - } - else - { - angle_steps = 1; - } - - - if(current_downsample > 1) - { - if(!downsampled_previous || - downsampled_previous->get_w() != downsampled_prev_w || - downsampled_previous->get_h() != downsampled_prev_h) - { - delete downsampled_previous; - downsampled_previous = new VFrame(); - downsampled_previous->set_use_shm(0); - downsampled_previous->reallocate(0, - -1, - 0, - 0, - 0, - downsampled_prev_w + 1, - downsampled_prev_h + 1, - previous_frame_arg->get_color_model(), - -1); - } - - if(!downsampled_current || - downsampled_current->get_w() != downsampled_current_w || - downsampled_current->get_h() != downsampled_current_h) - { - delete downsampled_current; - downsampled_current = new VFrame(); - downsampled_current->set_use_shm(0); - downsampled_current->reallocate(0, - -1, - 0, - 0, - 0, - downsampled_current_w + 1, - downsampled_current_h + 1, - current_frame_arg->get_color_model(), - -1); - } - - - downsample_frame(downsampled_previous, - previous_frame_arg, - current_downsample); - downsample_frame(downsampled_current, - current_frame_arg, - current_downsample); - previous_frame = downsampled_previous; - current_frame = downsampled_current; - - } - else - { - previous_frame = previous_frame_arg; - current_frame = current_frame_arg; - } - - - -// printf("MotionScan::pixel_search %d x_steps=%d y_steps=%d angle_steps=%d total_steps=%d\n", -// __LINE__, -// x_steps, -// y_steps, -// angle_steps, -// total_steps); - - - -// test variance of constant macroblock - int color_model = current_frame->get_color_model(); - int pixel_size = BC_CModels::calculate_pixelsize(color_model); - int row_bytes = current_frame->get_bytes_per_line(); - int block_w = block_x2 - block_x1; - int block_h = block_y2 - block_y1; - - unsigned char *current_ptr = - current_frame->get_rows()[block_y1 / current_downsample] + - (block_x1 / current_downsample) * pixel_size; - unsigned char *previous_ptr = - previous_frame->get_rows()[scan_y1 / current_downsample] + - (scan_x1 / current_downsample) * pixel_size; - - - -// test detail in prev & current frame - double range1 = calculate_range(current_ptr, - row_bytes, - block_w / current_downsample, - block_h / current_downsample, - color_model); - - if(range1 < 1) - { -printf("MotionScan::pixel_search %d range fail range1=%f\n", __LINE__, range1); - failed = 1; - return; - } - - double range2 = calculate_range(previous_ptr, - row_bytes, - block_w / current_downsample, - block_h / current_downsample, - color_model); - - if(range2 < 1) - { -printf("MotionScan::pixel_search %d range fail range2=%f\n", __LINE__, range2); - failed = 1; - return; - } - - -// create rotated images - if(rotated_current && - (total_rotated != angle_steps || - rotated_current[0]->get_w() != downsampled_current_w || - rotated_current[0]->get_h() != downsampled_current_h)) - { - for(int i = 0; i < total_rotated; i++) - { - delete rotated_current[i]; - } - - delete [] rotated_current; - rotated_current = 0; - total_rotated = 0; - } - - if(do_rotate) - { - total_rotated = angle_steps; - - - if(!rotated_current) - { - rotated_current = new VFrame*[total_rotated]; - bzero(rotated_current, sizeof(VFrame*) * total_rotated); - } - -// printf("MotionScan::pixel_search %d total_rotated=%d w=%d h=%d block_w=%d block_h=%d\n", -// __LINE__, -// total_rotated, -// downsampled_current_w, -// downsampled_current_h, -// (block_x2 - block_x1) / current_downsample, -// (block_y2 - block_y1) / current_downsample); - for(int i = 0; i < angle_steps; i++) - { - -// printf("MotionScan::pixel_search %d w=%d h=%d x=%d y=%d angle=%f\n", -// __LINE__, -// downsampled_current_w, -// downsampled_current_h, -// (block_x1 + block_x2) / 2 / current_downsample, -// (block_y1 + block_y2) / 2 / current_downsample, -// step_to_angle(i, r_result)); - -// printf("MotionScan::pixel_search %d i=%d rotated_current[i]=%p\n", -// __LINE__, -// i, -// rotated_current[i]); - if(!rotated_current[i]) + if(horizontal_only) { - rotated_current[i] = new VFrame(); - rotated_current[i]->set_use_shm(0); - rotated_current[i]->reallocate(0, - -1, - 0, - 0, - 0, - downsampled_current_w + 1, - downsampled_current_h + 1, - current_frame_arg->get_color_model(), - -1); -//printf("MotionScan::pixel_search %d\n", __LINE__); + pkg->sub_y = 0; } - - if(!rotater) + if(vertical_only) { - rotater = new AffineEngine(get_total_clients(), - get_total_clients()); + pkg->sub_x = 0; } -// get smallest viewport size required for the angle - double diag = hypot((block_x2 - block_x1) / current_downsample, - (block_y2 - block_y1) / current_downsample); - double angle1 = atan2(block_y2 - block_y1, block_x2 - block_x1) + - TO_RAD(step_to_angle(i, r_result)); - double angle2 = -atan2(block_y2 - block_y1, block_x2 - block_x1) + - TO_RAD(step_to_angle(i, r_result)); - double max_horiz = MAX(abs(diag * cos(angle1)), abs(diag * cos(angle2))); - double max_vert = MAX(abs(diag * sin(angle1)), abs(diag * sin(angle2))); - int center_x = (block_x1 + block_x2) / 2 / current_downsample; - int center_y = (block_y1 + block_y2) / 2 / current_downsample; - int x1 = center_x - max_horiz / 2; - int y1 = center_y - max_vert / 2; - int x2 = x1 + max_horiz; - int y2 = y1 + max_vert; - CLAMP(x1, 0, downsampled_current_w - 1); - CLAMP(y1, 0, downsampled_current_h - 1); - CLAMP(x2, 0, downsampled_current_w - 1); - CLAMP(y2, 0, downsampled_current_h - 1); - -//printf("MotionScan::pixel_search %d %f %f %d %d\n", -//__LINE__, TO_DEG(angle1), TO_DEG(angle2), (int)max_horiz, (int)max_vert); - rotater->set_in_viewport(x1, - y1, - x2 - x1, - y2 - y1); - rotater->set_out_viewport(x1, - y1, - x2 - x1, - y2 - y1); - -// rotater->set_in_viewport(0, -// 0, -// downsampled_current_w, -// downsampled_current_h); -// rotater->set_out_viewport(0, -// 0, -// downsampled_current_w, -// downsampled_current_h); - - rotater->set_in_pivot(center_x, center_y); - rotater->set_out_pivot(center_x, center_y); - - rotater->rotate(rotated_current[i], - current_frame, - step_to_angle(i, r_result)); - -// rotated_current[i]->draw_rect(block_x1 / current_downsample, -// block_y1 / current_downsample, -// block_x2 / current_downsample, -// block_y2 / current_downsample); -// char string[BCTEXTLEN]; -// sprintf(string, "/tmp/rotated%d", i); -// rotated_current[i]->write_png(string); -//downsampled_previous->write_png("/tmp/previous"); -//printf("MotionScan::pixel_search %d\n", __LINE__); - } - } - - - - - - -// printf("MotionScan::pixel_search %d block x=%d y=%d w=%d h=%d\n", -// __LINE__, -// block_x1 / current_downsample, -// block_y1 / current_downsample, -// block_w / current_downsample, -// block_h / current_downsample); - - - - - - - -//exit(1); -// Test only translation of the middle rotated frame - rotation_pass = 0; - total_steps = x_steps * y_steps; - set_package_count(total_steps); - process_packages(); - - - - - - -// Get least difference - int64_t min_difference = -1; -#ifdef STDDEV_TEST - double stddev_table[get_total_packages()]; -#endif - for(int i = 0; i < get_total_packages(); i++) - { - MotionScanPackage *pkg = (MotionScanPackage*)get_package(i); - -#ifdef STDDEV_TEST - double stddev = sqrt(pkg->difference1) / - (block_w / current_downsample) / - (block_h / current_downsample) / - 3; -// printf("MotionScan::pixel_search %d current_downsample=%d search_x=%d search_y=%d diff1=%f\n", -// __LINE__, -// current_downsample, -// pkg->search_x, -// pkg->search_y, -// sqrt(pkg->difference1) / block_w / current_downsample / block_h / 3 /* / variance */); - -// printf("MotionScan::pixel_search %d range1=%f stddev=%f\n", -// __LINE__, -// range1, -// stddev); - - stddev_table[i] = stddev; -#endif // STDDEV_TEST - - if(pkg->difference1 < min_difference || i == 0) - { - min_difference = pkg->difference1; - x_result = pkg->search_x * current_downsample * OVERSAMPLE; - y_result = pkg->search_y * current_downsample * OVERSAMPLE; - -// printf("MotionScan::pixel_search %d x_result=%d y_result=%d angle_step=%d diff=%lld\n", -// __LINE__, -// block_x1 * OVERSAMPLE - x_result, -// block_y1 * OVERSAMPLE - y_result, -// pkg->angle_step, -// pkg->difference1); - - } - } - - -#ifdef STDDEV_TEST - qsort(stddev_table, get_total_packages(), sizeof(double), compare); - - -// reject motion vector if not similar enough -// if(stddev_table[0] > 0.2) -// { -// if(debug) -// { -// printf("MotionScan::pixel_search %d stddev fail min_stddev=%f\n", -// __LINE__, -// stddev_table[0]); -// } -// failed = 1; -// return; -// } - -if(debug) -{ - printf("MotionScan::pixel_search %d\n", __LINE__); - for(int i = 0; i < get_total_packages(); i++) - { - printf("%f\n", stddev_table[i]); - } -} - -// reject motion vector if not a sigmoid curve -// TODO: use linear interpolation - int steps = 2; - int step = get_total_packages() / steps; - double curve[steps]; - for(int i = 0; i < steps; i++) - { - int start = get_total_packages() * i / steps; - int end = get_total_packages() * (i + 1) / steps; - end = MIN(end, get_total_packages() - 1); - curve[i] = stddev_table[end] - stddev_table[start]; - } - - -// if(curve[0] < (curve[1] * 1.01) || -// curve[2] < (curve[1] * 1.01) || -// curve[0] < (curve[2] * 0.75)) -// if(curve[0] < curve[1]) -// { -// if(debug) -// { -// printf("MotionScan::pixel_search %d curve fail %f %f\n", -// __LINE__, -// curve[0], -// curve[1]); -// } -// failed = 1; -// return; -// } - -if(debug) -{ -printf("MotionScan::pixel_search %d curve=%f %f ranges=%f %f min_stddev=%f\n", -__LINE__, -curve[0], -curve[1], -range1, -range2, -stddev_table[0]); -} -#endif // STDDEV_TEST - - - - - - if(do_rotate) - { - rotation_pass = 1;; - total_steps = angle_steps; - scan_x1 = x_result / OVERSAMPLE; - scan_y1 = y_result / OVERSAMPLE; - set_package_count(total_steps); - process_packages(); - + pkg->search_x = pkg->scan_x1 + pkg->sub_x / OVERSAMPLE + 1; + pkg->search_y = pkg->scan_y1 + pkg->sub_y / OVERSAMPLE + 1; + pkg->sub_x %= OVERSAMPLE; + pkg->sub_y %= OVERSAMPLE; - min_difference = -1; - double prev_r_result = r_result; - for(int i = 0; i < get_total_packages(); i++) - { - MotionScanPackage *pkg = (MotionScanPackage*)get_package(i); -// printf("MotionScan::pixel_search %d search_x=%d search_y=%d angle_step=%d sub_x=%d sub_y=%d diff1=%lld diff2=%lld\n", +// printf("MotionScan::init_packages %d i=%d search_x=%d search_y=%d sub_x=%d sub_y=%d\n", // __LINE__, +// i, // pkg->search_x, // pkg->search_y, -// pkg->search_angle_step, // pkg->sub_x, -// pkg->sub_y, -// pkg->difference1, -// pkg->difference2); - if(pkg->difference1 < min_difference || i == 0) - { - min_difference = pkg->difference1; - r_result = step_to_angle(i, prev_r_result); - - // printf("MotionScan::pixel_search %d x_result=%d y_result=%d angle_step=%d diff=%lld\n", - // __LINE__, - // block_x1 * OVERSAMPLE - x_result, - // block_y1 * OVERSAMPLE - y_result, - // pkg->angle_step, - // pkg->difference1); - } +// pkg->sub_y); } - } - -// printf("MotionScan::scan_frame %d current_downsample=%d x_result=%f y_result=%f r_result=%f\n", +// printf("MotionScan::init_packages %d %d,%d %d,%d %d,%d\n", // __LINE__, -// current_downsample, -// (float)x_result / OVERSAMPLE, -// (float)y_result / OVERSAMPLE, -// r_result); - -} - - -// subpixel motion search -void MotionScan::subpixel_search(int &x_result, int &y_result) -{ - rotation_pass = 0; - previous_frame = previous_frame_arg; - current_frame = current_frame_arg; - -//printf("MotionScan::scan_frame %d %d %d\n", __LINE__, x_result, y_result); -// Scan every subpixel in a SUBPIXEL_RANGE * SUBPIXEL_RANGE square - total_steps = (SUBPIXEL_RANGE * OVERSAMPLE) * (SUBPIXEL_RANGE * OVERSAMPLE); - -// These aren't used in subpixel - x_steps = OVERSAMPLE * SUBPIXEL_RANGE; - y_steps = OVERSAMPLE * SUBPIXEL_RANGE; - angle_steps = 1; - - set_package_count(this->total_steps); - process_packages(); - -// Get least difference - int64_t min_difference = -1; - for(int i = 0; i < get_total_packages(); i++) - { - MotionScanPackage *pkg = (MotionScanPackage*)get_package(i); -//printf("MotionScan::scan_frame %d search_x=%d search_y=%d sub_x=%d sub_y=%d diff1=%lld diff2=%lld\n", -//__LINE__, pkg->search_x, pkg->search_y, pkg->sub_x, pkg->sub_y, pkg->difference1, pkg->difference2); - if(pkg->difference1 < min_difference || min_difference == -1) - { - min_difference = pkg->difference1; - -// The sub coords are 1 pixel up & left of the block coords - x_result = pkg->search_x * OVERSAMPLE + pkg->sub_x; - y_result = pkg->search_y * OVERSAMPLE + pkg->sub_y; - +// scan_x1, +// scan_x2, +// scan_y1, +// scan_y2, +// pkg->search_x, +// pkg->search_y); + } +} -// Fill in results - dx_result = block_x1 * OVERSAMPLE - x_result; - dy_result = block_y1 * OVERSAMPLE - y_result; -//printf("MotionScan::scan_frame %d dx_result=%d dy_result=%d diff=%lld\n", -//__LINE__, dx_result, dy_result, min_difference); - } +LoadClient* MotionScan::new_client() +{ + return new MotionScanUnit(this); +} - if(pkg->difference2 < min_difference) - { - min_difference = pkg->difference2; +LoadPackage* MotionScan::new_package() +{ + return new MotionScanPackage; +} - x_result = pkg->search_x * OVERSAMPLE - pkg->sub_x; - y_result = pkg->search_y * OVERSAMPLE - pkg->sub_y; - dx_result = block_x1 * OVERSAMPLE - x_result; - dy_result = block_y1 * OVERSAMPLE - y_result; -//printf("MotionScan::scan_frame %d dx_result=%d dy_result=%d diff=%lld\n", -//__LINE__, dx_result, dy_result, min_difference); - } - } +void MotionScan::set_test_match(int value) +{ + this->test_match = value; } - void MotionScan::scan_frame(VFrame *previous_frame, VFrame *current_frame, int global_range_w, int global_range_h, int global_block_w, int global_block_h, - int block_x, - int block_y, + double block_x, + double block_y, int frame_type, int tracking_type, int action_type, int horizontal_only, int vertical_only, int source_position, + int total_steps, int total_dx, int total_dy, int global_origin_x, - int global_origin_y, - int do_motion, - int do_rotate, - double rotation_center, - double rotation_range) + int global_origin_y) { this->previous_frame_arg = previous_frame; this->current_frame_arg = current_frame; @@ -1085,51 +351,25 @@ void MotionScan::scan_frame(VFrame *previous_frame, this->current_frame = current_frame_arg; this->global_origin_x = global_origin_x; this->global_origin_y = global_origin_y; - this->action_type = action_type; - this->do_motion = do_motion; - this->do_rotate = do_rotate; - this->rotation_center = rotation_center; - this->rotation_range = rotation_range; - -//printf("MotionScan::scan_frame %d\n", __LINE__); - dx_result = 0; - dy_result = 0; - dr_result = 0; - failed = 0; - subpixel = 0; -// starting level of detail -// TODO: base it on a table of resolutions - current_downsample = STARTING_DOWNSAMPLE; - angle_step = 0; + + cache.remove_all_objects(); // Single macroblock int w = current_frame->get_w(); int h = current_frame->get_h(); // Initial search parameters - scan_w = global_range_w; - scan_h = global_range_h; - - int block_w = global_block_w; - int block_h = global_block_h; - -// printf("MotionScan::scan_frame %d %d %d %d %d %d %d %d %d\n", -// __LINE__, -// global_range_w, -// global_range_h, -// global_block_w, -// global_block_h, -// scan_w, -// scan_h, -// block_w, -// block_h); + int scan_w = w * global_range_w / 100; + int scan_h = h * global_range_h / 100; + int block_w = w * global_block_w / 100; + int block_h = h * global_block_h / 100; // Location of block in previous frame - block_x1 = (int)(block_x - block_w / 2); - block_y1 = (int)(block_y - block_h / 2); - block_x2 = (int)(block_x + block_w / 2); - block_y2 = (int)(block_y + block_h / 2); + block_x1 = (int)(w * block_x / 100 - block_w / 2); + block_y1 = (int)(h * block_y / 100 - block_h / 2); + block_x2 = (int)(w * block_x / 100 + block_w / 2); + block_y2 = (int)(h * block_y / 100 + block_h / 2); // Offset to location of previous block. This offset needn't be very accurate // since it's the offset of the previous image and current image we want. @@ -1149,7 +389,6 @@ void MotionScan::scan_frame(VFrame *previous_frame, case MotionScan::NO_CALCULATE: dx_result = 0; dy_result = 0; - dr_result = rotation_center; skip = 1; break; @@ -1157,55 +396,21 @@ void MotionScan::scan_frame(VFrame *previous_frame, { // Load result from disk char string[BCTEXTLEN]; - - skip = 1; - if(do_motion) - { - sprintf(string, "%s%06d", - MOTION_FILE, - source_position); + sprintf(string, "%s%06d", + MOTION_FILE, + source_position); //printf("MotionScan::scan_frame %d %s\n", __LINE__, string); - FILE *input = fopen(string, "r"); - if(input) - { - int temp = fscanf(input, - "%d %d", - &dx_result, - &dy_result); - if( temp != 2 ) - printf("MotionScan::scan_frame %d %s\n", __LINE__, string); + FILE *input = fopen(string, "r"); + if(input) + { + (void)fscanf(input, "%d %d", + &dx_result, &dy_result); // HACK //dx_result *= 2; //dy_result *= 2; //printf("MotionScan::scan_frame %d %d %d\n", __LINE__, dx_result, dy_result); - fclose(input); - } - else - { - skip = 0; - } - } - - if(do_rotate) - { - sprintf(string, - "%s%06d", - ROTATION_FILE, - source_position); - FILE *input = fopen(string, "r"); - if(input) - { - int temp = fscanf(input, "%f", &dr_result); - if( temp != 1 ) - printf("MotionScan::scan_frame %d %s\n", __LINE__, string); -// DEBUG -//dr_result += 0.25; - fclose(input); - } - else - { - skip = 0; - } + fclose(input); + skip = 1; } break; } @@ -1216,8 +421,6 @@ void MotionScan::scan_frame(VFrame *previous_frame, break; } - - if(!skip && test_match) { if(previous_frame->data_matches(current_frame)) @@ -1225,21 +428,19 @@ void MotionScan::scan_frame(VFrame *previous_frame, printf("MotionScan::scan_frame: data matches. skipping.\n"); dx_result = 0; dy_result = 0; - dr_result = rotation_center; skip = 1; } } - // Perform scan if(!skip) { +//printf("MotionScan::scan_frame %d\n", __LINE__); // Location of block in current frame - int origin_offset_x = this->global_origin_x; - int origin_offset_y = this->global_origin_y; + int origin_offset_x = this->global_origin_x * w / 100; + int origin_offset_y = this->global_origin_y * h / 100; int x_result = block_x1 + origin_offset_x; int y_result = block_y1 + origin_offset_y; - double r_result = rotation_center; // printf("MotionScan::scan_frame 1 %d %d %d %d %d %d %d %d\n", // block_x1 + block_w / 2, @@ -1251,31 +452,32 @@ printf("MotionScan::scan_frame: data matches. skipping.\n"); // block_x2, // block_y2); - while(!failed) + while(1) { +// Cache needs to be cleared if downsampling is used because the sums of +// different downsamplings can't be compared. +// Subpixel never uses the cache. +// cache.remove_all_objects(); scan_x1 = x_result - scan_w / 2; scan_y1 = y_result - scan_h / 2; scan_x2 = x_result + scan_w / 2; scan_y2 = y_result + scan_h / 2; - scan_angle1 = r_result - rotation_range; - scan_angle2 = r_result + rotation_range; // Zero out requested values -// if(horizontal_only) -// { -// scan_y1 = block_y1; -// scan_y2 = block_y1 + 1; -// } -// if(vertical_only) -// { -// scan_x1 = block_x1; -// scan_x2 = block_x1 + 1; -// } - -// printf("MotionScan::scan_frame %d block_x1=%d block_y1=%d block_x2=%d block_y2=%d scan_x1=%d scan_y1=%d scan_x2=%d scan_y2=%d\n", -// __LINE__, + if(horizontal_only) + { + scan_y1 = block_y1; + scan_y2 = block_y1 + 1; + } + if(vertical_only) + { + scan_x1 = block_x1; + scan_x2 = block_x1 + 1; + } + +// printf("MotionScan::scan_frame 1 %d %d %d %d %d %d %d %d\n", // block_x1, // block_y1, // block_x2, @@ -1284,11 +486,9 @@ printf("MotionScan::scan_frame: data matches. skipping.\n"); // scan_y1, // scan_x2, // scan_y2); - - // Clamp the block coords before the scan so we get useful scan coords. - clamp_scan(w, - h, + clamp_scan(w, + h, &block_x1, &block_y1, &block_x2, @@ -1298,21 +498,18 @@ printf("MotionScan::scan_frame: data matches. skipping.\n"); &scan_x2, &scan_y2, 0); - - -// printf("MotionScan::scan_frame %d block_x1=%d block_y1=%d block_x2=%d block_y2=%d scan_x1=%d scan_y1=%d scan_x2=%d scan_y2=%d x_result=%d y_result=%d\n", +// printf("MotionScan::scan_frame 1 %d block_x1=%d block_y1=%d block_x2=%d block_y2=%d\n scan_x1=%d scan_y1=%d scan_x2=%d scan_y2=%d\n x_result=%d y_result=%d\n", // __LINE__, // block_x1, // block_y1, // block_x2, // block_y2, -// scan_x1, -// scan_y1, -// scan_x2, -// scan_y2, -// x_result, +// scan_x1, +// scan_y1, +// scan_x2, +// scan_y2, +// x_result, // y_result); -//if(y_result == 88) exit(0); // Give up if invalid coords. @@ -1320,49 +517,179 @@ printf("MotionScan::scan_frame: data matches. skipping.\n"); scan_x2 <= scan_x1 || block_x2 <= block_x1 || block_y2 <= block_y1) - { break; - } // For subpixel, the top row and left column are skipped if(subpixel) { - subpixel_search(x_result, y_result); -// printf("MotionScan::scan_frame %d x_result=%d y_result=%d\n", -// __LINE__, -// x_result / OVERSAMPLE, -// y_result / OVERSAMPLE); +//printf("MotionScan::scan_frame %d %d %d\n", __LINE__, x_result, y_result); +// Scan every subpixel in a 2 pixel * 2 pixel square + total_pixels = (2 * OVERSAMPLE) * (2 * OVERSAMPLE); + + this->total_steps = total_pixels; +// These aren't used in subpixel + this->x_steps = OVERSAMPLE * 2; + this->y_steps = OVERSAMPLE * 2; + + set_package_count(this->total_steps); + process_packages(); + +// Get least difference + int64_t min_difference = -1; + for(int i = 0; i < get_total_packages(); i++) + { + MotionScanPackage *pkg = (MotionScanPackage*)get_package(i); +//printf("MotionScan::scan_frame %d search_x=%d search_y=%d sub_x=%d sub_y=%d diff1=%lld diff2=%lld\n", +//__LINE__, pkg->search_x, pkg->search_y, pkg->sub_x, pkg->sub_y, pkg->difference1, pkg->difference2); + if(pkg->difference1 < min_difference || min_difference == -1) + { + min_difference = pkg->difference1; + +// The sub coords are 1 pixel up & left of the block coords + x_result = pkg->search_x * OVERSAMPLE + pkg->sub_x; + y_result = pkg->search_y * OVERSAMPLE + pkg->sub_y; + + +// Fill in results + dx_result = block_x1 * OVERSAMPLE - x_result; + dy_result = block_y1 * OVERSAMPLE - y_result; +//printf("MotionScan::scan_frame %d dx_result=%d dy_result=%d diff=%lld\n", +//__LINE__, dx_result, dy_result, min_difference); + } + + if(pkg->difference2 < min_difference) + { + min_difference = pkg->difference2; + + x_result = pkg->search_x * OVERSAMPLE - pkg->sub_x; + y_result = pkg->search_y * OVERSAMPLE - pkg->sub_y; + + dx_result = block_x1 * OVERSAMPLE - x_result; + dy_result = block_y1 * OVERSAMPLE - y_result; +//printf("MotionScan::scan_frame %d dx_result=%d dy_result=%d diff=%lld\n", +//__LINE__, dx_result, dy_result, min_difference); + } + } break; } else // Single pixel { - pixel_search(x_result, y_result, r_result); -//printf("MotionScan::scan_frame %d x_result=%d y_result=%d\n", __LINE__, x_result / OVERSAMPLE, y_result / OVERSAMPLE); + total_pixels = (scan_x2 - scan_x1) * (scan_y2 - scan_y1); + this->total_steps = MIN(total_steps, total_pixels); - if(failed) + if(this->total_steps == total_pixels) { - dr_result = 0; - dx_result = 0; - dy_result = 0; + x_steps = scan_x2 - scan_x1; + y_steps = scan_y2 - scan_y1; } else - if(current_downsample <= 1) { - // Single pixel accuracy reached. Now do exhaustive subpixel search. + x_steps = (int)sqrt(this->total_steps); + y_steps = (int)sqrt(this->total_steps); + } + +// Use downsampled images +// if(scan_x2 - scan_x1 > x_steps * 4 || +// scan_y2 - scan_y1 > y_steps * 4) +// { +// printf("MotionScan::scan_frame %d total_pixels=%d total_steps=%d x_steps=%d y_steps=%d x y steps=%d\n", +// __LINE__, +// total_pixels, +// total_steps, +// x_steps, +// y_steps, +// x_steps * y_steps); +// +// if(!downsampled_previous || +// !downsampled_previous->equivalent(previous_frame_arg)) +// { +// delete downsampled_previous; +// downsampled_previous = new VFrame(*previous_frame_arg); +// } +// +// if(!downsampled_current || +// !downsampled_current->equivalent(current_frame_arg)) +// { +// delete downsampled_current; +// downsampled_current = new VFrame(*current_frame_arg); +// } +// +// +// if(!downsample) +// downsample = new DownSampleServer(get_total_clients(), +// get_total_clients()); +// downsample->process_frame(downsampled_previous, +// previous_frame_arg, +// 1, +// 1, +// 1, +// 1, +// (scan_y2 - scan_y1) / y_steps, +// (scan_x2 - scan_x1) / x_steps, +// 0, +// 0); +// downsample->process_frame(downsampled_current, +// current_frame_arg, +// 1, +// 1, +// 1, +// 1, +// (scan_y2 - scan_y1) / y_steps, +// (scan_x2 - scan_x1) / x_steps, +// 0, +// 0); +// this->previous_frame = downsampled_previous; +// this->current_frame = downsampled_current; +// } + + + + + +// printf("MotionScan::scan_frame %d this->total_steps=%d\n", +// __LINE__, +// this->total_steps); + + + set_package_count(this->total_steps); + process_packages(); + +// Get least difference + int64_t min_difference = -1; + for(int i = 0; i < get_total_packages(); i++) + { + MotionScanPackage *pkg = (MotionScanPackage*)get_package(i); +//printf("MotionScan::scan_frame %d search_x=%d search_y=%d sub_x=%d sub_y=%d diff1=%lld diff2=%lld\n", +//__LINE__, pkg->search_x, pkg->search_y, pkg->sub_x, pkg->sub_y, pkg->difference1, pkg->difference2); + if(pkg->difference1 < min_difference || min_difference == -1) + { + min_difference = pkg->difference1; + x_result = pkg->search_x; + y_result = pkg->search_y; + x_result *= OVERSAMPLE; + y_result *= OVERSAMPLE; +//printf("MotionScan::scan_frame %d x_result=%d y_result=%d diff=%lld\n", +//__LINE__, block_x1 * OVERSAMPLE - x_result, block_y1 * OVERSAMPLE - y_result, pkg->difference1); + } + } + + +// If a new search is required, rescale results back to pixels. + if(this->total_steps >= total_pixels) + { +// Single pixel accuracy reached. Now do exhaustive subpixel search. if(action_type == MotionScan::STABILIZE || action_type == MotionScan::TRACK || action_type == MotionScan::NOTHING) { +//printf("MotionScan::scan_frame %d %d %d\n", __LINE__, x_result, y_result); x_result /= OVERSAMPLE; y_result /= OVERSAMPLE; -//printf("MotionScan::scan_frame %d x_result=%d y_result=%d\n", __LINE__, x_result, y_result); - scan_w = SUBPIXEL_RANGE; - scan_h = SUBPIXEL_RANGE; -// Final R result - dr_result = rotation_center - r_result; + scan_w = 2; + scan_h = 2; subpixel = 1; } else @@ -1370,109 +697,59 @@ printf("MotionScan::scan_frame: data matches. skipping.\n"); // Fill in results and quit dx_result = block_x1 * OVERSAMPLE - x_result; dy_result = block_y1 * OVERSAMPLE - y_result; - dr_result = rotation_center - r_result; +//printf("MotionScan::scan_frame %d %d %d\n", __LINE__, dx_result, dy_result); break; } } else // Reduce scan area and try again { -// scan_w = (scan_x2 - scan_x1) / 2; -// scan_h = (scan_y2 - scan_y1) / 2; -// need slightly more than 2x downsampling factor - - if(current_downsample * 3 < scan_w && - current_downsample * 3 < scan_h) - { - scan_w = current_downsample * 3; - scan_h = current_downsample * 3; - } - - if(angle_step * 1.5 < rotation_range) - { - rotation_range = angle_step * 1.5; - } -//printf("MotionScan::scan_frame %d %f %f\n", __LINE__, angle_step, rotation_range); - - current_downsample /= 2; - -// convert back to pixels + scan_w = (scan_x2 - scan_x1) / 2; + scan_h = (scan_y2 - scan_y1) / 2; x_result /= OVERSAMPLE; y_result /= OVERSAMPLE; -// debug -//exit(1); } - } } dx_result *= -1; dy_result *= -1; - dr_result *= -1; } -// printf("MotionScan::scan_frame %d dx=%f dy=%f dr=%f\n", -// __LINE__, -// (float)dx_result / OVERSAMPLE, -// (float)dy_result / OVERSAMPLE, -// dr_result); +//printf("MotionScan::scan_frame %d\n", __LINE__); + if(vertical_only) dx_result = 0; + if(horizontal_only) dy_result = 0; + // Write results - if(!skip && tracking_type == MotionScan::SAVE) + if(tracking_type == MotionScan::SAVE) { char string[BCTEXTLEN]; - - - if(do_motion) + sprintf(string, + "%s%06d", + MOTION_FILE, + source_position); + FILE *output = fopen(string, "w"); + if(output) { - sprintf(string, - "%s%06d", - MOTION_FILE, - source_position); - FILE *output = fopen(string, "w"); - if(output) - { - fprintf(output, - "%d %d\n", - dx_result, - dy_result); - fclose(output); - } - else - { - printf("MotionScan::scan_frame %d: save motion failed\n", __LINE__); - } + fprintf(output, + "%d %d\n", + dx_result, + dy_result); + fclose(output); } - - if(do_rotate) + else { - sprintf(string, - "%s%06d", - ROTATION_FILE, - source_position); - FILE *output = fopen(string, "w"); - if(output) - { - fprintf(output, "%f\n", dr_result); - fclose(output); - } - else - { - printf("MotionScan::scan_frame %d save rotation failed\n", __LINE__); - } + printf("MotionScan::scan_frame %d: save coordinate failed", __LINE__); } } - - if(vertical_only) dx_result = 0; - if(horizontal_only) dy_result = 0; - -// printf("MotionScan::scan_frame %d dx=%d dy=%d\n", +// printf("MotionScan::scan_frame %d dx=%.2f dy=%.2f\n", // __LINE__, -// this->dx_result, -// this->dy_result); +// (float)this->dx_result / OVERSAMPLE, +// (float)this->dy_result / OVERSAMPLE); } @@ -1491,6 +768,31 @@ printf("MotionScan::scan_frame: data matches. skipping.\n"); +int64_t MotionScan::get_cache(int x, int y) +{ + int64_t result = -1; + cache_lock->lock("MotionScan::get_cache"); + for(int i = 0; i < cache.total; i++) + { + MotionScanCache *ptr = cache.values[i]; + if(ptr->x == x && ptr->y == y) + { + result = ptr->difference; + break; + } + } + cache_lock->unlock(); + return result; +} + +void MotionScan::put_cache(int x, int y, int64_t difference) +{ + MotionScanCache *ptr = new MotionScanCache(x, y, difference); + cache_lock->lock("MotionScan::put_cache"); + cache.append(ptr); + cache_lock->unlock(); +} + #define ABS_DIFF(type, temp_type, multiplier, components) \ @@ -1506,8 +808,10 @@ printf("MotionScan::scan_frame: data matches. skipping.\n"); { \ temp_type difference; \ difference = *prev_row++ - *current_row++; \ - difference *= difference; \ - result_temp += difference; \ + if(difference < 0) \ + result_temp -= difference; \ + else \ + result_temp += difference; \ } \ if(components == 4) \ { \ @@ -1549,6 +853,12 @@ int64_t MotionScan::abs_diff(unsigned char *prev_ptr, case BC_YUVA8888: ABS_DIFF(unsigned char, int64_t, 1, 4) break; + case BC_YUV161616: + ABS_DIFF(uint16_t, int64_t, 1, 3) + break; + case BC_YUVA16161616: + ABS_DIFF(uint16_t, int64_t, 1, 4) + break; } return result; } @@ -1583,8 +893,10 @@ int64_t MotionScan::abs_diff(unsigned char *prev_ptr, 0x100 / 0x100; \ temp_type current_value = *current_row++; \ difference = prev_value - current_value; \ - difference *= difference; \ - result_temp += difference; \ + if(difference < 0) \ + result_temp -= difference; \ + else \ + result_temp += difference; \ } \ \ /* skip alpha */ \ @@ -1639,161 +951,31 @@ int64_t MotionScan::abs_diff_sub(unsigned char *prev_ptr, case BC_YUVA8888: ABS_DIFF_SUB(unsigned char, int64_t, 1, 4) break; - } - return result; -} - - -#if 0 -#define VARIANCE(type, temp_type, multiplier, components) \ -{ \ - temp_type average[3] = { 0 }; \ - temp_type variance[3] = { 0 }; \ - \ - for(int i = 0; i < h; i++) \ - { \ - type *row = (type*)current_ptr + i * row_bytes; \ - for(int j = 0; j < w; j++) \ - { \ - for(int k = 0; k < 3; k++) \ - { \ - average[k] += row[k]; \ - } \ - row += components; \ - } \ - } \ - for(int k = 0; k < 3; k++) \ - { \ - average[k] /= w * h; \ - } \ - \ - for(int i = 0; i < h; i++) \ - { \ - type *row = (type*)current_ptr + i * row_bytes; \ - for(int j = 0; j < w; j++) \ - { \ - for(int k = 0; k < 3; k++) \ - { \ - variance[k] += SQR(row[k] - average[k]); \ - } \ - row += components; \ - } \ - } \ - result = (double)multiplier * \ - sqrt((variance[0] + variance[1] + variance[2]) / w / h / 3); \ -} - -double MotionScan::calculate_variance(unsigned char *current_ptr, - int row_bytes, - int w, - int h, - int color_model) -{ - double result = 0; - - switch(color_model) - { - case BC_RGB888: - VARIANCE(unsigned char, int, 1, 3) - break; - case BC_RGBA8888: - VARIANCE(unsigned char, int, 1, 4) - break; - case BC_RGB_FLOAT: - VARIANCE(float, double, 255, 3) - break; - case BC_RGBA_FLOAT: - VARIANCE(float, double, 255, 4) - break; - case BC_YUV888: - VARIANCE(unsigned char, int, 1, 3) + case BC_YUV161616: + ABS_DIFF_SUB(uint16_t, int64_t, 1, 3) break; - case BC_YUVA8888: - VARIANCE(unsigned char, int, 1, 4) + case BC_YUVA16161616: + ABS_DIFF_SUB(uint16_t, int64_t, 1, 4) break; } - - return result; } -#endif // 0 -#define RANGE(type, temp_type, multiplier, components) \ -{ \ - temp_type min[3]; \ - temp_type max[3]; \ - min[0] = 0x7fff; \ - min[1] = 0x7fff; \ - min[2] = 0x7fff; \ - max[0] = 0; \ - max[1] = 0; \ - max[2] = 0; \ - \ - for(int i = 0; i < h; i++) \ - { \ - type *row = (type*)current_ptr + i * row_bytes; \ - for(int j = 0; j < w; j++) \ - { \ - for(int k = 0; k < 3; k++) \ - { \ - if(row[k] > max[k]) max[k] = row[k]; \ - if(row[k] < min[k]) min[k] = row[k]; \ - } \ - row += components; \ - } \ - } \ - \ - for(int k = 0; k < 3; k++) \ - { \ - /* printf("MotionScan::calculate_range %d k=%d max=%d min=%d\n", __LINE__, k, max[k], min[k]); */ \ - if(max[k] - min[k] > result) result = max[k] - min[k]; \ - } \ - \ -} -double MotionScan::calculate_range(unsigned char *current_ptr, - int row_bytes, - int w, - int h, - int color_model) +MotionScanCache::MotionScanCache(int x, int y, int64_t difference) { - double result = 0; - - switch(color_model) - { - case BC_RGB888: - RANGE(unsigned char, int, 1, 3) - break; - case BC_RGBA8888: - RANGE(unsigned char, int, 1, 4) - break; - case BC_RGB_FLOAT: - RANGE(float, float, 255, 3) - break; - case BC_RGBA_FLOAT: - RANGE(float, float, 255, 4) - break; - case BC_YUV888: - RANGE(unsigned char, int, 1, 3) - break; - case BC_YUVA8888: - RANGE(unsigned char, int, 1, 4) - break; - } - - - return result; + this->x = x; + this->y = y; + this->difference = difference; } -//#define CLAMP_BLOCK -// this truncates the scan area but not the macroblock unless the macro is defined -void MotionScan::clamp_scan(int w, - int h, +void MotionScan::clamp_scan(int w, + int h, int *block_x1, int *block_y1, int *block_x2, @@ -1824,37 +1006,29 @@ void MotionScan::clamp_scan(int w, // scan is always out of range before block. if(*scan_x1 < 0) { -#ifdef CLAMP_BLOCK - int difference = -*scan_x1; - *block_x1 += difference; -#endif +// int difference = -*scan_x1; +// *block_x1 += difference; *scan_x1 = 0; } if(*scan_y1 < 0) { -#ifdef CLAMP_BLOCK - int difference = -*scan_y1; - *block_y1 += difference; -#endif +// int difference = -*scan_y1; +// *block_y1 += difference; *scan_y1 = 0; } if(*scan_x2 > w) { int difference = *scan_x2 - w; -#ifdef CLAMP_BLOCK - *block_x2 -= difference; -#endif +// *block_x2 -= difference; *scan_x2 -= difference; } if(*scan_y2 > h) { int difference = *scan_y2 - h; -#ifdef CLAMP_BLOCK - *block_y2 -= difference; -#endif +// *block_y2 -= difference; *scan_y2 -= difference; } @@ -1870,9 +1044,7 @@ void MotionScan::clamp_scan(int w, if(*scan_x1 < 0) { int difference = -*scan_x1; -#ifdef CLAMP_BLOCK - *block_x1 += difference; -#endif +// *block_x1 += difference; *scan_x2 += difference; *scan_x1 = 0; } @@ -1880,9 +1052,7 @@ void MotionScan::clamp_scan(int w, if(*scan_y1 < 0) { int difference = -*scan_y1; -#ifdef CLAMP_BLOCK - *block_y1 += difference; -#endif +// *block_y1 += difference; *scan_y2 += difference; *scan_y1 = 0; } @@ -1891,18 +1061,14 @@ void MotionScan::clamp_scan(int w, { int difference = *scan_x2 - *block_x1 + *block_x2 - w; *scan_x2 -= difference; -#ifdef CLAMP_BLOCK - *block_x2 -= difference; -#endif +// *block_x2 -= difference; } if(*scan_y2 - *block_y1 + *block_y2 > h) { int difference = *scan_y2 - *block_y1 + *block_y2 - h; *scan_y2 -= difference; -#ifdef CLAMP_BLOCK - *block_y2 -= difference; -#endif +// *block_y2 -= difference; } // CLAMP(*scan_x1, 0, w - (*block_x2 - *block_x1)); diff --git a/cinelerra-5.1/plugins/motion/motionscan.h b/cinelerra-5.1/plugins/motion/motionscan.h index 72c52980..8e756ee3 100644 --- a/cinelerra-5.1/plugins/motion/motionscan.h +++ b/cinelerra-5.1/plugins/motion/motionscan.h @@ -1,29 +1,30 @@ /* * CINELERRA - * Copyright (C) 2016 Adam Williams - * + * Copyright (C) 2008 Adam Williams + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * */ #ifndef MOTIONSCAN_H #define MOTIONSCAN_H -#include "affine.inc" +#include "arraylist.h" +//#include "../downsample/downsampleengine.inc" #include "loadbalance.h" #include "vframe.inc" #include @@ -32,7 +33,6 @@ class MotionScan; #define OVERSAMPLE 4 #define MOTION_FILE "/tmp/m" -#define ROTATION_FILE "/tmp/r" class MotionScanPackage : public LoadPackage { @@ -40,11 +40,10 @@ public: MotionScanPackage(); // For multiple blocks -// Position of stationary block after downsampling +// Position of stationary block int block_x1, block_y1, block_x2, block_y2; -// index of rotated frame - int angle_step; - +// Range of positions to scan + int scan_x1, scan_y1, scan_x2, scan_y2; int dx; int dy; int64_t max_difference; @@ -52,9 +51,11 @@ public: int64_t min_pixel; int is_border; int valid; +// For single block + int step; int64_t difference1; int64_t difference2; -// Search position of current package to nearest pixel with downsampling +// Search position to nearest pixel int search_x; int search_y; // Subpixel of search position @@ -62,6 +63,14 @@ public: int sub_y; }; +class MotionScanCache +{ +public: + MotionScanCache(int x, int y, int64_t difference); + int x, y; + int64_t difference; +}; + class MotionScanUnit : public LoadClient { public: @@ -69,16 +78,19 @@ public: ~MotionScanUnit(); void process_package(LoadPackage *package); - void subpixel(MotionScanPackage *pkg); - void single_pixel(MotionScanPackage *pkg); + int64_t get_cache(int x, int y); + void put_cache(int x, int y, int64_t difference); MotionScan *server; + + ArrayList cache; + Mutex *cache_lock; }; class MotionScan : public LoadServer { public: - MotionScan(int total_clients, + MotionScan(int total_clients, int total_packages); ~MotionScan(); @@ -93,27 +105,27 @@ public: // Invoke the motion engine for a search // Frame before motion void scan_frame(VFrame *previous_frame, +// Frame after motion VFrame *current_frame, - int global_range_w, // in pixels + int global_range_w, int global_range_h, - int global_block_w, // in pixels + int global_block_w, int global_block_h, - int block_x, // in pixels - int block_y, + double block_x, + double block_y, int frame_type, int tracking_type, int action_type, int horizontal_only, int vertical_only, int source_position, - int total_dx, // in pixels * OVERSAMPLE + int total_steps, + int total_dx, int total_dy, - int global_origin_x, // in pixels - int global_origin_y, - int do_motion, - int do_rotate, - double rotation_center, // in deg - double rotation_range); + int global_origin_x, + int global_origin_y); + int64_t get_cache(int x, int y); + void put_cache(int x, int y, int64_t difference); static int64_t abs_diff(unsigned char *prev_ptr, unsigned char *current_ptr, @@ -131,8 +143,8 @@ public: int sub_y); - static void clamp_scan(int w, - int h, + static void clamp_scan(int w, + int h, int *block_x1, int *block_y1, int *block_x2, @@ -143,11 +155,10 @@ public: int *scan_y2, int use_absolute); -// Change between previous frame and current frame multiplied by +// Change between previous frame and current frame multiplied by // OVERSAMPLE int dx_result; int dy_result; - float dr_result; enum { @@ -158,7 +169,7 @@ public: STABILIZE_PIXEL, NOTHING }; - + enum { // tracking_type @@ -167,7 +178,7 @@ public: LOAD, NO_CALCULATE }; - + enum { // frame_type @@ -177,27 +188,6 @@ public: }; private: - void downsample_frame(VFrame *dst, - VFrame *src, - int downsample); - void pixel_search(int &x_result, int &y_result, double &r_result); - void subpixel_search(int &x_result, int &y_result); - double step_to_angle(int step, double center); - -// double calculate_variance(unsigned char *current_ptr, -// int row_bytes, -// int w, -// int h, -// int color_model); - double calculate_range(unsigned char *current_ptr, - int row_bytes, - int w, - int h, - int color_model); - - - - AffineEngine *rotater; // Pointer to downsampled frame before motion VFrame *previous_frame; // Pointer to downsampled frame after motion @@ -208,49 +198,33 @@ private: // Downsampled frames VFrame *downsampled_previous; VFrame *downsampled_current; -// rotated versions of current_frame - VFrame **rotated_current; -// allocation of rotated_current array, a copy of angle_steps - int total_rotated; // Test for identical frames before processing // Faster to skip it if the frames are usually different int test_match; int skip; -// macroblocks didn't have enough data - int failed; // For single block int block_x1; int block_x2; int block_y1; int block_y2; - int scan_w; - int scan_h; int scan_x1; int scan_y1; int scan_x2; int scan_y2; - double scan_angle1, scan_angle2; + int total_pixels; + int total_steps; + int edge_steps; int y_steps; int x_steps; - int angle_steps; -// in deg - double angle_step; int subpixel; int horizontal_only; int vertical_only; int global_origin_x; int global_origin_y; - int action_type; - int current_downsample; - int downsampled_w; - int downsampled_h; - int total_steps; - int do_motion; - int do_rotate; - int rotation_pass; -// in deg - double rotation_center; - double rotation_range; + + ArrayList cache; + Mutex *cache_lock; +// DownSampleServer *downsample; }; diff --git a/cinelerra-5.1/plugins/motion/motionscan.inc b/cinelerra-5.1/plugins/motion/motionscan.inc index 4e8229ed..3c574345 100644 --- a/cinelerra-5.1/plugins/motion/motionscan.inc +++ b/cinelerra-5.1/plugins/motion/motionscan.inc @@ -2,21 +2,21 @@ /* * CINELERRA * Copyright (C) 2008 Adam Williams - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * */ #ifndef MOTIONSCAN_INC diff --git a/cinelerra-5.1/plugins/motion/motionwindow.C b/cinelerra-5.1/plugins/motion/motionwindow.C index 08c4022c..02972d15 100644 --- a/cinelerra-5.1/plugins/motion/motionwindow.C +++ b/cinelerra-5.1/plugins/motion/motionwindow.C @@ -53,10 +53,10 @@ void MotionWindow::create_objects() -// add_subwindow(global = new MotionGlobal(plugin, -// this, -// x1, -// y)); + add_subwindow(global = new MotionGlobal(plugin, + this, + x1, + y)); add_subwindow(rotate = new MotionRotate(plugin, this, @@ -108,20 +108,20 @@ void MotionWindow::create_objects() // y, // &plugin->config.rotation_block_h)); -// y += 50; -// add_subwindow(title = new BC_Title(x1, y, _("Translation search steps:"))); -// add_subwindow(global_search_positions = new GlobalSearchPositions(plugin, -// x1 + title->get_w() + 10, -// y, -// 80)); -// global_search_positions->create_objects(); -// -// add_subwindow(title = new BC_Title(x2, y, _("Rotation search steps:"))); -// add_subwindow(rotation_search_positions = new RotationSearchPositions(plugin, -// x2 + title->get_w() + 10, -// y, -// 80)); -// rotation_search_positions->create_objects(); + y += 50; + add_subwindow(title = new BC_Title(x1, y, _("Translation search steps:"))); + add_subwindow(global_search_positions = new GlobalSearchPositions(plugin, + x1 + title->get_w() + 10, + y, + 80)); + global_search_positions->create_objects(); + + add_subwindow(title = new BC_Title(x2, y, _("Rotation search steps:"))); + add_subwindow(rotation_search_positions = new RotationSearchPositions(plugin, + x2 + title->get_w() + 10, + y, + 80)); + rotation_search_positions->create_objects(); y += 50; add_subwindow(title = new BC_Title(x, y, _("Translation direction:"))); @@ -209,6 +209,11 @@ void MotionWindow::create_objects() this, x + track_single->get_w() + title->get_w() + 20, y)); + add_subwindow(addtrackedframeoffset = new AddTrackedFrameOffset(plugin, + this, + x + track_single->get_w() + title->get_w() + 20 + track_frame_number->get_w(), + y)); + y += 20; add_subwindow(track_previous = new TrackPreviousFrame(plugin, @@ -268,8 +273,9 @@ void MotionWindow::update_mode() MIN_ROTATION, MAX_ROTATION); vectors->update(plugin->config.draw_vectors); -// global->update(plugin->config.global); + global->update(plugin->config.global); rotate->update(plugin->config.rotate); + addtrackedframeoffset->update(plugin->config.addtrackedframeoffset); } @@ -391,81 +397,81 @@ int BlockSize::handle_event() -// GlobalSearchPositions::GlobalSearchPositions(MotionMain *plugin, -// int x, -// int y, -// int w) -// : BC_PopupMenu(x, -// y, -// w, -// "", -// 1) -// { -// this->plugin = plugin; -// } -// void GlobalSearchPositions::create_objects() -// { -// add_item(new BC_MenuItem("16")); -// add_item(new BC_MenuItem("32")); -// add_item(new BC_MenuItem("64")); -// add_item(new BC_MenuItem("128")); -// add_item(new BC_MenuItem("256")); -// add_item(new BC_MenuItem("512")); -// add_item(new BC_MenuItem("1024")); -// add_item(new BC_MenuItem("2048")); -// add_item(new BC_MenuItem("4096")); -// add_item(new BC_MenuItem("8192")); -// add_item(new BC_MenuItem("16384")); -// add_item(new BC_MenuItem("32768")); -// add_item(new BC_MenuItem("65536")); -// add_item(new BC_MenuItem("131072")); -// char string[BCTEXTLEN]; -// sprintf(string, "%d", plugin->config.global_positions); -// set_text(string); -// } -// -// int GlobalSearchPositions::handle_event() -// { -// plugin->config.global_positions = atoi(get_text()); -// plugin->send_configure_change(); -// return 1; -// } -// -// -// -// -// -// -// -// RotationSearchPositions::RotationSearchPositions(MotionMain *plugin, -// int x, -// int y, -// int w) -// : BC_PopupMenu(x, -// y, -// w, -// "", -// 1) -// { -// this->plugin = plugin; -// } -// void RotationSearchPositions::create_objects() -// { -// add_item(new BC_MenuItem("4")); -// add_item(new BC_MenuItem("8")); -// add_item(new BC_MenuItem("16")); -// add_item(new BC_MenuItem("32")); -// char string[BCTEXTLEN]; -// sprintf(string, "%d", plugin->config.rotate_positions); -// set_text(string); -// } -// -// int RotationSearchPositions::handle_event() -// { -// plugin->config.rotate_positions = atoi(get_text()); -// plugin->send_configure_change(); -// return 1; -// } +GlobalSearchPositions::GlobalSearchPositions(MotionMain *plugin, + int x, + int y, + int w) + : BC_PopupMenu(x, + y, + w, + "", + 1) +{ + this->plugin = plugin; +} +void GlobalSearchPositions::create_objects() +{ + add_item(new BC_MenuItem("16")); + add_item(new BC_MenuItem("32")); + add_item(new BC_MenuItem("64")); + add_item(new BC_MenuItem("128")); + add_item(new BC_MenuItem("256")); + add_item(new BC_MenuItem("512")); + add_item(new BC_MenuItem("1024")); + add_item(new BC_MenuItem("2048")); + add_item(new BC_MenuItem("4096")); + add_item(new BC_MenuItem("8192")); + add_item(new BC_MenuItem("16384")); + add_item(new BC_MenuItem("32768")); + add_item(new BC_MenuItem("65536")); + add_item(new BC_MenuItem("131072")); + char string[BCTEXTLEN]; + sprintf(string, "%d", plugin->config.global_positions); + set_text(string); +} + +int GlobalSearchPositions::handle_event() +{ + plugin->config.global_positions = atoi(get_text()); + plugin->send_configure_change(); + return 1; +} + + + + + + + +RotationSearchPositions::RotationSearchPositions(MotionMain *plugin, + int x, + int y, + int w) + : BC_PopupMenu(x, + y, + w, + "", + 1) +{ + this->plugin = plugin; +} +void RotationSearchPositions::create_objects() +{ + add_item(new BC_MenuItem("4")); + add_item(new BC_MenuItem("8")); + add_item(new BC_MenuItem("16")); + add_item(new BC_MenuItem("32")); + char string[BCTEXTLEN]; + sprintf(string, "%d", plugin->config.rotate_positions); + set_text(string); +} + +int RotationSearchPositions::handle_event() +{ + plugin->config.rotate_positions = atoi(get_text()); + plugin->send_configure_change(); + return 1; +} @@ -515,6 +521,27 @@ int MotionReturnSpeed::handle_event() +AddTrackedFrameOffset::AddTrackedFrameOffset(MotionMain *plugin, + MotionWindow *gui, + int x, + int y) + : BC_CheckBox(x, + y, + plugin->config.addtrackedframeoffset, + _("Add (loaded) offset from tracked frame")) +{ + this->plugin = plugin; + this->gui = gui; +} + +int AddTrackedFrameOffset::handle_event() +{ + plugin->config.addtrackedframeoffset = get_value(); + plugin->send_configure_change(); + return 1; +} + + MotionRMagnitude::MotionRMagnitude(MotionMain *plugin, int x, int y) @@ -559,25 +586,25 @@ int MotionRReturnSpeed::handle_event() -// MotionGlobal::MotionGlobal(MotionMain *plugin, -// MotionWindow *gui, -// int x, -// int y) -// : BC_CheckBox(x, -// y, -// plugin->config.global, -// _("Track translation")) -// { -// this->plugin = plugin; -// this->gui = gui; -// } -// -// int MotionGlobal::handle_event() -// { -// plugin->config.global = get_value(); -// plugin->send_configure_change(); -// return 1; -// } +MotionGlobal::MotionGlobal(MotionMain *plugin, + MotionWindow *gui, + int x, + int y) + : BC_CheckBox(x, + y, + plugin->config.global, + _("Track translation")) +{ + this->plugin = plugin; + this->gui = gui; +} + +int MotionGlobal::handle_event() +{ + plugin->config.global = get_value(); + plugin->send_configure_change(); + return 1; +} MotionRotate::MotionRotate(MotionMain *plugin, MotionWindow *gui, diff --git a/cinelerra-5.1/plugins/motion/motionwindow.h b/cinelerra-5.1/plugins/motion/motionwindow.h index ef977ccb..4a66a1a6 100644 --- a/cinelerra-5.1/plugins/motion/motionwindow.h +++ b/cinelerra-5.1/plugins/motion/motionwindow.h @@ -2,21 +2,21 @@ /* * CINELERRA * Copyright (C) 2008 Adam Williams - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * */ #include "guicast.h" @@ -78,9 +78,9 @@ public: class TrackSingleFrame : public BC_Radial { public: - TrackSingleFrame(MotionMain *plugin, + TrackSingleFrame(MotionMain *plugin, MotionWindow *gui, - int x, + int x, int y); int handle_event(); MotionMain *plugin; @@ -90,9 +90,9 @@ public: class TrackFrameNumber : public BC_TextBox { public: - TrackFrameNumber(MotionMain *plugin, + TrackFrameNumber(MotionMain *plugin, MotionWindow *gui, - int x, + int x, int y); int handle_event(); MotionMain *plugin; @@ -102,9 +102,9 @@ public: class TrackPreviousFrame : public BC_Radial { public: - TrackPreviousFrame(MotionMain *plugin, + TrackPreviousFrame(MotionMain *plugin, MotionWindow *gui, - int x, + int x, int y); int handle_event(); MotionMain *plugin; @@ -114,9 +114,9 @@ public: class PreviousFrameSameBlock : public BC_Radial { public: - PreviousFrameSameBlock(MotionMain *plugin, + PreviousFrameSameBlock(MotionMain *plugin, MotionWindow *gui, - int x, + int x, int y); int handle_event(); MotionMain *plugin; @@ -126,8 +126,8 @@ public: class GlobalRange : public BC_IPot { public: - GlobalRange(MotionMain *plugin, - int x, + GlobalRange(MotionMain *plugin, + int x, int y, int *value); int handle_event(); @@ -138,8 +138,8 @@ public: class RotationRange : public BC_IPot { public: - RotationRange(MotionMain *plugin, - int x, + RotationRange(MotionMain *plugin, + int x, int y); int handle_event(); MotionMain *plugin; @@ -148,8 +148,8 @@ public: class RotationCenter : public BC_IPot { public: - RotationCenter(MotionMain *plugin, - int x, + RotationCenter(MotionMain *plugin, + int x, int y); int handle_event(); MotionMain *plugin; @@ -158,8 +158,8 @@ public: class BlockSize : public BC_IPot { public: - BlockSize(MotionMain *plugin, - int x, + BlockSize(MotionMain *plugin, + int x, int y, int *value); int handle_event(); @@ -170,9 +170,9 @@ public: class MotionBlockX : public BC_FPot { public: - MotionBlockX(MotionMain *plugin, + MotionBlockX(MotionMain *plugin, MotionWindow *gui, - int x, + int x, int y); int handle_event(); MotionWindow *gui; @@ -182,9 +182,9 @@ public: class MotionBlockY : public BC_FPot { public: - MotionBlockY(MotionMain *plugin, + MotionBlockY(MotionMain *plugin, MotionWindow *gui, - int x, + int x, int y); int handle_event(); MotionWindow *gui; @@ -194,9 +194,9 @@ public: class MotionBlockXText : public BC_TextBox { public: - MotionBlockXText(MotionMain *plugin, + MotionBlockXText(MotionMain *plugin, MotionWindow *gui, - int x, + int x, int y); int handle_event(); MotionWindow *gui; @@ -206,44 +206,44 @@ public: class MotionBlockYText : public BC_TextBox { public: - MotionBlockYText(MotionMain *plugin, + MotionBlockYText(MotionMain *plugin, MotionWindow *gui, - int x, + int x, int y); int handle_event(); MotionWindow *gui; MotionMain *plugin; }; -// class GlobalSearchPositions : public BC_PopupMenu -// { -// public: -// GlobalSearchPositions(MotionMain *plugin, -// int x, -// int y, -// int w); -// void create_objects(); -// int handle_event(); -// MotionMain *plugin; -// }; -// -// class RotationSearchPositions : public BC_PopupMenu -// { -// public: -// RotationSearchPositions(MotionMain *plugin, -// int x, -// int y, -// int w); -// void create_objects(); -// int handle_event(); -// MotionMain *plugin; -// }; +class GlobalSearchPositions : public BC_PopupMenu +{ +public: + GlobalSearchPositions(MotionMain *plugin, + int x, + int y, + int w); + void create_objects(); + int handle_event(); + MotionMain *plugin; +}; + +class RotationSearchPositions : public BC_PopupMenu +{ +public: + RotationSearchPositions(MotionMain *plugin, + int x, + int y, + int w); + void create_objects(); + int handle_event(); + MotionMain *plugin; +}; class MotionMagnitude : public BC_IPot { public: - MotionMagnitude(MotionMain *plugin, - int x, + MotionMagnitude(MotionMain *plugin, + int x, int y); int handle_event(); MotionMain *plugin; @@ -252,8 +252,8 @@ public: class MotionRMagnitude : public BC_IPot { public: - MotionRMagnitude(MotionMain *plugin, - int x, + MotionRMagnitude(MotionMain *plugin, + int x, int y); int handle_event(); MotionMain *plugin; @@ -262,8 +262,8 @@ public: class MotionReturnSpeed : public BC_IPot { public: - MotionReturnSpeed(MotionMain *plugin, - int x, + MotionReturnSpeed(MotionMain *plugin, + int x, int y); int handle_event(); MotionMain *plugin; @@ -273,8 +273,8 @@ public: class MotionRReturnSpeed : public BC_IPot { public: - MotionRReturnSpeed(MotionMain *plugin, - int x, + MotionRReturnSpeed(MotionMain *plugin, + int x, int y); int handle_event(); MotionMain *plugin; @@ -284,9 +284,9 @@ public: class MotionDrawVectors : public BC_CheckBox { public: - MotionDrawVectors(MotionMain *plugin, + MotionDrawVectors(MotionMain *plugin, MotionWindow *gui, - int x, + int x, int y); int handle_event(); MotionMain *plugin; @@ -296,33 +296,33 @@ public: class AddTrackedFrameOffset : public BC_CheckBox { public: - AddTrackedFrameOffset(MotionMain *plugin, + AddTrackedFrameOffset(MotionMain *plugin, MotionWindow *gui, - int x, + int x, int y); int handle_event(); MotionWindow *gui; MotionMain *plugin; }; -// class MotionGlobal : public BC_CheckBox -// { -// public: -// MotionGlobal(MotionMain *plugin, -// MotionWindow *gui, -// int x, -// int y); -// int handle_event(); -// MotionWindow *gui; -// MotionMain *plugin; -// }; +class MotionGlobal : public BC_CheckBox +{ +public: + MotionGlobal(MotionMain *plugin, + MotionWindow *gui, + int x, + int y); + int handle_event(); + MotionWindow *gui; + MotionMain *plugin; +}; class MotionRotate : public BC_CheckBox { public: - MotionRotate(MotionMain *plugin, + MotionRotate(MotionMain *plugin, MotionWindow *gui, - int x, + int x, int y); int handle_event(); MotionWindow *gui; @@ -353,15 +353,15 @@ public: MotionBlockY *block_y; MotionBlockXText *block_x_text; MotionBlockYText *block_y_text; -// GlobalSearchPositions *global_search_positions; -// RotationSearchPositions *rotation_search_positions; + GlobalSearchPositions *global_search_positions; + RotationSearchPositions *rotation_search_positions; MotionMagnitude *magnitude; MotionRMagnitude *rotate_magnitude; MotionReturnSpeed *return_speed; MotionRReturnSpeed *rotate_return_speed; ActionType *action_type; MotionDrawVectors *vectors; -// MotionGlobal *global; + MotionGlobal *global; MotionRotate *rotate; AddTrackedFrameOffset *addtrackedframeoffset; TrackSingleFrame *track_single; diff --git a/cinelerra-5.1/plugins/motion/motionwindow.inc b/cinelerra-5.1/plugins/motion/motionwindow.inc index 8fbef467..50404d71 100644 --- a/cinelerra-5.1/plugins/motion/motionwindow.inc +++ b/cinelerra-5.1/plugins/motion/motionwindow.inc @@ -2,21 +2,21 @@ /* * CINELERRA * Copyright (C) 2008 Adam Williams - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * */ #ifndef MOTIONWINDOW_INC diff --git a/cinelerra-5.1/plugins/motion/opencvwrapper.C b/cinelerra-5.1/plugins/motion/opencvwrapper.C index 3fadcf0d..7c19be68 100644 --- a/cinelerra-5.1/plugins/motion/opencvwrapper.C +++ b/cinelerra-5.1/plugins/motion/opencvwrapper.C @@ -1,21 +1,21 @@ /* * CINELERRA * Copyright (C) 1997-2012 Adam Williams - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * */ @@ -128,10 +128,10 @@ findPairs( const CvSeq* objectKeypoints, const CvSeq* objectDescriptors, void -flannFindPairs( const CvSeq*, +flannFindPairs( const CvSeq*, const CvSeq* objectDescriptors, - const CvSeq*, - const CvSeq* imageDescriptors, + const CvSeq*, + const CvSeq* imageDescriptors, vector& ptpairs ) { int length = (int)(objectDescriptors->elem_size/sizeof(float)); @@ -171,10 +171,10 @@ flannFindPairs( const CvSeq*, int* indices_ptr = m_indices.ptr(0); float* dists_ptr = m_dists.ptr(0); //printf("flannFindPairs %d m_indices.rows=%d\n", __LINE__, m_indices.rows); - for (int i = 0; i < m_indices.rows; ++i) + for (int i = 0; i < m_indices.rows; ++i) { //printf("flannFindPairs %d dists=%f %f\n", __LINE__, dists_ptr[2 * i], 0.6 * dists_ptr[2 * i + 1]); - if (dists_ptr[2 * i] < 0.6 * dists_ptr[2 * i + 1]) + if (dists_ptr[2 * i] < 0.6 * dists_ptr[2 * i + 1]) { //printf("flannFindPairs %d pairs=%d\n", __LINE__, ptpairs.size()); ptpairs.push_back(i); @@ -185,11 +185,11 @@ flannFindPairs( const CvSeq*, /* a rough implementation for object location */ -int OpenCVWrapper::locatePlanarObject(const CvSeq* objectKeypoints, +int OpenCVWrapper::locatePlanarObject(const CvSeq* objectKeypoints, const CvSeq* objectDescriptors, - const CvSeq* imageKeypoints, + const CvSeq* imageKeypoints, const CvSeq* imageDescriptors, - const CvPoint src_corners[4], + const CvPoint src_corners[4], int *(*point_pairs), int (*total_pairs)) { @@ -199,7 +199,7 @@ int OpenCVWrapper::locatePlanarObject(const CvSeq* objectKeypoints, vector pt1, pt2; CvMat _pt1, _pt2; int i, n; - + (*point_pairs) = 0; (*total_pairs) = 0; @@ -213,8 +213,8 @@ int OpenCVWrapper::locatePlanarObject(const CvSeq* objectKeypoints, // Store keypoints (*point_pairs) = (int*)calloc(ptpairs.size(), sizeof(int)); (*total_pairs) = ptpairs.size() / 2; - - + + for(int i = 0; i < (int)ptpairs.size(); i++) { (*point_pairs)[i] = ptpairs[i]; @@ -292,7 +292,7 @@ OpenCVWrapper::~OpenCVWrapper() int OpenCVWrapper::scan(VFrame *object_frame, VFrame *scene_frame, - int object_x1, + int object_x1, int object_y1, int object_x2, int object_y2, @@ -321,7 +321,7 @@ int OpenCVWrapper::scan(VFrame *object_frame, if(scene_w % QUANTIZE) scene_image_w += QUANTIZE - (scene_w % QUANTIZE); if(scene_h % QUANTIZE) scene_image_h += QUANTIZE - (scene_h % QUANTIZE); - if(object_image && + if(object_image && (object_image_w != this->object_image_w || object_image_h != this->object_image_h)) { @@ -331,7 +331,7 @@ int OpenCVWrapper::scan(VFrame *object_frame, this->object_image_w = object_image_w; this->object_image_h = object_image_h; - if(scene_image && + if(scene_image && (scene_image_w != this->scene_image_w || scene_image_h != this->scene_image_h)) { @@ -345,18 +345,18 @@ int OpenCVWrapper::scan(VFrame *object_frame, if(!object_image) { // Only does greyscale - object_image = cvCreateImage( - cvSize(object_image_w, object_image_h), - 8, + object_image = cvCreateImage( + cvSize(object_image_w, object_image_h), + 8, 1); } if(!scene_image) { // Only does greyscale - scene_image = cvCreateImage( - cvSize(scene_image_w, scene_image_h), - 8, + scene_image = cvCreateImage( + cvSize(scene_image_w, scene_image_h), + 8, 1); } @@ -365,19 +365,19 @@ int OpenCVWrapper::scan(VFrame *object_frame, cvSetImageROI( object_image, cvRect( 0, 0, object_w, object_h ) ); cvSetImageROI( scene_image, cvRect( 0, 0, scene_w, scene_h ) ); - grey_crop((unsigned char*)scene_image->imageData, - scene_frame, - scene_x1, - scene_y1, - scene_x2, + grey_crop((unsigned char*)scene_image->imageData, + scene_frame, + scene_x1, + scene_y1, + scene_x2, scene_y2, scene_image_w, scene_image_h); - grey_crop((unsigned char*)object_image->imageData, - object_frame, - object_x1, - object_y1, - object_x2, + grey_crop((unsigned char*)object_image->imageData, + object_frame, + object_x1, + object_y1, + object_x2, object_y2, object_image_w, object_image_h); @@ -404,11 +404,11 @@ int OpenCVWrapper::scan(VFrame *object_frame, point_pairs = 0; - cvExtractSURF(object_image, - 0, - &object_keypoints, - &object_descriptors, - storage, + cvExtractSURF(object_image, + 0, + &object_keypoints, + &object_descriptors, + storage, params, 0); @@ -418,21 +418,21 @@ int OpenCVWrapper::scan(VFrame *object_frame, // { // CvSURFPoint* r1 = (CvSURFPoint*)cvGetSeqElem( object_keypoints, i ); // int size = r1->size / 4; -// draw_rect(frame[object_layer], -// r1->pt.x + object_x1 - size, -// r1->pt.y + object_y1 - size, -// r1->pt.x + object_x1 + size, +// draw_rect(frame[object_layer], +// r1->pt.x + object_x1 - size, +// r1->pt.y + object_y1 - size, +// r1->pt.x + object_x1 + size, // r1->pt.y + object_y1 + size); // } //printf("OpenCVWrapper::process_buffer %d\n", __LINE__); - cvExtractSURF(scene_image, - 0, - &scene_keypoints, - &scene_descriptors, - storage, + cvExtractSURF(scene_image, + 0, + &scene_keypoints, + &scene_descriptors, + storage, params, 0); @@ -441,24 +441,24 @@ int OpenCVWrapper::scan(VFrame *object_frame, // { // CvSURFPoint* r1 = (CvSURFPoint*)cvGetSeqElem( scene_keypoints, i ); // int size = r1->size / 4; -// draw_rect(frame[scene_layer], -// r1->pt.x + scene_x1 - size, -// r1->pt.y + scene_y1 - size, -// r1->pt.x + scene_x1 + size, +// draw_rect(frame[scene_layer], +// r1->pt.x + scene_x1 - size, +// r1->pt.y + scene_y1 - size, +// r1->pt.x + scene_x1 + size, // r1->pt.y + scene_y1 + size); // } -// printf("OpenCVWrapper::scan %d %d %d scene keypoints=%d\n", -// __LINE__, +// printf("OpenCVWrapper::scan %d %d %d scene keypoints=%d\n", +// __LINE__, // scene_w, // scene_h, // scene_keypoints->total); - CvPoint src_corners[4] = + CvPoint src_corners[4] = { - { 0, 0 }, - { object_w, 0 }, - { object_w, object_h }, + { 0, 0 }, + { object_w, 0 }, + { object_w, object_h }, { 0, object_h } }; @@ -472,11 +472,11 @@ int OpenCVWrapper::scan(VFrame *object_frame, //printf("OpenCVWrapper::process_buffer %d\n", __LINE__); if(scene_keypoints->total && object_keypoints->total && - locatePlanarObject(object_keypoints, - object_descriptors, - scene_keypoints, - scene_descriptors, - src_corners, + locatePlanarObject(object_keypoints, + object_descriptors, + scene_keypoints, + scene_descriptors, + src_corners, &point_pairs, &total_pairs)) { @@ -493,9 +493,9 @@ int OpenCVWrapper::scan(VFrame *object_frame, // Convert to greyscale & crop -void OpenCVWrapper::grey_crop(unsigned char *dst, - VFrame *src, - int x1, +void OpenCVWrapper::grey_crop(unsigned char *dst, + VFrame *src, + int x1, int y1, int x2, int y2, diff --git a/cinelerra-5.1/plugins/motion/opencvwrapper.h b/cinelerra-5.1/plugins/motion/opencvwrapper.h index 53e0c46b..a33bec7f 100644 --- a/cinelerra-5.1/plugins/motion/opencvwrapper.h +++ b/cinelerra-5.1/plugins/motion/opencvwrapper.h @@ -1,21 +1,21 @@ /* * CINELERRA * Copyright (C) 1997-2012 Adam Williams - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * */ @@ -37,9 +37,9 @@ public: float get_dst_x(int number); float get_dst_y(int number); - void grey_crop(unsigned char *dst, - VFrame *src, - int x1, + void grey_crop(unsigned char *dst, + VFrame *src, + int x1, int y1, int x2, int y2, @@ -49,7 +49,7 @@ public: // Returns 1 when it got something int scan(VFrame *object_frame, VFrame *scene_frame, - int object_x1, + int object_x1, int object_y1, int object_x2, int object_y2, @@ -59,11 +59,11 @@ public: int scene_y2); private: - int locatePlanarObject(const CvSeq* objectKeypoints, + int locatePlanarObject(const CvSeq* objectKeypoints, const CvSeq* objectDescriptors, - const CvSeq* imageKeypoints, + const CvSeq* imageKeypoints, const CvSeq* imageDescriptors, - const CvPoint src_corners[4], + const CvPoint src_corners[4], int *(*point_pairs), int (*total_pairs)); diff --git a/cinelerra-5.1/plugins/motion/rotatescan.C b/cinelerra-5.1/plugins/motion/rotatescan.C deleted file mode 100644 index e7b346e9..00000000 --- a/cinelerra-5.1/plugins/motion/rotatescan.C +++ /dev/null @@ -1,471 +0,0 @@ - -/* - * CINELERRA - * Copyright (C) 2016 Adam Williams - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "affine.h" -#include "bcsignals.h" -#include "clip.h" -#include "motionscan.h" -#include "rotatescan.h" -#include "motion.h" -#include "mutex.h" -#include "vframe.h" - - - - - - - - - -RotateScanPackage::RotateScanPackage() -{ -} - - -RotateScanUnit::RotateScanUnit(RotateScan *server, MotionMain *plugin) - : LoadClient(server) -{ - this->server = server; - this->plugin = plugin; - rotater = 0; - temp = 0; -} - -RotateScanUnit::~RotateScanUnit() -{ - delete rotater; - delete temp; -} - -void RotateScanUnit::process_package(LoadPackage *package) -{ - if(server->skip) return; - RotateScanPackage *pkg = (RotateScanPackage*)package; - - if((pkg->difference = server->get_cache(pkg->angle)) < 0) - { -//printf("RotateScanUnit::process_package %d\n", __LINE__); - int color_model = server->previous_frame->get_color_model(); - int pixel_size = BC_CModels::calculate_pixelsize(color_model); - int row_bytes = server->previous_frame->get_bytes_per_line(); - - if(!rotater) - rotater = new AffineEngine(1, 1); - if(!temp) temp = new VFrame(0, - -1, - server->previous_frame->get_w(), - server->previous_frame->get_h(), - color_model, - -1); -//printf("RotateScanUnit::process_package %d\n", __LINE__); - - -// Rotate original block size -// rotater->set_viewport(server->block_x1, -// server->block_y1, -// server->block_x2 - server->block_x1, -// server->block_y2 - server->block_y1); - rotater->set_in_viewport(server->block_x1, - server->block_y1, - server->block_x2 - server->block_x1, - server->block_y2 - server->block_y1); - rotater->set_out_viewport(server->block_x1, - server->block_y1, - server->block_x2 - server->block_x1, - server->block_y2 - server->block_y1); -// rotater->set_pivot(server->block_x, server->block_y); - rotater->set_in_pivot(server->block_x, server->block_y); - rotater->set_out_pivot(server->block_x, server->block_y); -//printf("RotateScanUnit::process_package %d\n", __LINE__); - rotater->rotate(temp, - server->previous_frame, - pkg->angle); - -// Scan reduced block size -//plugin->output_frame->copy_from(server->current_frame); -//plugin->output_frame->copy_from(temp); -// printf("RotateScanUnit::process_package %d %d %d %d %d\n", -// __LINE__, -// server->scan_x, -// server->scan_y, -// server->scan_w, -// server->scan_h); -// Clamp coordinates - int x1 = server->scan_x; - int y1 = server->scan_y; - int x2 = x1 + server->scan_w; - int y2 = y1 + server->scan_h; - x2 = MIN(temp->get_w(), x2); - y2 = MIN(temp->get_h(), y2); - x2 = MIN(server->current_frame->get_w(), x2); - y2 = MIN(server->current_frame->get_h(), y2); - x1 = MAX(0, x1); - y1 = MAX(0, y1); - - if(x2 > x1 && y2 > y1) - { - pkg->difference = MotionScan::abs_diff( - temp->get_rows()[y1] + x1 * pixel_size, - server->current_frame->get_rows()[y1] + x1 * pixel_size, - row_bytes, - x2 - x1, - y2 - y1, - color_model); -//printf("RotateScanUnit::process_package %d\n", __LINE__); - server->put_cache(pkg->angle, pkg->difference); - } - -// printf("RotateScanUnit::process_package 10 x=%d y=%d w=%d h=%d block_x=%d block_y=%d angle=%f scan_w=%d scan_h=%d diff=%lld\n", -// server->block_x1, -// server->block_y1, -// server->block_x2 - server->block_x1, -// server->block_y2 - server->block_y1, -// server->block_x, -// server->block_y, -// pkg->angle, -// server->scan_w, -// server->scan_h, -// pkg->difference); - } -} - - - - - - - - - - - - - - - - - - - - - - -RotateScan::RotateScan(MotionMain *plugin, - int total_clients, - int total_packages) - : LoadServer( -//1, 1 -total_clients, total_packages -) -{ - this->plugin = plugin; - cache_lock = new Mutex("RotateScan::cache_lock"); -} - - -RotateScan::~RotateScan() -{ - delete cache_lock; -} - -void RotateScan::init_packages() -{ - for(int i = 0; i < get_total_packages(); i++) - { - RotateScanPackage *pkg = (RotateScanPackage*)get_package(i); - pkg->angle = i * - (scan_angle2 - scan_angle1) / - (total_steps - 1) + - scan_angle1; - } -} - -LoadClient* RotateScan::new_client() -{ - return new RotateScanUnit(this, plugin); -} - -LoadPackage* RotateScan::new_package() -{ - return new RotateScanPackage; -} - - -float RotateScan::scan_frame(VFrame *previous_frame, - VFrame *current_frame, - int block_x, - int block_y) -{ - skip = 0; - this->block_x = block_x; - this->block_y = block_y; - -//printf("RotateScan::scan_frame %d\n", __LINE__); - switch(plugin->config.tracking_type) - { - case MotionScan::NO_CALCULATE: - result = plugin->config.rotation_center; - skip = 1; - break; - - case MotionScan::LOAD: - { - char string[BCTEXTLEN]; - sprintf(string, - "%s%06ld", - ROTATION_FILE, - plugin->get_source_position()); - FILE *input = fopen(string, "r"); - if(input) - { - int temp = fscanf(input, "%f", &result); - fclose(input); - skip = 1; - } - else - { - perror("RotateScan::scan_frame LOAD"); - } - break; - } - } - - - - - - - - - this->previous_frame = previous_frame; - this->current_frame = current_frame; - int w = current_frame->get_w(); - int h = current_frame->get_h(); - int block_w = w * plugin->config.global_block_w / 100; - int block_h = h * plugin->config.global_block_h / 100; - - if(this->block_x - block_w / 2 < 0) block_w = this->block_x * 2; - if(this->block_y - block_h / 2 < 0) block_h = this->block_y * 2; - if(this->block_x + block_w / 2 > w) block_w = (w - this->block_x) * 2; - if(this->block_y + block_h / 2 > h) block_h = (h - this->block_y) * 2; - - block_x1 = this->block_x - block_w / 2; - block_x2 = this->block_x + block_w / 2; - block_y1 = this->block_y - block_h / 2; - block_y2 = this->block_y + block_h / 2; - - -// Calculate the maximum area available to scan after rotation. -// Must be calculated from the starting range because of cache. -// Get coords of rectangle after rotation. - double center_x = this->block_x; - double center_y = this->block_y; - double max_angle = plugin->config.rotation_range; - double base_angle1 = atan((float)block_h / block_w); - double base_angle2 = atan((float)block_w / block_h); - double target_angle1 = base_angle1 + max_angle * 2 * M_PI / 360; - double target_angle2 = base_angle2 + max_angle * 2 * M_PI / 360; - double radius = sqrt(block_w * block_w + block_h * block_h) / 2; - double x1 = center_x - cos(target_angle1) * radius; - double y1 = center_y - sin(target_angle1) * radius; - double x2 = center_x + sin(target_angle2) * radius; - double y2 = center_y - cos(target_angle2) * radius; - double x3 = center_x - sin(target_angle2) * radius; - double y3 = center_y + cos(target_angle2) * radius; - -// Track top edge to find greatest area. - double max_area1 = 0; - double max_x1 = 0; - double max_y1 = 0; - for(double x = x1; x < x2; x++) - { - double y = y1 + (y2 - y1) * (x - x1) / (x2 - x1); - if(x >= center_x && x < block_x2 && y >= block_y1 && y < center_y) - { - double area = fabs(x - center_x) * fabs(y - center_y); - if(area > max_area1) - { - max_area1 = area; - max_x1 = x; - max_y1 = y; - } - } - } - -// Track left edge to find greatest area. - double max_area2 = 0; - double max_x2 = 0; - double max_y2 = 0; - for(double y = y1; y < y3; y++) - { - double x = x1 + (x3 - x1) * (y - y1) / (y3 - y1); - if(x >= block_x1 && x < center_x && y >= block_y1 && y < center_y) - { - double area = fabs(x - center_x) * fabs(y - center_y); - if(area > max_area2) - { - max_area2 = area; - max_x2 = x; - max_y2 = y; - } - } - } - - double max_x, max_y; - max_x = max_x2; - max_y = max_y1; - -// Get reduced scan coords - scan_w = (int)(fabs(max_x - center_x) * 2); - scan_h = (int)(fabs(max_y - center_y) * 2); - scan_x = (int)(center_x - scan_w / 2); - scan_y = (int)(center_y - scan_h / 2); -// printf("RotateScan::scan_frame center=%d,%d scan=%d,%d %dx%d\n", -// this->block_x, this->block_y, scan_x, scan_y, scan_w, scan_h); -// printf(" angle_range=%f block= %d,%d,%d,%d\n", max_angle, block_x1, block_y1, block_x2, block_y2); - -// Determine min angle from size of block - double angle1 = atan((double)block_h / block_w); - double angle2 = atan((double)(block_h - 1) / (block_w + 1)); - double min_angle = fabs(angle2 - angle1) / OVERSAMPLE; - min_angle = MAX(min_angle, MIN_ANGLE); - -//printf("RotateScan::scan_frame %d min_angle=%f\n", __LINE__, min_angle * 360 / 2 / M_PI); - - cache.remove_all_objects(); - - - if(!skip) - { - if(previous_frame->data_matches(current_frame)) - { -//printf("RotateScan::scan_frame: frames match. Skipping.\n"); - result = plugin->config.rotation_center; - skip = 1; - } - } - - if(!skip) - { -// Initial search range - float angle_range = max_angle; - result = plugin->config.rotation_center; - total_steps = plugin->config.rotate_positions; - - - while(angle_range >= min_angle * total_steps) - { - scan_angle1 = result - angle_range; - scan_angle2 = result + angle_range; - - - set_package_count(total_steps); -//set_package_count(1); - process_packages(); - - int64_t min_difference = -1; - for(int i = 0; i < get_total_packages(); i++) - { - RotateScanPackage *pkg = (RotateScanPackage*)get_package(i); - if(pkg->difference < min_difference || min_difference == -1) - { - min_difference = pkg->difference; - result = pkg->angle; - } -//break; - } - - angle_range /= 2; - -//break; - } - } - -//printf("RotateScan::scan_frame %d\n", __LINE__); - - if(!skip && plugin->config.tracking_type == MotionScan::SAVE) - { - char string[BCTEXTLEN]; - sprintf(string, - "%s%06ld", - ROTATION_FILE, - plugin->get_source_position()); - FILE *output = fopen(string, "w"); - if(output) - { - fprintf(output, "%f\n", result); - fclose(output); - } - else - { - perror("RotateScan::scan_frame SAVE"); - } - } - -//printf("RotateScan::scan_frame %d angle=%f\n", __LINE__, result); - - - - return result; -} - -int64_t RotateScan::get_cache(float angle) -{ - int64_t result = -1; - cache_lock->lock("RotateScan::get_cache"); - for(int i = 0; i < cache.total; i++) - { - RotateScanCache *ptr = cache.values[i]; - if(fabs(ptr->angle - angle) <= MIN_ANGLE) - { - result = ptr->difference; - break; - } - } - cache_lock->unlock(); - return result; -} - -void RotateScan::put_cache(float angle, int64_t difference) -{ - RotateScanCache *ptr = new RotateScanCache(angle, difference); - cache_lock->lock("RotateScan::put_cache"); - cache.append(ptr); - cache_lock->unlock(); -} - - - - - - - - - -RotateScanCache::RotateScanCache(float angle, int64_t difference) -{ - this->angle = angle; - this->difference = difference; -} - - - diff --git a/cinelerra-5.1/plugins/motion/rotatescan.h b/cinelerra-5.1/plugins/motion/rotatescan.h deleted file mode 100644 index 27001e34..00000000 --- a/cinelerra-5.1/plugins/motion/rotatescan.h +++ /dev/null @@ -1,136 +0,0 @@ - -/* - * CINELERRA - * Copyright (C) 2016 Adam Williams - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - - - - -#ifndef ROTATESCAN_H -#define ROTATESCAN_H - - - -#include "affine.inc" -#include "loadbalance.h" -#include "motion.inc" -#include "vframe.inc" -#include - -class RotateScan; - - -class RotateScanPackage : public LoadPackage -{ -public: - RotateScanPackage(); - float angle; - int64_t difference; -}; - -class RotateScanCache -{ -public: - RotateScanCache(float angle, int64_t difference); - float angle; - int64_t difference; -}; - -class RotateScanUnit : public LoadClient -{ -public: - RotateScanUnit(RotateScan *server, MotionMain *plugin); - ~RotateScanUnit(); - - void process_package(LoadPackage *package); - - RotateScan *server; - MotionMain *plugin; - AffineEngine *rotater; - VFrame *temp; -}; - -class RotateScan : public LoadServer -{ -public: - RotateScan(MotionMain *plugin, - int total_clients, - int total_packages); - ~RotateScan(); - - friend class RotateScanUnit; - - void init_packages(); - LoadClient* new_client(); - LoadPackage* new_package(); - -// Invoke the motion engine for a search -// Frame before rotation - float scan_frame(VFrame *previous_frame, -// Frame after rotation - VFrame *current_frame, -// Pivot - int block_x, - int block_y); - int64_t get_cache(float angle); - void put_cache(float angle, int64_t difference); - - -// Angle result - float result; - -private: - VFrame *previous_frame; -// Frame after motion - VFrame *current_frame; - - MotionMain *plugin; - int skip; - -// Pivot - int block_x; - int block_y; -// Block to rotate - int block_x1; - int block_x2; - int block_y1; - int block_y2; -// Area to compare - int scan_x; - int scan_y; - int scan_w; - int scan_h; -// Range of angles to compare - float scan_angle1, scan_angle2; - int total_steps; - - ArrayList cache; - Mutex *cache_lock; -}; - - - - - -#endif - - - - - diff --git a/cinelerra-5.1/plugins/motion/rotatescan.inc b/cinelerra-5.1/plugins/motion/rotatescan.inc deleted file mode 100644 index d3dfd975..00000000 --- a/cinelerra-5.1/plugins/motion/rotatescan.inc +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef ROTATESCAN_INC -#define ROTATESCAN_INC - - -class RotateScan; - - -#endif - - - - diff --git a/cinelerra-5.1/plugins/motion2point/Makefile b/cinelerra-5.1/plugins/motion2point/Makefile index 914cf00d..5a00398c 100644 --- a/cinelerra-5.1/plugins/motion2point/Makefile +++ b/cinelerra-5.1/plugins/motion2point/Makefile @@ -2,15 +2,17 @@ include ../../plugin_defs OBJS := \ $(OBJDIR)/downsampleengine.o \ - $(OBJDIR)/motionscan.o \ + $(OBJDIR)/motionscan-hv.o \ $(OBJDIR)/motion.o \ $(OBJDIR)/motionwindow.o +CFLAGS += -DMotionScan=MotionHVScan + PLUGIN = motion2 include ../../plugin_config $(OBJDIR)/downsampleengine.o: downsampleengine.C $(OBJDIR)/motion.o: motion.C -$(OBJDIR)/motionscan.o: motionscan.C +$(OBJDIR)/motionscan-hv.o: motionscan-hv.C $(OBJDIR)/motionwindow.o: motionwindow.C diff --git a/cinelerra-5.1/plugins/motion2point/motion.C b/cinelerra-5.1/plugins/motion2point/motion.C index 28c09378..ad745c4f 100644 --- a/cinelerra-5.1/plugins/motion2point/motion.C +++ b/cinelerra-5.1/plugins/motion2point/motion.C @@ -20,7 +20,7 @@ */ #include "affine.h" -#include "../motion/motionscan.h" +#include "../motion-hv/motionscan-hv.h" #include "bcdisplayinfo.h" #include "bcsignals.h" #include "clip.h" diff --git a/cinelerra-5.1/plugins/motion2point/motion.h b/cinelerra-5.1/plugins/motion2point/motion.h index 0fd65948..19d1fa65 100644 --- a/cinelerra-5.1/plugins/motion2point/motion.h +++ b/cinelerra-5.1/plugins/motion2point/motion.h @@ -27,7 +27,7 @@ #include #include "affine.inc" -#include "motionscan.inc" +#include "motionscan-hv.inc" #include "bchash.inc" #include "filexml.inc" #include "keyframe.inc" diff --git a/cinelerra-5.1/plugins/motion2point/motionscan-hv.C b/cinelerra-5.1/plugins/motion2point/motionscan-hv.C new file mode 120000 index 00000000..898c188d --- /dev/null +++ b/cinelerra-5.1/plugins/motion2point/motionscan-hv.C @@ -0,0 +1 @@ +../motion-hv/motionscan-hv.C \ No newline at end of file diff --git a/cinelerra-5.1/plugins/motion2point/motionscan-hv.h b/cinelerra-5.1/plugins/motion2point/motionscan-hv.h new file mode 120000 index 00000000..d5eb987c --- /dev/null +++ b/cinelerra-5.1/plugins/motion2point/motionscan-hv.h @@ -0,0 +1 @@ +../motion-hv/motionscan-hv.h \ No newline at end of file diff --git a/cinelerra-5.1/plugins/motion2point/motionscan-hv.inc b/cinelerra-5.1/plugins/motion2point/motionscan-hv.inc new file mode 120000 index 00000000..4c59354a --- /dev/null +++ b/cinelerra-5.1/plugins/motion2point/motionscan-hv.inc @@ -0,0 +1 @@ +../motion-hv/motionscan-hv.inc \ No newline at end of file diff --git a/cinelerra-5.1/plugins/motion2point/motionscan.C b/cinelerra-5.1/plugins/motion2point/motionscan.C deleted file mode 120000 index 4ac14722..00000000 --- a/cinelerra-5.1/plugins/motion2point/motionscan.C +++ /dev/null @@ -1 +0,0 @@ -../motion/motionscan.C \ No newline at end of file diff --git a/cinelerra-5.1/plugins/motion2point/motionscan.h b/cinelerra-5.1/plugins/motion2point/motionscan.h deleted file mode 120000 index 9461913f..00000000 --- a/cinelerra-5.1/plugins/motion2point/motionscan.h +++ /dev/null @@ -1 +0,0 @@ -../motion/motionscan.h \ No newline at end of file diff --git a/cinelerra-5.1/plugins/motion2point/motionscan.inc b/cinelerra-5.1/plugins/motion2point/motionscan.inc deleted file mode 120000 index e4b8d557..00000000 --- a/cinelerra-5.1/plugins/motion2point/motionscan.inc +++ /dev/null @@ -1 +0,0 @@ -../motion/motionscan.inc \ No newline at end of file diff --git a/cinelerra-5.1/plugins/motion2point/motionwindow.C b/cinelerra-5.1/plugins/motion2point/motionwindow.C index ad959d8c..ccfbe504 100644 --- a/cinelerra-5.1/plugins/motion2point/motionwindow.C +++ b/cinelerra-5.1/plugins/motion2point/motionwindow.C @@ -24,7 +24,7 @@ #include "clip.h" #include "language.h" #include "motion.h" -#include "motionscan.h" +#include "motionscan-hv.h" #include "motionwindow.h" diff --git a/cinelerra-5.1/thirdparty/Makefile b/cinelerra-5.1/thirdparty/Makefile index a7e7cd63..c7cc7f3c 100644 --- a/cinelerra-5.1/thirdparty/Makefile +++ b/cinelerra-5.1/thirdparty/Makefile @@ -160,7 +160,7 @@ a52dec.mak_params?= ; cd $(call bld_path,a52dec,include); ln -sf . a52dec a52dec.cfg_vars?= CFLAGS+=" -U__FreeBSD__ $(call inc_path,djbfft)" LIBS+=" $(call ld_path,djbfft)" a52dec.cfg_params?=--enable-djbfft djbfft.cfg_vars?=echo "$(call bld_path,djbfft)" > conf-home; \ - (CFLAGS="$(CFLAGS)"; $(CFG_VARS); echo "$(CC) $$$$CFLAGS") > conf-cc; \ + (CFLAGS="$(CFLAGS)"; $(CFG_VARS)$(if $(CFG_VARS),; )echo "$(CC) $$$$CFLAGS") > conf-cc; \ echo > ./configure; chmod +x ./configure; djbfft.mak_params?=; cd $(call bld_path,djbfft); ln -sf djbfft.a libdjbfft.a audiofile.cfg_params?=--enable-shared=no -- 2.26.2