17 #include "objdetectdata.pb.h"
19 #define int64 opencv_broken_int
20 #define uint64 opencv_broken_uint
21 #include <opencv2/core.hpp>
22 #include <opencv2/imgproc.hpp>
39 QImage image(mask.
width, mask.
height, QImage::Format_ARGB32_Premultiplied);
40 image.fill(Qt::transparent);
44 QRgb* data =
reinterpret_cast<QRgb*
>(image.bits());
48 for (uint32_t count : mask.
rle) {
49 const int end = std::min(total, offset +
static_cast<int>(count));
51 std::fill(data + offset, data + end, qRgba(255, 255, 255, 255));
60 cv::Mat BinaryMaskFromImage(
const QImage& image)
62 QImage rgba = image.convertToFormat(QImage::Format_RGBA8888);
63 cv::Mat binary(rgba.height(), rgba.width(), CV_8UC1, cv::Scalar(0));
64 for (
int y = 0; y < rgba.height(); ++y) {
65 const uchar* source = rgba.constScanLine(y);
66 uchar* target = binary.ptr<uchar>(y);
67 for (
int x = 0; x < rgba.width(); ++x)
68 target[x] = source[x * 4 + 3] > 0 ? 255 : 0;
73 QImage StrokeImageFromMask(
const QImage& alphaMask,
int width)
75 QImage result(alphaMask.size(), QImage::Format_ARGB32_Premultiplied);
76 result.fill(Qt::transparent);
80 cv::Mat binary = BinaryMaskFromImage(alphaMask);
82 const int kernelSize = std::max(1, width * 2 + 1);
83 cv::Mat kernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(kernelSize, kernelSize));
84 cv::dilate(binary, dilated, kernel);
85 cv::Mat edge = dilated - binary;
87 for (
int y = 0; y < edge.rows; ++y) {
88 const uchar* edgeRow = edge.ptr<uchar>(y);
89 QRgb* target =
reinterpret_cast<QRgb*
>(result.scanLine(y));
90 for (
int x = 0; x < edge.cols; ++x) {
92 target[x] = qRgba(255, 255, 255, 255);
102 , mask_color(83, 160, 237, 255)
103 , mask_alpha(120.0 / 255.0)
104 , stroke_color(255, 255, 255, 255)
108 init_effect_details();
111 void ObjectMask::init_effect_details()
116 info.
description =
"Create and draw a segmentation mask for a prompted object.";
124 std::shared_ptr<QImage> frame_image = frame->GetImage();
125 if (!frame_image || frame_image->isNull() ||
draw_mask.
GetValue(frame_number) != 1)
128 auto mask_it = masksData.find(frame_number);
129 if (mask_it == masksData.end() || !mask_it->second.HasData())
132 QImage alpha_mask = AlphaMaskImageFromRLE(mask_it->second)
133 .scaled(frame_image->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
135 QColor overlay_color(mask_rgba[0], mask_rgba[1], mask_rgba[2], 255 *
mask_alpha.
GetValue(frame_number));
137 QImage overlay(frame_image->size(), QImage::Format_ARGB32_Premultiplied);
138 overlay.fill(Qt::transparent);
139 QPainter overlay_painter(&overlay);
140 overlay_painter.setCompositionMode(QPainter::CompositionMode_Source);
141 overlay_painter.fillRect(overlay.rect(), overlay_color);
142 overlay_painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
143 overlay_painter.drawImage(0, 0, alpha_mask);
144 overlay_painter.end();
146 QPainter painter(frame_image.get());
147 painter.drawImage(0, 0, overlay);
151 QImage stroke_mask = StrokeImageFromMask(alpha_mask, strokeWidth);
153 QColor stroke_qcolor(stroke_rgba[0], stroke_rgba[1], stroke_rgba[2], 255 *
stroke_alpha.
GetValue(frame_number));
155 QImage stroke_overlay(frame_image->size(), QImage::Format_ARGB32_Premultiplied);
156 stroke_overlay.fill(Qt::transparent);
157 QPainter stroke_painter(&stroke_overlay);
158 stroke_painter.setCompositionMode(QPainter::CompositionMode_Source);
159 stroke_painter.fillRect(stroke_overlay.rect(), stroke_qcolor);
160 stroke_painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
161 stroke_painter.drawImage(0, 0, stroke_mask);
162 stroke_painter.end();
163 painter.drawImage(0, 0, stroke_overlay);
172 pb_objdetect::ObjDetect objMessage;
173 std::fstream input(inputFilePath, std::ios::in | std::ios::binary);
174 if (!objMessage.ParseFromIstream(&input))
180 auto trackedObject = std::make_shared<TrackedObjectBBox>(83, 160, 237, 255);
181 trackedObject->Id(
Id().empty() ?
"Object Mask" :
Id() +
"-1");
182 trackedObject->ParentClip(this->
ParentClip());
183 trackedObject->draw_box =
Keyframe(0.0);
184 trackedObject->draw_text =
Keyframe(0.0);
192 for (
int frameIndex = 0; frameIndex < objMessage.frame_size(); ++frameIndex) {
193 const auto& pbFrame = objMessage.frame(frameIndex);
194 if (pbFrame.bounding_box_size() <= 0)
197 const auto& box = pbFrame.bounding_box(0);
199 mask.
box =
BBox(box.x() + box.w() / 2.0f, box.y() + box.h() / 2.0f, box.w(), box.h(), 0.0f);
200 mask.
score = box.confidence();
201 if (box.has_mask()) {
202 mask.
width = box.mask().width();
203 mask.
height = box.mask().height();
204 for (
int rleIndex = 0; rleIndex < box.mask().rle_size(); ++rleIndex)
205 mask.
rle.push_back(box.mask().rle(rleIndex));
207 masksData[pbFrame.id()] = mask;
214 trackedMask.
rle = mask.
rle;
215 trackedObject->AddMask(pbFrame.id(), trackedMask);
219 if (!masksData.empty())
222 google::protobuf::ShutdownProtobufLibrary();
228 if (!target_image || target_image->isNull())
231 auto mask_it = masksData.find(frame_number);
232 if (mask_it == masksData.end() || !mask_it->second.HasData())
235 QImage alpha_mask = AlphaMaskImageFromRLE(mask_it->second)
236 .scaled(target_image->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
238 auto mask_image = std::make_shared<QImage>(
239 target_image->width(), target_image->height(), QImage::Format_RGBA8888_Premultiplied);
240 mask_image->fill(QColor(0, 0, 0, 255));
241 QPainter painter(mask_image.get());
242 painter.drawImage(0, 0, alpha_mask);
256 root[
"protobuf_data_path"] = protobuf_data_path;
270 }
catch (
const std::exception&) {
271 throw InvalidJSON(
"JSON is invalid (missing keys or invalid data types)");
279 if (!root[
"draw_mask"].isNull())
281 if (!root[
"mask_color"].isNull())
283 if (!root[
"mask_alpha"].isNull())
285 if (!root[
"stroke_color"].isNull())
287 if (!root[
"stroke"].isNull())
289 if (!root[
"stroke_alpha"].isNull())
291 if (!root[
"stroke_width"].isNull())
294 if (!root[
"protobuf_data_path"].isNull()) {
295 std::string new_path = root[
"protobuf_data_path"].asString();
296 if (protobuf_data_path != new_path || masksData.empty()) {
297 protobuf_data_path = new_path;
299 throw InvalidFile(
"Invalid object mask protobuf data path",
"");
307 root[
"protobuf_data_path"] =
add_property_json(
"Object Mask Data", 0.0,
"string", protobuf_data_path, NULL, -1, -1,
false, requested_frame);
313 root[
"mask_color"] =
add_property_json(
"Mask Color", 0.0,
"color",
"", NULL, 0, 255,
false, requested_frame);
319 root[
"stroke_color"] =
add_property_json(
"Stroke Color", 0.0,
"color",
"", NULL, 0, 255,
false, requested_frame);
326 return root.toStyledString();