22 #include "objdetectdata.pb.h"
30 #include <QStringList>
35 bool is_all_objects_key(
const std::string& name)
37 const QString normalized = QString::fromStdString(name).trimmed().toLower();
38 return normalized ==
"all" || normalized ==
"*" || normalized ==
"-1";
41 std::shared_ptr<TrackedObjectBBox> make_all_objects_properties(
42 const std::shared_ptr<TrackedObjectBase>& source,
43 bool has_mask_data =
false)
45 auto properties = std::make_shared<TrackedObjectBBox>();
47 properties->SetJsonValue(source->JsonValue());
48 auto source_bbox = std::dynamic_pointer_cast<TrackedObjectBBox>(source);
49 has_mask_data = has_mask_data || (source_bbox && source_bbox->HasMaskData());
54 properties->Id(
"All Objects");
58 bool has_tracked_object_mask_data(
59 const std::map<
int, std::shared_ptr<TrackedObjectBase>>& tracked_objects)
61 for (
const auto& tracked_object : tracked_objects) {
62 auto bbox = std::dynamic_pointer_cast<TrackedObjectBBox>(tracked_object.second);
63 if (bbox && bbox->HasMaskData())
69 cv::Scalar default_class_color(
const std::string& class_name,
int index)
71 const QString normalized = QString::fromStdString(class_name).trimmed().toLower();
75 if (normalized ==
"person")
return cv::Scalar(83, 160, 237);
76 if (normalized ==
"car")
return cv::Scalar(42, 200, 185);
77 if (normalized ==
"truck")
return cv::Scalar(239, 126, 92);
78 if (normalized ==
"bus")
return cv::Scalar(250, 196, 72);
79 if (normalized ==
"bicycle")
return cv::Scalar(122, 201, 67);
80 if (normalized ==
"motorbike" || normalized ==
"motorcycle")
return cv::Scalar(180, 126, 235);
81 if (normalized ==
"dog")
return cv::Scalar(237, 92, 140);
82 if (normalized ==
"cat")
return cv::Scalar(101, 214, 128);
84 static const cv::Scalar palette[] = {
85 cv::Scalar(83, 160, 237),
86 cv::Scalar(42, 200, 185),
87 cv::Scalar(239, 126, 92),
88 cv::Scalar(250, 196, 72),
89 cv::Scalar(122, 201, 67),
90 cv::Scalar(180, 126, 235),
91 cv::Scalar(237, 92, 140),
92 cv::Scalar(72, 190, 230),
94 return palette[index % (
sizeof(palette) /
sizeof(palette[0]))];
99 QImage image(mask.
width, mask.
height, QImage::Format_ARGB32_Premultiplied);
100 image.fill(Qt::transparent);
104 QRgb* data =
reinterpret_cast<QRgb*
>(image.bits());
108 for (uint32_t count : mask.
rle) {
109 const int end = std::min(total, offset +
static_cast<int>(count));
111 std::fill(data + offset, data + end, qRgba(255, 255, 255, 255));
124 ObjectDetection::ObjectDetection()
125 : display_box_text(1.0)
129 init_effect_details();
136 void ObjectDetection::init_effect_details()
154 std::shared_ptr<QImage> frame_image = frame->GetImage();
157 if(!frame_image || frame_image->isNull()) {
161 QPainter painter(frame_image.get());
162 painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
164 if (detectionsData.find(frame_number) != detectionsData.end()) {
166 for (
int i = 0; i < detections.
boxes.size(); i++) {
167 if (detections.
confidences.at(i) < confidence_threshold ||
168 (!display_classes.empty() &&
169 std::find(display_classes.begin(), display_classes.end(), classNames[detections.
classIds.at(i)]) == display_classes.end())) {
173 int objectId = detections.
objectIds.at(i);
177 std::shared_ptr<TrackedObjectBBox> trackedObject = std::static_pointer_cast<TrackedObjectBBox>(trackedObject_it->second);
180 if (parentClip && trackedObject->Contains(frame_number) && trackedObject->visible.GetValue(frame_number) == 1) {
181 BBox trackedBox = trackedObject->GetBox(frame_number);
182 QRectF boxRect((trackedBox.
cx - trackedBox.
width / 2) * frame_image->width(),
183 (trackedBox.
cy - trackedBox.
height / 2) * frame_image->height(),
184 trackedBox.
width * frame_image->width(),
185 trackedBox.
height * frame_image->height());
188 std::vector<int> stroke_rgba = trackedObject->stroke.GetColorRGBA(frame_number);
189 std::vector<int> bg_rgba = trackedObject->background.GetColorRGBA(frame_number);
190 float stroke_alpha = trackedObject->stroke_alpha.GetValue(frame_number);
191 float bg_alpha = trackedObject->background_alpha.GetValue(frame_number);
192 float bg_corner = trackedObject->background_corner.GetValue(frame_number);
195 QPen pen(QColor(stroke_rgba[0], stroke_rgba[1], stroke_rgba[2], 255 * stroke_alpha));
196 pen.setWidthF(trackedObject->ScaledStrokeWidth(
197 frame_number, frame_image->width(), frame_image->height()));
201 QBrush brush(QColor(bg_rgba[0], bg_rgba[1], bg_rgba[2], 255 * bg_alpha));
202 painter.setBrush(brush);
204 if (display_boxes.
GetValue(frame_number) == 1 && trackedObject->draw_box.GetValue(frame_number) == 1) {
206 painter.drawRoundedRect(boxRect, bg_corner, bg_corner);
209 ObjectMaskData object_mask = trackedObject->GetMask(frame_number, 5);
210 if (object_mask.
HasData() && trackedObject->draw_mask.GetValue(frame_number) == 1) {
211 QImage mask = alpha_mask_image_from_rle(object_mask)
212 .scaled(frame_image->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
213 std::vector<int> mask_rgba = trackedObject->mask_color.GetColorRGBA(frame_number);
214 float mask_alpha = trackedObject->mask_alpha.GetValue(frame_number);
215 QColor mask_color(mask_rgba[0], mask_rgba[1], mask_rgba[2], 255 * mask_alpha);
216 QImage overlay(frame_image->size(), QImage::Format_ARGB32_Premultiplied);
217 overlay.fill(Qt::transparent);
218 QPainter overlay_painter(&overlay);
219 overlay_painter.setCompositionMode(QPainter::CompositionMode_Source);
220 overlay_painter.fillRect(overlay.rect(), mask_color);
221 overlay_painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
222 overlay_painter.drawImage(0, 0, mask);
223 overlay_painter.end();
224 painter.drawImage(0, 0, overlay);
227 if(display_box_text.
GetValue(frame_number) == 1 && trackedObject->draw_text.GetValue(frame_number) == 1) {
230 int classId = detections.
classIds.at(i);
233 QString label = QString::number(objectId);
234 if (!classNames.empty()) {
235 label = QString::fromStdString(classNames[classId]) +
":" + label;
240 font.setPixelSize(14);
241 painter.setFont(font);
244 QFontMetrics fontMetrics(font);
245 QSize labelSize = fontMetrics.size(Qt::TextSingleLine, label);
248 double left = boxRect.center().x() - (labelSize.width() / 2.0);
249 double top = std::max(
static_cast<int>(boxRect.top()), labelSize.height()) - 4.0;
252 painter.drawText(QPointF(left, top), label);
269 pb_objdetect::ObjDetect objMessage;
270 std::fstream input(inputFilePath, std::ios::in | std::ios::binary);
271 if (!objMessage.ParseFromIstream(&input)) {
272 std::cerr <<
"Failed to parse protobuf message." << std::endl;
278 classesColor.clear();
279 detectionsData.clear();
283 for (
int i = 0; i < objMessage.classnames_size(); ++i) {
284 const std::string class_name = objMessage.classnames(i);
285 classNames.push_back(class_name);
286 classesColor.push_back(default_class_color(class_name, i));
290 for (
size_t fi = 0; fi < objMessage.frame_size(); ++fi) {
291 const auto &pbFrame = objMessage.frame(fi);
292 size_t frameId = pbFrame.id();
295 std::vector<int> classIds;
296 std::vector<float> confidences;
297 std::vector<cv::Rect_<float>> boxes;
298 std::vector<int> objectIds;
299 std::vector<ObjectMaskData> masks;
302 for (
int di = 0; di < pbFrame.bounding_box_size(); ++di) {
303 const auto &b = pbFrame.bounding_box(di);
304 float x = b.x(), y = b.y(), w = b.w(), h = b.h();
305 int classId = b.classid();
306 float confidence= b.confidence();
307 int objectId = b.objectid();
310 mask.
width = b.mask().width();
311 mask.
height = b.mask().height();
312 for (
int rleIndex = 0; rleIndex < b.mask().rle_size(); ++rleIndex) {
313 mask.
rle.push_back(b.mask().rle(rleIndex));
318 classIds.push_back(classId);
319 confidences.push_back(confidence);
320 boxes.emplace_back(x, y, w, h);
321 objectIds.push_back(objectId);
322 masks.push_back(mask);
327 it->second->AddBox(frameId, x + w/2, y + h/2, w, h, 0.0);
328 auto bbox = std::dynamic_pointer_cast<TrackedObjectBBox>(it->second);
330 bbox->AddMask(frameId, mask);
335 (
int)classesColor[classId][0],
336 (
int)classesColor[classId][1],
337 (
int)classesColor[classId][2],
342 tmpObj.
AddBox(frameId, x + w/2, y + h/2, w, h, 0.0);
346 auto ptr = std::make_shared<TrackedObjectBBox>(tmpObj);
350 std::string prefix = this->
Id();
353 ptr->Id(prefix + std::to_string(objectId));
360 classIds, confidences, boxes, frameId, objectIds, masks
364 google::protobuf::ShutdownProtobufLibrary();
379 root[
"visible_objects_index"] = Json::Value(Json::arrayValue);
380 root[
"visible_objects_id"] = Json::Value(Json::arrayValue);
381 root[
"visible_class_names"] = Json::Value(Json::arrayValue);
384 if (detectionsData.find(frame_number) == detectionsData.end()){
385 return root.toStyledString();
390 root[
"visible_objects_index"].append(-1);
391 root[
"visible_objects_id"].append(
"All Objects");
392 root[
"visible_class_names"].append(
"All Objects");
396 for(
int i = 0; i<detections.
boxes.size(); i++){
398 if(detections.
confidences.at(i) < confidence_threshold){
403 auto className = classNames[detections.
classIds.at(i)];
406 if (!display_classes.empty()) {
407 auto it = std::find(display_classes.begin(), display_classes.end(), className);
408 if (it == display_classes.end()) {
412 root[
"visible_class_names"].append(className);
415 root[
"visible_class_names"].append(className);
418 int objectId = detections.
objectIds.at(i);
423 Json::Value trackedObjectJSON = trackedObject->second->PropertiesJSON(frame_number);
425 if (trackedObjectJSON[
"visible"][
"value"].asBool() &&
426 trackedObject->second->ExactlyContains(frame_number)){
428 root[
"visible_objects_index"].append(trackedObject->first);
429 root[
"visible_objects_id"].append(trackedObject->second->Id());
433 return root.toStyledString();
437 if (!target_image || target_image->isNull())
440 auto detections_it = detectionsData.find(frame_number);
441 if (detections_it == detectionsData.end())
444 auto mask_image = std::make_shared<QImage>(
445 target_image->width(), target_image->height(), QImage::Format_RGBA8888_Premultiplied);
446 mask_image->fill(QColor(0, 0, 0, 255));
448 QPainter painter(mask_image.get());
449 painter.setRenderHint(QPainter::Antialiasing,
true);
450 painter.setPen(Qt::NoPen);
451 painter.setBrush(QBrush(QColor(255, 255, 255, 255)));
453 bool drew_any_box =
false;
455 for (
int i = 0; i < detections.
boxes.size(); i++) {
456 if (detections.
confidences.at(i) < confidence_threshold)
459 const int class_id = detections.
classIds.at(i);
460 if (class_id < 0 || class_id >= classNames.size())
463 const std::string class_name = classNames[class_id];
464 if (!display_classes.empty() &&
465 std::find(display_classes.begin(), display_classes.end(), class_name) == display_classes.end()) {
469 int object_id = detections.
objectIds.at(i);
471 if (tracked_object_it ==
trackedObjects.end() || !tracked_object_it->second)
474 auto tracked_object = std::static_pointer_cast<TrackedObjectBBox>(tracked_object_it->second);
475 if (!tracked_object->ExactlyContains(frame_number) ||
476 tracked_object->visible.GetValue(frame_number) != 1) {
480 BBox box = tracked_object->GetBox(frame_number);
481 if (box.
width <= 0.0f || box.
height <= 0.0f || box.
cx < 0.0f || box.
cy < 0.0f)
484 ObjectMaskData object_mask = tracked_object->GetMask(frame_number, 5);
485 if (object_mask.
HasData() && tracked_object->draw_mask.GetValue(frame_number) == 1) {
486 QImage mask = alpha_mask_image_from_rle(object_mask)
487 .scaled(target_image->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
488 painter.drawImage(0, 0, mask);
493 const double x = (box.
cx - box.
width / 2.0) * target_image->
width();
494 const double y = (box.
cy - box.
height / 2.0) * target_image->height();
495 const double w = box.
width * target_image->width();
496 const double h = box.
height * target_image->height();
497 const double corner = tracked_object->background_corner.GetValue(frame_number);
498 painter.drawRoundedRect(QRectF(x, y, w, h), corner, corner);
521 root[
"protobuf_data_path"] = protobuf_data_path;
523 root[
"confidence_threshold"] = confidence_threshold;
524 root[
"display_box_text"] = display_box_text.
JsonValue();
525 root[
"display_boxes"] = display_boxes.
JsonValue();
530 Json::Value trackedObjectJSON = trackedObject.second->JsonValue();
532 objects[trackedObject.second->Id()] = trackedObjectJSON;
534 root[
"objects"] = objects;
550 catch (
const std::exception& e)
553 throw InvalidJSON(
"JSON is invalid (missing keys or invalid data types)");
564 if (!root[
"protobuf_data_path"].isNull()) {
565 std::string new_path = root[
"protobuf_data_path"].asString();
567 protobuf_data_path = new_path;
568 allObjectsProperties.reset();
570 throw InvalidFile(
"Invalid protobuf data path",
"");
576 if (!root[
"selected_object_index"].isNull())
578 if (!root[
"confidence_threshold"].isNull())
579 confidence_threshold = root[
"confidence_threshold"].asFloat();
580 if (!root[
"display_box_text"].isNull())
581 display_box_text.
SetJsonValue(root[
"display_box_text"]);
582 if (!root[
"display_boxes"].isNull())
585 if (!root[
"class_filter"].isNull()) {
586 class_filter = root[
"class_filter"].asString();
587 QStringList parts = QString::fromStdString(class_filter).split(
',');
588 display_classes.clear();
589 for (
auto &p : parts) {
590 auto s = p.trimmed().toLower();
592 display_classes.push_back(s.toStdString());
598 if (!root[
"objects"].isNull()) {
600 const auto memberNames = root[
"objects"].getMemberNames();
601 for (
const auto& name : memberNames)
603 if (is_all_objects_key(name)) {
604 if (!allObjectsProperties) {
605 std::shared_ptr<TrackedObjectBase> firstObject;
608 allObjectsProperties = make_all_objects_properties(
611 allObjectsProperties->SetJsonValue(root[
"objects"][name]);
613 if (trackedObject.second)
614 trackedObject.second->SetJsonValue(root[
"objects"][name]);
619 for (
const auto& name : memberNames)
621 if (is_all_objects_key(name))
625 bool numeric_key = std::all_of(name.begin(), name.end(), ::isdigit);
627 index = std::stoi(name);
631 size_t pos = name.find_last_of(
'-');
632 if (pos != std::string::npos) {
634 index = std::stoi(name.substr(pos + 1));
645 obj_it->second->Id(name);
646 obj_it->second->SetJsonValue(root[
"objects"][name]);
651 if (!root[
"objects_id"].isNull()) {
653 if (!root[
"objects_id"][kv.first].isNull())
654 kv.second->Id(root[
"objects_id"][kv.first].asString());
667 auto selectedObject = allObjectsProperties
668 ? allObjectsProperties
671 Json::Value trackedObjectJSON = selectedObject->PropertiesJSON(requested_frame);
672 trackedObjectJSON[
"box_id"][
"memo"] =
"All Objects";
673 trackedObjectJSON.removeMember(
"x1");
674 trackedObjectJSON.removeMember(
"y1");
675 trackedObjectJSON.removeMember(
"x2");
676 trackedObjectJSON.removeMember(
"y2");
677 objects[
"all"] = trackedObjectJSON;
683 Json::Value trackedObjectJSON = selectedObject->PropertiesJSON(requested_frame);
685 objects[selectedObject->Id()] = trackedObjectJSON;
688 root[
"objects"] = objects;
691 root[
"confidence_threshold"] =
add_property_json(
"Confidence Threshold", confidence_threshold,
"float",
"", NULL, 0, 1,
false, requested_frame);
692 root[
"class_filter"] =
add_property_json(
"Class Filter", 0.0,
"string", class_filter, NULL, -1, -1,
false, requested_frame);
695 return root.toStyledString();