Skip to content
Snippets Groups Projects
Commit a7741367 authored by Nicolas Pope's avatar Nicolas Pope
Browse files

Generate stereo mkv videos

parent b171f76a
No related branches found
No related tags found
No related merge requests found
...@@ -41,18 +41,21 @@ static AVStream *add_video_stream(AVFormatContext *oc, const ftl::codecs::Packet ...@@ -41,18 +41,21 @@ static AVStream *add_video_stream(AVFormatContext *oc, const ftl::codecs::Packet
//c->codec_id = codec_id; //c->codec_id = codec_id;
//c->codec_type = AVMEDIA_TYPE_VIDEO; //c->codec_type = AVMEDIA_TYPE_VIDEO;
st->time_base.den = 20; //st->time_base.den = 20;
st->time_base.num = 1; //st->time_base.num = 1;
//st->id = oc->nb_streams-1; //st->id = oc->nb_streams-1;
//st->nb_frames = 0; //st->nb_frames = 0;
st->codecpar->codec_id = codec_id; st->codecpar->codec_id = codec_id;
st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
st->codecpar->width = ftl::codecs::getWidth(pkt.definition); st->codecpar->width = ftl::codecs::getWidth(pkt.definition);
//if (pkt.flags & ftl::codecs::kFlagStereo) st->codecpar->width *= 2;
st->codecpar->height = ftl::codecs::getHeight(pkt.definition); st->codecpar->height = ftl::codecs::getHeight(pkt.definition);
st->codecpar->format = AV_PIX_FMT_NV12; st->codecpar->format = AV_PIX_FMT_NV12;
st->codecpar->bit_rate = 4000000; st->codecpar->bit_rate = 4000000;
if (pkt.flags & ftl::codecs::kFlagStereo) av_dict_set_int(&st->metadata, "stereo_mode", 1, 0); if (pkt.flags & ftl::codecs::kFlagStereo) av_dict_set(&st->metadata, "stereo_mode", "left_right", 0);
//if (pkt.flags & ftl::codecs::kFlagStereo) av_dict_set(&oc->metadata, "stereo_mode", "1", 0);
//if (pkt.flags & ftl::codecs::kFlagStereo) av_dict_set_int(&st->metadata, "StereoMode", 1, 0);
/* put sample parameters */ /* put sample parameters */
//c->bit_rate = 4000000; //c->bit_rate = 4000000;
...@@ -141,9 +144,11 @@ int main(int argc, char **argv) { ...@@ -141,9 +144,11 @@ int main(int argc, char **argv) {
//bool stream_added[10] = {false}; //bool stream_added[10] = {false};
int64_t first_ts = 10000000000000ll;
// TODO: In future, find a better way to discover number of streams... // TODO: In future, find a better way to discover number of streams...
// Read entire file to find all streams before reading again to write data // Read entire file to find all streams before reading again to write data
bool res = r.read(90000000000000, [&current_stream,&current_channel,&r,&video_st,oc](const ftl::codecs::StreamPacket &spkt, const ftl::codecs::Packet &pkt) { bool res = r.read(90000000000000, [&first_ts,&current_stream,&current_channel,&r,&video_st,oc](const ftl::codecs::StreamPacket &spkt, const ftl::codecs::Packet &pkt) {
if (spkt.channel != static_cast<ftl::codecs::Channel>(current_channel) && current_channel != -1) return; if (spkt.channel != static_cast<ftl::codecs::Channel>(current_channel) && current_channel != -1) return;
if (spkt.frame_number == current_stream || current_stream == 255) { if (spkt.frame_number == current_stream || current_stream == 255) {
...@@ -153,6 +158,8 @@ int main(int argc, char **argv) { ...@@ -153,6 +158,8 @@ int main(int argc, char **argv) {
if (spkt.frame_number >= 10) return; // TODO: Allow for more than 10 if (spkt.frame_number >= 10) return; // TODO: Allow for more than 10
if (spkt.timestamp < first_ts) first_ts = spkt.timestamp;
if (video_st[spkt.frame_number][(spkt.channel == Channel::Left) ? 0 : 1] == nullptr) { if (video_st[spkt.frame_number][(spkt.channel == Channel::Left) ? 0 : 1] == nullptr) {
video_st[spkt.frame_number][(spkt.channel == Channel::Left) ? 0 : 1] = add_video_stream(oc, pkt); video_st[spkt.frame_number][(spkt.channel == Channel::Left) ? 0 : 1] = add_video_stream(oc, pkt);
} }
...@@ -177,7 +184,7 @@ int main(int argc, char **argv) { ...@@ -177,7 +184,7 @@ int main(int argc, char **argv) {
bool seen_key[10] = {false}; bool seen_key[10] = {false};
res = r.read(90000000000000, [&current_stream,&current_channel,&r,&video_st,oc,&seen_key](const ftl::codecs::StreamPacket &spkt, const ftl::codecs::Packet &pkt) { res = r.read(90000000000000, [first_ts,&current_stream,&current_channel,&r,&video_st,oc,&seen_key](const ftl::codecs::StreamPacket &spkt, const ftl::codecs::Packet &pkt) {
if (spkt.channel != static_cast<ftl::codecs::Channel>(current_channel) && current_channel != -1) return; if (spkt.channel != static_cast<ftl::codecs::Channel>(current_channel) && current_channel != -1) return;
if (spkt.frame_number == current_stream || current_stream == 255) { if (spkt.frame_number == current_stream || current_stream == 255) {
...@@ -203,10 +210,14 @@ int main(int argc, char **argv) { ...@@ -203,10 +210,14 @@ int main(int argc, char **argv) {
} }
if (!seen_key[spkt.frame_number]) return; if (!seen_key[spkt.frame_number]) return;
//if (spkt.timestamp > last_ts) framecount++;
//last_ts = spkt.timestamp;
AVPacket avpkt; AVPacket avpkt;
av_init_packet(&avpkt); av_init_packet(&avpkt);
if (keyframe) avpkt.flags |= AV_PKT_FLAG_KEY; if (keyframe) avpkt.flags |= AV_PKT_FLAG_KEY;
avpkt.pts = spkt.timestamp - r.getStartTime(); //avpkt.pts = framecount*50; //spkt.timestamp - r.getStartTime();
avpkt.pts = spkt.timestamp - first_ts;
avpkt.dts = avpkt.pts; avpkt.dts = avpkt.pts;
avpkt.stream_index= video_st[spkt.frame_number][(spkt.channel == Channel::Left) ? 0 : 1]->index; avpkt.stream_index= video_st[spkt.frame_number][(spkt.channel == Channel::Left) ? 0 : 1]->index;
avpkt.data= const_cast<uint8_t*>(pkt.data.data()); avpkt.data= const_cast<uint8_t*>(pkt.data.data());
......
...@@ -410,11 +410,6 @@ void ftl::gui::Camera::_draw(std::vector<ftl::rgbd::FrameSet*> &fss) { ...@@ -410,11 +410,6 @@ void ftl::gui::Camera::_draw(std::vector<ftl::rgbd::FrameSet*> &fss) {
// Normalize depth map // Normalize depth map
frame_.get<cv::cuda::GpuMat>(Channel::Depth).convertTo(frame_.get<cv::cuda::GpuMat>(Channel::Depth), CV_32F, 1.0/8.0); frame_.get<cv::cuda::GpuMat>(Channel::Depth).convertTo(frame_.get<cv::cuda::GpuMat>(Channel::Depth), CV_32F, 1.0/8.0);
// Unmap GL buffer from CUDA and finish updating GL texture
texture1_.unmap(renderer_->getCUDAStream());
depth1_.unmap(renderer_->getCUDAStream());
if (isStereo()) texture2_.unmap(renderer2_->getCUDAStream());
width_ = texture1_.width(); width_ = texture1_.width();
height_ = texture1_.height(); height_ = texture1_.height();
...@@ -426,16 +421,26 @@ void ftl::gui::Camera::_draw(std::vector<ftl::rgbd::FrameSet*> &fss) { ...@@ -426,16 +421,26 @@ void ftl::gui::Camera::_draw(std::vector<ftl::rgbd::FrameSet*> &fss) {
fs2.mask = 1; fs2.mask = 1;
//fs2.stale = false; //fs2.stale = false;
fs2.set(ftl::data::FSFlag::STALE); fs2.set(ftl::data::FSFlag::STALE);
frame_.swapTo(Channels<0>(Channel::Colour), f); // Channel::Colour + Channel::Depth if (frame_.hasChannel(Channel::Colour2)) {
if (f.hasChannel(Channel::Colour)) { frame_.swapTo(Channels<0>(Channel::Colour) | Channel::Colour2, f);
f.create<cv::cuda::GpuMat>(Channel::Colour2).create(f.get<cv::cuda::GpuMat>(Channel::Colour).size(), f.get<cv::cuda::GpuMat>(Channel::Colour2).type()); ftl::cuda::flip(f.getTexture<uchar4>(Channel::Colour), 0);
f.swapChannels(Channel::Colour, Channel::Colour2); ftl::cuda::flip(f.getTexture<uchar4>(Channel::Colour2), 0);
cv::cuda::flip(f.get<cv::cuda::GpuMat>(Channel::Colour2), f.get<cv::cuda::GpuMat>(Channel::Colour), 0); } else {
frame_.swapTo(Channels<0>(Channel::Colour), f); // Channel::Colour + Channel::Depth
ftl::cuda::flip(f.getTexture<uchar4>(Channel::Colour), 0);
} }
fs2.timestamp = ftl::timer::get_time(); fs2.timestamp = ftl::timer::get_time();
fs2.id = 0; fs2.id = 0;
record_sender_->post(fs2); record_sender_->post(fs2);
record_stream_->select(0, Channels<0>(Channel::Colour)); record_stream_->select(0, Channels<0>(Channel::Colour));
// Reverse the flip
if (f.hasChannel(Channel::Colour2)) {
ftl::cuda::flip(f.getTexture<uchar4>(Channel::Colour), 0);
ftl::cuda::flip(f.getTexture<uchar4>(Channel::Colour2), 0);
} else {
ftl::cuda::flip(f.getTexture<uchar4>(Channel::Colour), 0);
}
f.swapTo(Channels<0>(Channel::Colour), frame_); f.swapTo(Channels<0>(Channel::Colour), frame_);
} else if (do_snapshot_) { } else if (do_snapshot_) {
do_snapshot_ = false; do_snapshot_ = false;
...@@ -451,6 +456,11 @@ void ftl::gui::Camera::_draw(std::vector<ftl::rgbd::FrameSet*> &fss) { ...@@ -451,6 +456,11 @@ void ftl::gui::Camera::_draw(std::vector<ftl::rgbd::FrameSet*> &fss) {
cv::cvtColor(flipped, flipped, cv::COLOR_BGRA2BGR); cv::cvtColor(flipped, flipped, cv::COLOR_BGRA2BGR);
cv::imwrite(snapshot_filename_, flipped); cv::imwrite(snapshot_filename_, flipped);
} }
// Unmap GL buffer from CUDA and finish updating GL texture
texture1_.unmap(renderer_->getCUDAStream());
depth1_.unmap(renderer_->getCUDAStream());
if (isStereo()) texture2_.unmap(renderer2_->getCUDAStream());
} }
void ftl::gui::Camera::update(int fsid, const ftl::codecs::Channels<0> &c) { void ftl::gui::Camera::update(int fsid, const ftl::codecs::Channels<0> &c) {
...@@ -807,6 +817,7 @@ void ftl::gui::Camera::startVideoRecording(const std::string &filename) { ...@@ -807,6 +817,7 @@ void ftl::gui::Camera::startVideoRecording(const std::string &filename) {
record_sender_ = ftl::create<ftl::stream::Sender>(screen_->root(), "videoEncode"); record_sender_ = ftl::create<ftl::stream::Sender>(screen_->root(), "videoEncode");
record_sender_->setStream(record_stream_); record_sender_->setStream(record_stream_);
record_sender_->value("codec", 2); // Default H264 record_sender_->value("codec", 2); // Default H264
record_sender_->value("stereo", true); // If both channels, then default to stereo
} }
if (record_stream_->active()) return; if (record_stream_->active()) return;
......
...@@ -112,8 +112,10 @@ bool NvPipeEncoder::encode(const cv::cuda::GpuMat &in, ftl::codecs::Packet &pkt) ...@@ -112,8 +112,10 @@ bool NvPipeEncoder::encode(const cv::cuda::GpuMat &in, ftl::codecs::Packet &pkt)
return false; return false;
} }
bool is_stereo = pkt.flags & ftl::codecs::kFlagStereo;
auto [tx,ty] = ftl::codecs::chooseTileConfig(pkt.frame_count); auto [tx,ty] = ftl::codecs::chooseTileConfig(pkt.frame_count);
pkt.definition = (pkt.definition == definition_t::Any) ? ftl::codecs::findDefinition(in.cols/tx, in.rows/ty) : pkt.definition; pkt.definition = (pkt.definition == definition_t::Any) ? ftl::codecs::findDefinition((is_stereo) ? in.cols/tx/2 : in.cols/tx, in.rows/ty) : pkt.definition;
if (pkt.definition == definition_t::Invalid || pkt.definition == definition_t::Any) { if (pkt.definition == definition_t::Invalid || pkt.definition == definition_t::Any) {
LOG(ERROR) << "Could not find appropriate definition"; LOG(ERROR) << "Could not find appropriate definition";
return false; return false;
...@@ -133,7 +135,7 @@ bool NvPipeEncoder::encode(const cv::cuda::GpuMat &in, ftl::codecs::Packet &pkt) ...@@ -133,7 +135,7 @@ bool NvPipeEncoder::encode(const cv::cuda::GpuMat &in, ftl::codecs::Packet &pkt)
return false; return false;
} }
if (tx*width != in.cols || ty*height != in.rows) { if (((is_stereo) ? tx*width*2 : tx*width) != in.cols || ty*height != in.rows) {
// TODO: Resize if lower definition requested... // TODO: Resize if lower definition requested...
LOG(ERROR) << "Input size does not match expected: " << in.cols << " != " << tx*width; LOG(ERROR) << "Input size does not match expected: " << in.cols << " != " << tx*width;
pkt.definition = definition_t::Invalid; pkt.definition = definition_t::Invalid;
......
...@@ -56,9 +56,9 @@ class Sender : public ftl::Configurable { ...@@ -56,9 +56,9 @@ class Sender : public ftl::Configurable {
//ftl::codecs::Encoder *_getEncoder(int fsid, int fid, ftl::codecs::Channel c); //ftl::codecs::Encoder *_getEncoder(int fsid, int fid, ftl::codecs::Channel c);
void _encodeChannel(ftl::rgbd::FrameSet &fs, ftl::codecs::Channel c, bool reset); void _encodeChannel(ftl::rgbd::FrameSet &fs, ftl::codecs::Channel c, bool reset);
int _generateTiles(const ftl::rgbd::FrameSet &fs, int offset, ftl::codecs::Channel c, cv::cuda::Stream &stream, bool); int _generateTiles(const ftl::rgbd::FrameSet &fs, int offset, ftl::codecs::Channel c, cv::cuda::Stream &stream, bool, bool);
EncodingState &_getTile(int fsid, ftl::codecs::Channel c); EncodingState &_getTile(int fsid, ftl::codecs::Channel c);
cv::Rect _generateROI(const ftl::rgbd::FrameSet &fs, ftl::codecs::Channel c, int offset); cv::Rect _generateROI(const ftl::rgbd::FrameSet &fs, ftl::codecs::Channel c, int offset, bool stereo);
float _selectFloatMax(ftl::codecs::Channel c); float _selectFloatMax(ftl::codecs::Channel c);
}; };
......
...@@ -262,6 +262,9 @@ void Sender::_encodeChannel(ftl::rgbd::FrameSet &fs, Channel c, bool reset) { ...@@ -262,6 +262,9 @@ void Sender::_encodeChannel(ftl::rgbd::FrameSet &fs, Channel c, bool reset) {
codec_t codec = static_cast<codec_t>(value("codec", static_cast<int>(codec_t::Any))); codec_t codec = static_cast<codec_t>(value("codec", static_cast<int>(codec_t::Any)));
device_t device = static_cast<device_t>(value("encoder_device", static_cast<int>(device_t::Any))); device_t device = static_cast<device_t>(value("encoder_device", static_cast<int>(device_t::Any)));
// TODO: Support high res
bool is_stereo = value("stereo", false) && c == Channel::Colour && fs.firstFrame().hasChannel(Channel::Colour2);
uint32_t offset = 0; uint32_t offset = 0;
while (offset < fs.frames.size()) { while (offset < fs.frames.size()) {
Channel cc = c; Channel cc = c;
...@@ -303,7 +306,7 @@ void Sender::_encodeChannel(ftl::rgbd::FrameSet &fs, Channel c, bool reset) { ...@@ -303,7 +306,7 @@ void Sender::_encodeChannel(ftl::rgbd::FrameSet &fs, Channel c, bool reset) {
} }
} }
int count = _generateTiles(fs, offset, cc, enc->stream(), lossless); int count = _generateTiles(fs, offset, cc, enc->stream(), lossless, is_stereo);
if (count <= 0) { if (count <= 0) {
LOG(ERROR) << "Could not generate tiles."; LOG(ERROR) << "Could not generate tiles.";
break; break;
...@@ -326,12 +329,13 @@ void Sender::_encodeChannel(ftl::rgbd::FrameSet &fs, Channel c, bool reset) { ...@@ -326,12 +329,13 @@ void Sender::_encodeChannel(ftl::rgbd::FrameSet &fs, Channel c, bool reset) {
if (!lossless && ftl::codecs::isFloatChannel(cc)) pkt.flags = ftl::codecs::kFlagFloat | ftl::codecs::kFlagMappedDepth; if (!lossless && ftl::codecs::isFloatChannel(cc)) pkt.flags = ftl::codecs::kFlagFloat | ftl::codecs::kFlagMappedDepth;
else if (lossless && ftl::codecs::isFloatChannel(cc)) pkt.flags = ftl::codecs::kFlagFloat; else if (lossless && ftl::codecs::isFloatChannel(cc)) pkt.flags = ftl::codecs::kFlagFloat;
else pkt.flags = ftl::codecs::kFlagFlipRGB; else pkt.flags = ftl::codecs::kFlagFlipRGB;
if (is_stereo) pkt.flags |= ftl::codecs::kFlagStereo;
// In the event of partial frames, add a flag to indicate that // In the event of partial frames, add a flag to indicate that
if (static_cast<size_t>(fs.count) < fs.frames.size()) pkt.flags |= ftl::codecs::kFlagPartial; if (static_cast<size_t>(fs.count) < fs.frames.size()) pkt.flags |= ftl::codecs::kFlagPartial;
// Choose correct region of interest into the surface. // Choose correct region of interest into the surface.
cv::Rect roi = _generateROI(fs, cc, offset); cv::Rect roi = _generateROI(fs, cc, offset, is_stereo);
cv::cuda::GpuMat sroi = tile.surface(roi); cv::cuda::GpuMat sroi = tile.surface(roi);
FTL_Profile("Encoder",0.02); FTL_Profile("Encoder",0.02);
...@@ -357,9 +361,10 @@ void Sender::_encodeChannel(ftl::rgbd::FrameSet &fs, Channel c, bool reset) { ...@@ -357,9 +361,10 @@ void Sender::_encodeChannel(ftl::rgbd::FrameSet &fs, Channel c, bool reset) {
} }
} }
cv::Rect Sender::_generateROI(const ftl::rgbd::FrameSet &fs, ftl::codecs::Channel c, int offset) { cv::Rect Sender::_generateROI(const ftl::rgbd::FrameSet &fs, ftl::codecs::Channel c, int offset, bool stereo) {
const ftl::rgbd::Frame &cframe = fs.firstFrame(); const ftl::rgbd::Frame &cframe = fs.firstFrame();
int rwidth = cframe.get<cv::cuda::GpuMat>(c).cols; int rwidth = cframe.get<cv::cuda::GpuMat>(c).cols;
if (stereo) rwidth *= 2;
int rheight = cframe.get<cv::cuda::GpuMat>(c).rows; int rheight = cframe.get<cv::cuda::GpuMat>(c).rows;
auto [tx,ty] = ftl::codecs::chooseTileConfig(fs.frames.size()-offset); auto [tx,ty] = ftl::codecs::chooseTileConfig(fs.frames.size()-offset);
return cv::Rect(0, 0, tx*rwidth, ty*rheight); return cv::Rect(0, 0, tx*rwidth, ty*rheight);
...@@ -393,7 +398,7 @@ float Sender::_selectFloatMax(Channel c) { ...@@ -393,7 +398,7 @@ float Sender::_selectFloatMax(Channel c) {
} }
} }
int Sender::_generateTiles(const ftl::rgbd::FrameSet &fs, int offset, Channel c, cv::cuda::Stream &stream, bool lossless) { int Sender::_generateTiles(const ftl::rgbd::FrameSet &fs, int offset, Channel c, cv::cuda::Stream &stream, bool lossless, bool stereo) {
auto &surface = _getTile(fs.id, c); auto &surface = _getTile(fs.id, c);
const ftl::rgbd::Frame *cframe = nullptr; //&fs.frames[offset]; const ftl::rgbd::Frame *cframe = nullptr; //&fs.frames[offset];
...@@ -402,7 +407,7 @@ int Sender::_generateTiles(const ftl::rgbd::FrameSet &fs, int offset, Channel c, ...@@ -402,7 +407,7 @@ int Sender::_generateTiles(const ftl::rgbd::FrameSet &fs, int offset, Channel c,
// Choose tile configuration and allocate memory // Choose tile configuration and allocate memory
auto [tx,ty] = ftl::codecs::chooseTileConfig(fs.frames.size()); auto [tx,ty] = ftl::codecs::chooseTileConfig(fs.frames.size());
int rwidth = m.cols; int rwidth = (stereo) ? m.cols*2 : m.cols;
int rheight = m.rows; int rheight = m.rows;
int width = tx * rwidth; int width = tx * rwidth;
int height = ty * rheight; int height = ty * rheight;
...@@ -423,7 +428,7 @@ int Sender::_generateTiles(const ftl::rgbd::FrameSet &fs, int offset, Channel c, ...@@ -423,7 +428,7 @@ int Sender::_generateTiles(const ftl::rgbd::FrameSet &fs, int offset, Channel c,
if (fs.hasFrame(offset+count)) { if (fs.hasFrame(offset+count)) {
cframe = &fs.frames[offset+count]; cframe = &fs.frames[offset+count];
auto &m = cframe->get<cv::cuda::GpuMat>(c); auto &m = cframe->get<cv::cuda::GpuMat>(c);
cv::Rect roi((count % tx)*rwidth, (count / tx)*rheight, rwidth, rheight); cv::Rect roi((count % tx)*rwidth, (count / tx)*rheight, (stereo) ? rwidth/2 : rwidth, rheight);
cv::cuda::GpuMat sroi = surface.surface(roi); cv::cuda::GpuMat sroi = surface.surface(roi);
if (m.type() == CV_32F) { if (m.type() == CV_32F) {
...@@ -442,6 +447,30 @@ int Sender::_generateTiles(const ftl::rgbd::FrameSet &fs, int offset, Channel c, ...@@ -442,6 +447,30 @@ int Sender::_generateTiles(const ftl::rgbd::FrameSet &fs, int offset, Channel c,
LOG(ERROR) << "Unsupported colour format: " << m.type(); LOG(ERROR) << "Unsupported colour format: " << m.type();
return 0; return 0;
} }
// Do the right channel
if (stereo) {
auto &m = cframe->get<cv::cuda::GpuMat>((c == Channel::Colour) ? Channel::Colour2 : Channel::Colour2HighRes);
cv::Rect roi((count % tx)*rwidth + (rwidth/2), (count / tx)*rheight, rwidth/2, rheight);
cv::cuda::GpuMat sroi = surface.surface(roi);
if (m.type() == CV_32F) {
if (lossless) {
m.convertTo(sroi, CV_16UC1, 1000, stream);
} else {
ftl::cuda::depth_to_vuya(m, sroi, _selectFloatMax(c), stream);
}
} else if (m.type() == CV_8UC4) {
cv::cuda::cvtColor(m, sroi, cv::COLOR_BGRA2RGBA, 0, stream);
} else if (m.type() == CV_8UC3) {
cv::cuda::cvtColor(m, sroi, cv::COLOR_BGR2RGBA, 0, stream);
} else if (m.type() == CV_8UC1) {
m.copyTo(sroi, stream);
} else {
LOG(ERROR) << "Unsupported colour format: " << m.type();
return 0;
}
}
} else { } else {
cv::Rect roi((count % tx)*rwidth, (count / tx)*rheight, rwidth, rheight); cv::Rect roi((count % tx)*rwidth, (count / tx)*rheight, rwidth, rheight);
cv::cuda::GpuMat sroi = surface.surface(roi); cv::cuda::GpuMat sroi = surface.surface(roi);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment