Skip to content
This repository was archived by the owner on Dec 18, 2024. It is now read-only.

Commit 8659156

Browse files
committed
Add tracking filter and tracking rectangle to FilterData struct
1 parent 13a143d commit 8659156

File tree

3 files changed

+177
-37
lines changed

3 files changed

+177
-37
lines changed

data/locale/en-US.ini

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
Detect="Detect"
2+
Advanced="Advanced Settings"
3+
ConfThreshold="Confidence Threshold"
4+
InferenceDevice="Inference Device"
5+
CPU="CPU"
6+
GPUTensorRT="GPU (TensorRT)"
7+
GPUDirectML="GPU (DirectML)"
8+
CoreML="CoreML"
9+
NumThreads="Number of Threads"
10+
ModelSize="Model Size"
11+
SmallFast="Small (Fast)"
12+
Medium="Medium"
13+
LargeSlow="Large (Accurate)"
14+
Preview="Preview"
15+
ObjectCategory="Object Category"
16+
All="All"
17+
MaskingGroup="Masking Options"
18+
MaskingType="Masking Type"
19+
None="None"
20+
SolidColor="Solid Color"
21+
Blur="Blur"
22+
OutputMask="Output Mask"
23+
Transparent="Transparent"
24+
MaskingColor="Masking Color"
25+
MaskingBlurRadius="Masking Blur Radius"
26+
TrackingZoomFollowGroup="Tracking (Zoom, Follow) Options"
27+
ZoomFactor="Zoom Factor"
28+
ZoomObject="Zoom Object"
29+
SingleFirst="Single (First)"

src/FilterData.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ struct filter_data {
2424
bool trackingEnabled;
2525
float zoomFactor;
2626
std::string zoomObject;
27+
obs_source_t *trackingFilter;
28+
cv::Rect2f trackingRect;
2729

2830
obs_source_t *source;
2931
gs_texrender_t *texrender;

src/detect-filter.cpp

Lines changed: 146 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -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

597701
void 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

Comments
 (0)