@@ -318,6 +318,34 @@ void detect_filter_update(void *data, obs_data_t *settings)
318318 tf->zoomFactor = (float )obs_data_get_double (settings, " zoom_factor" );
319319 tf->zoomObject = obs_data_get_string (settings, " zoom_object" );
320320
321+ obs_source_t *parent = obs_filter_get_parent (tf->source );
322+ if (tf->trackingEnabled ) {
323+ obs_log (LOG_INFO, " Tracking enabled" );
324+ // get the parent of the source
325+ // check if it has a crop/pad filter
326+ obs_source_t *crop_pad_filter = obs_source_get_filter_by_name (
327+ parent, " Detect Tracking" );
328+ if (!crop_pad_filter) {
329+ // create a crop-pad filter
330+ crop_pad_filter = obs_source_create (" crop_filter" ,
331+ " Detect Tracking" ,
332+ nullptr , nullptr );
333+ // add a crop/pad filter to the source
334+ // set the parent of the crop/pad filter to the parent of the source
335+ obs_source_filter_add (parent, crop_pad_filter);
336+ }
337+ tf->trackingFilter = crop_pad_filter;
338+ } else {
339+ obs_log (LOG_INFO, " Tracking disabled" );
340+ // remove the crop/pad filter
341+ obs_source_t *crop_pad_filter = obs_source_get_filter_by_name (
342+ parent, " Detect Tracking" );
343+ if (crop_pad_filter) {
344+ obs_source_filter_remove (parent, crop_pad_filter);
345+ }
346+ tf->trackingFilter = nullptr ;
347+ }
348+
321349 const std::string newUseGpu = obs_data_get_string (settings, " useGPU" );
322350 const uint32_t newNumThreads =
323351 (uint32_t )obs_data_get_int (settings, " numThreads" );
@@ -592,6 +620,82 @@ void detect_filter_video_tick(void *data, float seconds)
592620 std::lock_guard<std::mutex> lock (tf->outputLock );
593621 cv::cvtColor (frame, tf->outputPreviewBGRA , cv::COLOR_BGR2BGRA);
594622 }
623+
624+ if (tf->trackingEnabled && tf->trackingFilter ) {
625+ cv::Rect2f boundingBox =
626+ cv::Rect2f (0 , 0 , (float )frame.cols , (float )frame.rows );
627+ // get location of the objects
628+ if (tf->zoomObject == " single" ) {
629+ if (objects.size () > 0 ) {
630+ boundingBox = objects[0 ].rect ;
631+ }
632+ } else {
633+ // get the bounding box of all objects
634+ if (objects.size () > 0 ) {
635+ boundingBox = objects[0 ].rect ;
636+ for (const edgeyolo_cpp::Object &obj :
637+ objects) {
638+ boundingBox |= obj.rect ;
639+ }
640+ }
641+ }
642+ bool lostTracking = objects.size () == 0 ;
643+ // the zooming box should maintain the aspect ratio of the image
644+ // with the tf->zoomFactor controlling the effective buffer around the bounding box
645+ // the bounding box is the center of the zooming box
646+ float frameAspectRatio = (float )frame.cols / (float )frame.rows ;
647+ // calculate an aspect ratio box around the object using its height
648+ float boxHeight = boundingBox.height ;
649+ float boxWidth = boxHeight * frameAspectRatio;
650+ // calculate the zooming box size
651+ // when the zoom factor is 1, the zooming box is the same size as the bounding box
652+ // when the zoom factor is 10, the zooming box is the same size of the image
653+ float dh = frame.rows - boxHeight;
654+ float buffer = dh * ((tf->zoomFactor - 1 ) / 9 );
655+ float zh = boxHeight + buffer;
656+ float zw = zh * frameAspectRatio;
657+ // calculate the top left corner of the zooming box
658+ float zx = boundingBox.x - (zw - boundingBox.width ) / 2 ;
659+ float zy = boundingBox.y - (zh - boundingBox.height ) / 2 ;
660+
661+ if (tf->trackingRect .width == 0 ) {
662+ // initialize the trackingRect
663+ tf->trackingRect = cv::Rect2f (zx, zy, zw, zh);
664+ } else {
665+ // interpolate the zooming box to tf->trackingRect
666+ // the interpolation factor is (lostTracking) ? 0.1 : 0.5 to make the zooming box move smoothly
667+ float factor = lostTracking ? 0 .01f : 0 .05f ;
668+ tf->trackingRect .x = tf->trackingRect .x +
669+ factor * (zx - tf->trackingRect .x );
670+ tf->trackingRect .y = tf->trackingRect .y +
671+ factor * (zy - tf->trackingRect .y );
672+ tf->trackingRect .width =
673+ tf->trackingRect .width +
674+ factor * (zw - tf->trackingRect .width );
675+ tf->trackingRect .height =
676+ tf->trackingRect .height +
677+ factor * (zh - tf->trackingRect .height );
678+ }
679+
680+ // get the settings of the crop/pad filter
681+ obs_data_t *crop_pad_settings =
682+ obs_source_get_settings (tf->trackingFilter );
683+ obs_data_set_int (crop_pad_settings, " left" ,
684+ (int )tf->trackingRect .x );
685+ obs_data_set_int (crop_pad_settings, " top" ,
686+ (int )tf->trackingRect .y );
687+ // right = image width - (zx + zw)
688+ obs_data_set_int (crop_pad_settings, " right" ,
689+ (int )(frame.cols - (tf->trackingRect .x +
690+ tf->trackingRect .width )));
691+ // bottom = image height - (zy + zh)
692+ obs_data_set_int (crop_pad_settings, " bottom" ,
693+ (int )(frame.rows - (tf->trackingRect .y +
694+ tf->trackingRect .height )));
695+ // apply the settings
696+ obs_source_update (tf->trackingFilter , crop_pad_settings);
697+ obs_data_release (crop_pad_settings);
698+ }
595699}
596700
597701void detect_filter_video_render (void *data, gs_effect_t *_effect)
@@ -617,55 +721,60 @@ void detect_filter_video_render(void *data, gs_effect_t *_effect)
617721
618722 // if preview is enabled, render the image
619723 if (tf->preview || tf->maskingEnabled ) {
620- gs_texture_t *tex = nullptr ;
621- gs_texture_t *maskTexture = nullptr ;
622- std::string technique_name = " Draw" ;
623- gs_eparam_t *imageParam =
624- gs_effect_get_param_by_name (tf->maskingEffect , " image" );
625- gs_eparam_t *maskParam = gs_effect_get_param_by_name (
626- tf->maskingEffect , " focalmask" );
627- gs_eparam_t *maskColorParam =
628- gs_effect_get_param_by_name (tf->maskingEffect , " color" );
629-
724+ cv::Mat outputBGRA, outputMask;
630725 {
631726 // lock the outputLock mutex
632727 std::lock_guard<std::mutex> lock (tf->outputLock );
633728 if (tf->outputPreviewBGRA .empty ()) {
634729 obs_log (LOG_ERROR, " Preview image is empty" );
635- obs_source_skip_video_filter (tf->source );
730+ if (tf->source ) {
731+ obs_source_skip_video_filter (
732+ tf->source );
733+ }
636734 return ;
637735 }
638736 if ((uint32_t )tf->outputPreviewBGRA .cols != width ||
639737 (uint32_t )tf->outputPreviewBGRA .rows != height) {
640- obs_source_skip_video_filter (tf->source );
738+ if (tf->source ) {
739+ obs_source_skip_video_filter (
740+ tf->source );
741+ }
641742 return ;
642743 }
744+ outputBGRA = tf->outputPreviewBGRA .clone ();
745+ outputMask = tf->outputMask .clone ();
746+ }
643747
644- tex = gs_texture_create (
645- width, height, GS_BGRA, 1 ,
646- (const uint8_t **)&tf->outputPreviewBGRA .data ,
647- 0 );
648-
649- if (tf->maskingEnabled ) {
650- maskTexture = gs_texture_create (
651- width, height, GS_R8, 1 ,
652- (const uint8_t **)&tf->outputMask .data ,
653- 0 );
654- gs_effect_set_texture (maskParam, maskTexture);
655- if (tf->maskingType == " output_mask" ) {
656- technique_name = " DrawMask" ;
657- } else if (tf->maskingType == " blur" ) {
658- gs_texture_destroy (tex);
659- tex = blur_image (tf, width, height,
660- maskTexture);
661- } else if (tf->maskingType == " transparent" ) {
662- technique_name = " DrawSolidColor" ;
663- gs_effect_set_color (maskColorParam, 0 );
664- } else if (tf->maskingType == " solid_color" ) {
665- technique_name = " DrawSolidColor" ;
666- gs_effect_set_color (maskColorParam,
667- tf->maskingColor );
668- }
748+ gs_texture_t *tex = gs_texture_create (
749+ width, height, GS_BGRA, 1 ,
750+ (const uint8_t **)&outputBGRA.data , 0 );
751+ gs_texture_t *maskTexture = nullptr ;
752+ std::string technique_name = " Draw" ;
753+ gs_eparam_t *imageParam =
754+ gs_effect_get_param_by_name (tf->maskingEffect , " image" );
755+ gs_eparam_t *maskParam = gs_effect_get_param_by_name (
756+ tf->maskingEffect , " focalmask" );
757+ gs_eparam_t *maskColorParam =
758+ gs_effect_get_param_by_name (tf->maskingEffect , " color" );
759+
760+ if (tf->maskingEnabled ) {
761+ maskTexture = gs_texture_create (
762+ width, height, GS_R8, 1 ,
763+ (const uint8_t **)&outputMask.data , 0 );
764+ gs_effect_set_texture (maskParam, maskTexture);
765+ if (tf->maskingType == " output_mask" ) {
766+ technique_name = " DrawMask" ;
767+ } else if (tf->maskingType == " blur" ) {
768+ gs_texture_destroy (tex);
769+ tex = blur_image (tf, width, height,
770+ maskTexture);
771+ } else if (tf->maskingType == " transparent" ) {
772+ technique_name = " DrawSolidColor" ;
773+ gs_effect_set_color (maskColorParam, 0 );
774+ } else if (tf->maskingType == " solid_color" ) {
775+ technique_name = " DrawSolidColor" ;
776+ gs_effect_set_color (maskColorParam,
777+ tf->maskingColor );
669778 }
670779 }
671780
0 commit comments