Skip to content

Commit

Permalink
Fix memory access violation from multiple threads
Browse files Browse the repository at this point in the history
When multiple threads access an image object in dlib's type, it caused
crash. This crash happened when CPU has high load and correlation
tracker becomes super slow.
Instead of having the image in dlib's type, store in the thread-safe
type obs_source_frame and create dlib's type in each thread.
  • Loading branch information
norihiro committed Apr 20, 2023
1 parent 56659d0 commit 7e16091
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 41 deletions.
24 changes: 14 additions & 10 deletions src/face-detector-dlib-cnn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,22 @@ void face_detector_dlib_cnn::detect_main()
{
if (!p->tex)
return;
const image_t *img = &p->tex->get_dlib_rgb_image();

dlib::matrix<dlib::rgb_pixel> img;
if (!p->tex->get_dlib_rgb_image(img))
return;

int x0 = 0, y0 = 0;
image_t img_crop;
if (p->crop_l > 0 || p->crop_r > 0 || p->crop_t > 0 || p->crop_b > 0) {
image_t img_crop;
x0 = (int)(p->crop_l / p->tex->scale);
int x1 = img->nc() - (int)(p->crop_r / p->tex->scale);
int x1 = img.nc() - (int)(p->crop_r / p->tex->scale);
y0 = (int)(p->crop_t / p->tex->scale);
int y1 = img->nr() - (int)(p->crop_b / p->tex->scale);
int y1 = img.nr() - (int)(p->crop_b / p->tex->scale);
if (x1 - x0 < 80 || y1 - y0 < 80) {
if (p->n_error++ < MAX_ERROR)
blog(LOG_ERROR, "too small image: %dx%d cropped left=%d right=%d top=%d bottom=%d",
(int)img->nc(), (int)img->nr(),
(int)img.nc(), (int)img.nr(),
p->crop_l, p->crop_r, p->crop_t, p->crop_b );
return;
}
Expand All @@ -77,14 +81,14 @@ void face_detector_dlib_cnn::detect_main()
img_crop.set_size(y1 - y0, x1 - x0);
for (int y = y0; y < y1; y++) {
for (int x = x0; x < x1; x++) {
img_crop(y-y0, x-x0) = (*img)(y, x);
img_crop(y-y0, x-x0) = img(y, x);
}
}
img = &img_crop;
img = img_crop;
}
if (img->nc()<80 || img->nr()<80) {
if (img.nc()<80 || img.nr()<80) {
if (p->n_error++ < MAX_ERROR)
blog(LOG_ERROR, "too small image: %dx%d", (int)img->nc(), (int)img->nr());
blog(LOG_ERROR, "too small image: %dx%d", (int)img.nc(), (int)img.nr());
return;
}
else if (p->n_error) {
Expand All @@ -107,7 +111,7 @@ void face_detector_dlib_cnn::detect_main()
if (p->has_error)
return;

auto dets = p->net(*img);
auto dets = p->net(img);
p->rects.resize(dets.size());
for (size_t i = 0; i < dets.size(); i++) {
auto &det = dets[i];
Expand Down
24 changes: 14 additions & 10 deletions src/face-detector-dlib-hog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,22 @@ void face_detector_dlib_hog::detect_main()
{
if (!p->tex)
return;
const auto *img = &p->tex->get_dlib_rgb_image();

dlib::matrix<dlib::rgb_pixel> img;
if (!p->tex->get_dlib_rgb_image(img))
return;

int x0 = 0, y0 = 0;
dlib::matrix<dlib::rgb_pixel> img_crop;
if (p->crop_l > 0 || p->crop_r > 0 || p->crop_t > 0 || p->crop_b > 0) {
dlib::matrix<dlib::rgb_pixel> img_crop;
x0 = (int)(p->crop_l / p->tex->scale);
int x1 = img->nc() - (int)(p->crop_r / p->tex->scale);
int x1 = img.nc() - (int)(p->crop_r / p->tex->scale);
y0 = (int)(p->crop_t / p->tex->scale);
int y1 = img->nr() - (int)(p->crop_b / p->tex->scale);
int y1 = img.nr() - (int)(p->crop_b / p->tex->scale);
if (x1 - x0 < 80 || y1 - y0 < 80) {
if (p->n_error++ < MAX_ERROR)
blog(LOG_ERROR, "too small image: %dx%d cropped left=%d right=%d top=%d bottom=%d",
(int)img->nc(), (int)img->nr(),
(int)img.nc(), (int)img.nr(),
p->crop_l, p->crop_r, p->crop_t, p->crop_b );
return;
}
Expand All @@ -71,14 +75,14 @@ void face_detector_dlib_hog::detect_main()
img_crop.set_size(y1 - y0, x1 - x0);
for (int y = y0; y < y1; y++) {
for (int x = x0; x < x1; x++) {
img_crop(y-y0, x-x0) = (*img)(y, x);
img_crop(y-y0, x-x0) = img(y, x);
}
}
img = &img_crop;
img = img_crop;
}
if (img->nc()<80 || img->nr()<80) {
if (img.nc()<80 || img.nr()<80) {
if (p->n_error++ < MAX_ERROR)
blog(LOG_ERROR, "too small image: %dx%d", (int)img->nc(), (int)img->nr());
blog(LOG_ERROR, "too small image: %dx%d", (int)img.nc(), (int)img.nr());
return;
}
else if (p->n_error) {
Expand All @@ -88,7 +92,7 @@ void face_detector_dlib_hog::detect_main()
if (!p->detector)
p->detector = new dlib::frontal_face_detector(dlib::get_frontal_face_detector());

std::vector<dlib::rectangle> dets = (*p->detector)(*img);
std::vector<dlib::rectangle> dets = (*p->detector)(img);
p->rects.resize(dets.size());
for (size_t i=0; i<dets.size(); i++) {
rect_s &r = p->rects[i];
Expand Down
12 changes: 9 additions & 3 deletions src/face-tracker-dlib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,10 @@ void face_tracker_dlib::track_main()
if (!p->tracker)
p->tracker = new dlib::correlation_tracker();

auto &img = p->tex->get_dlib_rgb_image();
dlib::matrix<dlib::rgb_pixel> img;
if (!p->tex->get_dlib_rgb_image(img))
return;

dlib::rectangle r (p->rect.x0, p->rect.y0, p->rect.x1, p->rect.y1);
p->tracker->start_track(img, r);
p->tracker_nc = img.nc();
Expand All @@ -118,7 +121,10 @@ void face_tracker_dlib::track_main()
p->rect.score = 0.0f;
}
else {
auto &img = p->tex->get_dlib_rgb_image();
dlib::matrix<dlib::rgb_pixel> img;
if (!p->tex->get_dlib_rgb_image(img))
return;

if (img.nc() != p->tracker_nc || img.nr() != p->tracker_nr) {
blog(LOG_ERROR, "face_tracker_dlib::track_main: cannot run correlation-tracker with different image size %dx%d, expected %dx%d",
(int)img.nc(), (int)img.nr(),
Expand Down Expand Up @@ -157,7 +163,7 @@ void face_tracker_dlib::track_main()
internal_division(r.left(), r.right(), p->upsize.x0 + 1.0f, p->upsize.x1),
internal_division(r.top(), r.bottom(), p->upsize.y0 + 1.0f, p->upsize.y1) );

p->shape = p->sp(p->tex->get_dlib_rgb_image(), r_face);
p->shape = p->sp(img, r_face);
p->last_scale = p->tex->scale;
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/face-tracker-ptz.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -954,7 +954,7 @@ static bool scale_set_texture(struct face_tracker_ptz *s, std::shared_ptr<textur
return NULL;
}

cvtex.get()->set_texture_obsframe_scale(&scaled_frame, 1);
cvtex.get()->set_texture_obsframe(&scaled_frame, 1);
return true;
}

Expand All @@ -970,7 +970,7 @@ static struct obs_source_frame *ftptz_filter_video(void *data, struct obs_source
cvtex.get()->tick = s->ftm->tick_cnt;

if (is_rgb_format(frame->format)) {
cvtex.get()->set_texture_obsframe_scale(frame, s->ftm->scale);
cvtex.get()->set_texture_obsframe(frame, s->ftm->scale);
} else {
scale_set_texture(s, cvtex, frame);
}
Expand Down
2 changes: 1 addition & 1 deletion src/face-tracker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -752,7 +752,7 @@ static inline std::shared_ptr<texture_object> surface_to_cvtex(struct face_track
frame.width = width;
frame.height = height;
frame.format = VIDEO_FORMAT_BGRA;
cvtex.get()->set_texture_obsframe_scale(&frame, 1);
cvtex.get()->set_texture_obsframe(&frame, 1);

gs_stagesurface_unmap(s->stagesurface);

Expand Down
53 changes: 40 additions & 13 deletions src/texture-object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,19 @@ static uint32_t formats_found = 0;

struct texture_object_private_s
{
dlib::matrix<dlib::rgb_pixel> dlib_rgb_image;
void *leak_test;
struct obs_source_frame *obs_frame = NULL;
int scale = 0;
};

texture_object::texture_object()
{
data = new texture_object_private_s;
data->leak_test = bmalloc(1);
data->obs_frame = NULL;
}

texture_object::~texture_object()
{
bfree(data->leak_test);
obs_source_frame_destroy(data->obs_frame);
delete data;
}

Expand Down Expand Up @@ -57,30 +57,57 @@ static void obsframe2dlib_rgbx(dlib::matrix<dlib::rgb_pixel> &img, const struct
}
}

void texture_object::set_texture_obsframe_scale(const struct obs_source_frame *frame, int scale)
static bool need_allocate_frame(const struct obs_source_frame *dst, const struct obs_source_frame *src)
{
if (!dst)
return true;

if (dst->format != src->format)
return true;

if (dst->width != src->width || dst->height != src->height)
return true;

return false;
}

void texture_object::set_texture_obsframe(const struct obs_source_frame *frame, int scale)
{
if (need_allocate_frame(data->obs_frame, frame)) {
obs_source_frame_destroy(data->obs_frame);
data->obs_frame = obs_source_frame_create(frame->format, frame->width, frame->height);
}

obs_source_frame_copy(data->obs_frame, frame);
data->scale = scale;
}

bool texture_object::get_dlib_rgb_image(dlib::matrix<dlib::rgb_pixel> &img) const
{
if (!data->obs_frame)
return false;

const auto *frame = data->obs_frame;
const int scale = data->scale;
if (TEST_FORMAT(frame->format))
blog(LOG_INFO, "received frame format=%d", frame->format);
data->dlib_rgb_image.set_size(frame->height/scale, frame->width/scale);
img.set_size(frame->height / scale, frame->width / scale);
switch(frame->format) {
case VIDEO_FORMAT_BGRX:
case VIDEO_FORMAT_BGRA:
obsframe2dlib_bgrx(data->dlib_rgb_image, frame, scale);
obsframe2dlib_bgrx(img, frame, scale);
break;
case VIDEO_FORMAT_BGR3:
obsframe2dlib_bgrx(data->dlib_rgb_image, frame, scale, 3);
obsframe2dlib_bgrx(img, frame, scale, 3);
break;
case VIDEO_FORMAT_RGBA:
obsframe2dlib_rgbx(data->dlib_rgb_image, frame, scale);
obsframe2dlib_rgbx(img, frame, scale);
break;
default:
if (TEST_FORMAT(frame->format))
blog(LOG_ERROR, "Frame format %d has to be RGB", (int)frame->format);
}
SET_FORMAT(frame->format);
}

const dlib::matrix<dlib::rgb_pixel> &texture_object::get_dlib_rgb_image()
{
return data->dlib_rgb_image;
return true;
}
4 changes: 2 additions & 2 deletions src/texture-object.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ class texture_object
texture_object();
~texture_object();

void set_texture_obsframe_scale(const struct obs_source_frame *frame, int scale);
const dlib::matrix<dlib::rgb_pixel> &get_dlib_rgb_image();
void set_texture_obsframe(const struct obs_source_frame *frame, int scale);
bool get_dlib_rgb_image(dlib::matrix<dlib::rgb_pixel> &img) const;

public:
int tick;
Expand Down

0 comments on commit 7e16091

Please sign in to comment.