Merge remote-tracking branch 'upstream/3.4' into merge-3.4
This commit is contained in:
+400
-114
@@ -8,144 +8,430 @@
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
string getGstDemuxPlugin(string container);
|
||||
string getGstAvDecodePlugin(string codec);
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
class GStreamerPipeline
|
||||
{
|
||||
const string keys =
|
||||
"{h help usage ? | | print help messages }"
|
||||
"{p pipeline |gst-default| pipeline name (supported: 'gst-default', 'gst-vaapi', 'gst-libav', 'ffmpeg') }"
|
||||
"{ct container |mp4 | container name (supported: 'mp4', 'mov', 'avi', 'mkv') }"
|
||||
"{cd codec |h264 | codec name (supported: 'h264', 'h265', 'mpeg2', 'mpeg4', 'mjpeg', 'vp8') }"
|
||||
"{f file path | | path to file }"
|
||||
"{fm fast | | fast measure fps }";
|
||||
|
||||
CommandLineParser parser(argc, argv, keys);
|
||||
|
||||
parser.about("This program shows how to read a video file with GStreamer pipeline with OpenCV.");
|
||||
|
||||
if (parser.has("help"))
|
||||
public:
|
||||
// Preprocessing arguments command line
|
||||
GStreamerPipeline(int argc, char *argv[])
|
||||
{
|
||||
parser.printMessage();
|
||||
return 0;
|
||||
const string keys =
|
||||
"{h help usage ? | | print help messages }"
|
||||
"{m mode | | coding mode (supported: encode, decode) }"
|
||||
"{p pipeline |default | pipeline name (supported: 'default', 'gst-basic', 'gst-vaapi', 'gst-libav', 'ffmpeg') }"
|
||||
"{ct container |mp4 | container name (supported: 'mp4', 'mov', 'avi', 'mkv') }"
|
||||
"{cd codec |h264 | codec name (supported: 'h264', 'h265', 'mpeg2', 'mpeg4', 'mjpeg', 'vp8') }"
|
||||
"{f file path | | path to file }"
|
||||
"{vr resolution |720p | video resolution for encoding (supported: '720p', '1080p', '4k') }"
|
||||
"{fps |30 | fix frame per second for encoding (supported: fps > 0) }"
|
||||
"{fm fast | | fast measure fps }";
|
||||
cmd_parser = new CommandLineParser(argc, argv, keys);
|
||||
cmd_parser->about("This program shows how to read a video file with GStreamer pipeline with OpenCV.");
|
||||
|
||||
if (cmd_parser->has("help"))
|
||||
{
|
||||
cmd_parser->printMessage();
|
||||
exit_code = -1;
|
||||
}
|
||||
|
||||
fast_measure = cmd_parser->has("fast"); // fast measure fps
|
||||
fix_fps = cmd_parser->get<int>("fps"); // fixed frame per second
|
||||
pipeline = cmd_parser->get<string>("pipeline"), // gstreamer pipeline type
|
||||
container = cmd_parser->get<string>("container"), // container type
|
||||
mode = cmd_parser->get<string>("mode"), // coding mode
|
||||
codec = cmd_parser->get<string>("codec"), // codec type
|
||||
file_name = cmd_parser->get<string>("file"), // path to videofile
|
||||
resolution = cmd_parser->get<string>("resolution"); // video resolution
|
||||
|
||||
if (!cmd_parser->check())
|
||||
{
|
||||
cmd_parser->printErrors();
|
||||
exit_code = -1;
|
||||
}
|
||||
exit_code = 0;
|
||||
}
|
||||
|
||||
bool arg_fast_measure = parser.has("fast"); // fast measure fps
|
||||
string arg_pipeline = parser.get<string>("pipeline"), // GStreamer pipeline type
|
||||
arg_container = parser.get<string>("container"), // container type
|
||||
arg_codec = parser.get<string>("codec"), // codec type
|
||||
arg_file_name = parser.get<string>("file"); // path to videofile
|
||||
VideoCapture cap;
|
||||
~GStreamerPipeline() { delete cmd_parser; }
|
||||
|
||||
if (!parser.check())
|
||||
// Start pipeline
|
||||
int run()
|
||||
{
|
||||
parser.printErrors();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Choose the constructed GStreamer pipeline
|
||||
if (arg_pipeline.find("gst") == 0)
|
||||
{
|
||||
ostringstream pipeline;
|
||||
pipeline << "filesrc location=\"" << arg_file_name << "\"";
|
||||
pipeline << " ! " << getGstDemuxPlugin(arg_container);
|
||||
|
||||
if (arg_pipeline.find("default") == 4) {
|
||||
pipeline << " ! decodebin";
|
||||
}
|
||||
else if (arg_pipeline.find("vaapi1710") == 4)
|
||||
if (exit_code < 0) { return exit_code; }
|
||||
if (mode == "decode") { if (createDecodePipeline() < 0) return -1; }
|
||||
else if (mode == "encode") { if (createEncodePipeline() < 0) return -1; }
|
||||
else
|
||||
{
|
||||
pipeline << " ! vaapidecodebin";
|
||||
if (arg_container == "mkv")
|
||||
{
|
||||
pipeline << " ! autovideoconvert";
|
||||
}
|
||||
else
|
||||
{
|
||||
pipeline << " ! video/x-raw, format=YV12";
|
||||
}
|
||||
cout << "Unsupported mode: " << mode << endl;
|
||||
cmd_parser->printErrors();
|
||||
return -1;
|
||||
}
|
||||
else if (arg_pipeline.find("libav") == 4)
|
||||
cout << "_____________________________________" << endl;
|
||||
cout << "Pipeline " << mode << ":" << endl;
|
||||
cout << stream_pipeline.str() << endl;
|
||||
// Choose a show video or only measure fps
|
||||
cout << "_____________________________________" << endl;
|
||||
cout << "Start measure frame per seconds (fps)" << endl;
|
||||
cout << "Loading ..." << endl;
|
||||
|
||||
vector<double> tick_counts;
|
||||
|
||||
cout << "Start " << mode << ": " << file_name;
|
||||
cout << " (" << pipeline << ")" << endl;
|
||||
|
||||
while(true)
|
||||
{
|
||||
pipeline << " ! " << getGstAvDecodePlugin(arg_codec);
|
||||
int64 temp_count_tick = 0;
|
||||
if (mode == "decode")
|
||||
{
|
||||
Mat frame;
|
||||
temp_count_tick = getTickCount();
|
||||
cap >> frame;
|
||||
temp_count_tick = getTickCount() - temp_count_tick;
|
||||
if (frame.empty()) { break; }
|
||||
}
|
||||
else if (mode == "encode")
|
||||
{
|
||||
Mat element;
|
||||
while(!cap.grab());
|
||||
cap.retrieve(element);
|
||||
temp_count_tick = getTickCount();
|
||||
wrt << element;
|
||||
temp_count_tick = getTickCount() - temp_count_tick;
|
||||
}
|
||||
|
||||
tick_counts.push_back(static_cast<double>(temp_count_tick));
|
||||
if (((mode == "decode") && fast_measure && (tick_counts.size() > 1e3)) ||
|
||||
((mode == "encode") && (tick_counts.size() > 3e3)) ||
|
||||
((mode == "encode") && fast_measure && (tick_counts.size() > 1e2)))
|
||||
{ break; }
|
||||
|
||||
}
|
||||
double time_fps = sum(tick_counts)[0] / getTickFrequency();
|
||||
|
||||
if (tick_counts.size() != 0)
|
||||
{
|
||||
cout << "Finished: " << tick_counts.size() << " in " << time_fps <<" sec ~ " ;
|
||||
cout << tick_counts.size() / time_fps <<" fps " << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
parser.printMessage();
|
||||
cout << "Unsupported pipeline: " << arg_pipeline << endl;
|
||||
return -4;
|
||||
cout << "Failed " << mode << ": " << file_name;
|
||||
cout << " (" << pipeline << ")" << endl;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Free video resource
|
||||
void close()
|
||||
{
|
||||
cap.release();
|
||||
wrt.release();
|
||||
}
|
||||
|
||||
private:
|
||||
// Choose the constructed GStreamer pipeline for decode
|
||||
int createDecodePipeline()
|
||||
{
|
||||
if (pipeline == "default") {
|
||||
cap = VideoCapture(file_name, CAP_GSTREAMER);
|
||||
}
|
||||
else if (pipeline.find("gst") == 0)
|
||||
{
|
||||
stream_pipeline << "filesrc location=\"" << file_name << "\"";
|
||||
stream_pipeline << " ! " << getGstMuxPlugin();
|
||||
|
||||
if (pipeline.find("basic") == 4)
|
||||
{
|
||||
stream_pipeline << getGstDefaultCodePlugin();
|
||||
}
|
||||
else if (pipeline.find("vaapi1710") == 4)
|
||||
{
|
||||
stream_pipeline << getGstVaapiCodePlugin();
|
||||
}
|
||||
else if (pipeline.find("libav") == 4)
|
||||
{
|
||||
stream_pipeline << getGstAvCodePlugin();
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "Unsupported pipeline: " << pipeline << endl;
|
||||
cmd_parser->printErrors();
|
||||
return -1;
|
||||
}
|
||||
|
||||
stream_pipeline << " ! videoconvert n-threads=" << getNumThreads();
|
||||
stream_pipeline << " ! appsink sync=false";
|
||||
cap = VideoCapture(stream_pipeline.str(), CAP_GSTREAMER);
|
||||
}
|
||||
else if (pipeline == "ffmpeg")
|
||||
{
|
||||
cap = VideoCapture(file_name, CAP_FFMPEG);
|
||||
stream_pipeline << "default pipeline for ffmpeg" << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "Unsupported pipeline: " << pipeline << endl;
|
||||
cmd_parser->printErrors();
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Choose the constructed GStreamer pipeline for encode
|
||||
int createEncodePipeline()
|
||||
{
|
||||
if (checkConfiguration() < 0) return -1;
|
||||
ostringstream test_pipeline;
|
||||
test_pipeline << "videotestsrc pattern=smpte";
|
||||
test_pipeline << " ! video/x-raw, " << getVideoSettings();
|
||||
test_pipeline << " ! appsink sync=false";
|
||||
cap = VideoCapture(test_pipeline.str(), CAP_GSTREAMER);
|
||||
|
||||
if (pipeline == "default") {
|
||||
wrt = VideoWriter(file_name, CAP_GSTREAMER, getFourccCode(), fix_fps, fix_size, true);
|
||||
}
|
||||
else if (pipeline.find("gst") == 0)
|
||||
{
|
||||
stream_pipeline << "appsrc ! videoconvert n-threads=" << getNumThreads() << " ! ";
|
||||
|
||||
if (pipeline.find("basic") == 4)
|
||||
{
|
||||
stream_pipeline << getGstDefaultCodePlugin();
|
||||
}
|
||||
else if (pipeline.find("vaapi1710") == 4)
|
||||
{
|
||||
stream_pipeline << getGstVaapiCodePlugin();
|
||||
}
|
||||
else if (pipeline.find("libav") == 4)
|
||||
{
|
||||
stream_pipeline << getGstAvCodePlugin();
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "Unsupported pipeline: " << pipeline << endl;
|
||||
cmd_parser->printErrors();
|
||||
return -1;
|
||||
}
|
||||
|
||||
stream_pipeline << " ! " << getGstMuxPlugin();
|
||||
stream_pipeline << " ! filesink location=\"" << file_name << "\"";
|
||||
wrt = VideoWriter(stream_pipeline.str(), CAP_GSTREAMER, 0, fix_fps, fix_size, true);
|
||||
}
|
||||
else if (pipeline == "ffmpeg")
|
||||
{
|
||||
wrt = VideoWriter(file_name, CAP_FFMPEG, getFourccCode(), fix_fps, fix_size, true);
|
||||
stream_pipeline << "default pipeline for ffmpeg" << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "Unsupported pipeline: " << pipeline << endl;
|
||||
cmd_parser->printErrors();
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Choose video resolution for encoding
|
||||
string getVideoSettings()
|
||||
{
|
||||
ostringstream video_size;
|
||||
if (fix_fps > 0) { video_size << "framerate=" << fix_fps << "/1, "; }
|
||||
else
|
||||
{
|
||||
cout << "Unsupported fps (< 0): " << fix_fps << endl;
|
||||
cmd_parser->printErrors();
|
||||
return string();
|
||||
}
|
||||
|
||||
pipeline << " ! videoconvert";
|
||||
pipeline << " n-threads=" << getNumThreads();
|
||||
pipeline << " ! appsink sync=false";
|
||||
cap = VideoCapture(pipeline.str(), CAP_GSTREAMER);
|
||||
if (resolution == "720p") { fix_size = Size(1280, 720); }
|
||||
else if (resolution == "1080p") { fix_size = Size(1920, 1080); }
|
||||
else if (resolution == "4k") { fix_size = Size(3840, 2160); }
|
||||
else
|
||||
{
|
||||
cout << "Unsupported video resolution: " << resolution << endl;
|
||||
cmd_parser->printErrors();
|
||||
return string();
|
||||
}
|
||||
|
||||
video_size << "width=" << fix_size.width << ", height=" << fix_size.height;
|
||||
return video_size.str();
|
||||
}
|
||||
else if (arg_pipeline == "ffmpeg")
|
||||
|
||||
// Choose a video container
|
||||
string getGstMuxPlugin()
|
||||
{
|
||||
cap = VideoCapture(arg_file_name, CAP_FFMPEG);
|
||||
ostringstream plugin;
|
||||
if (container == "avi") { plugin << "avi"; }
|
||||
else if (container == "mp4") { plugin << "qt"; }
|
||||
else if (container == "mov") { plugin << "qt"; }
|
||||
else if (container == "mkv") { plugin << "matroska"; }
|
||||
else
|
||||
{
|
||||
cout << "Unsupported container: " << container << endl;
|
||||
cmd_parser->printErrors();
|
||||
return string();
|
||||
}
|
||||
|
||||
if (mode == "decode") { plugin << "demux"; }
|
||||
else if (mode == "encode") { plugin << "mux"; }
|
||||
else
|
||||
{
|
||||
cout << "Unsupported mode: " << mode << endl;
|
||||
cmd_parser->printErrors();
|
||||
return string();
|
||||
}
|
||||
|
||||
return plugin.str();
|
||||
}
|
||||
else
|
||||
|
||||
// Choose a libav codec
|
||||
string getGstAvCodePlugin()
|
||||
{
|
||||
parser.printMessage();
|
||||
cout << "Unsupported pipeline: " << arg_pipeline << endl;
|
||||
return -4;
|
||||
ostringstream plugin;
|
||||
if (mode == "decode")
|
||||
{
|
||||
if (codec == "h264") { plugin << "h264parse ! "; }
|
||||
else if (codec == "h265") { plugin << "h265parse ! "; }
|
||||
plugin << "avdec_";
|
||||
}
|
||||
else if (mode == "encode") { plugin << "avenc_"; }
|
||||
else
|
||||
{
|
||||
cout << "Unsupported mode: " << mode << endl;
|
||||
cmd_parser->printErrors();
|
||||
return string();
|
||||
}
|
||||
|
||||
if (codec == "h264") { plugin << "h264"; }
|
||||
else if (codec == "h265") { plugin << "h265"; }
|
||||
else if (codec == "mpeg2") { plugin << "mpeg2video"; }
|
||||
else if (codec == "mpeg4") { plugin << "mpeg4"; }
|
||||
else if (codec == "mjpeg") { plugin << "mjpeg"; }
|
||||
else if (codec == "vp8") { plugin << "vp8"; }
|
||||
else
|
||||
{
|
||||
cout << "Unsupported libav codec: " << codec << endl;
|
||||
cmd_parser->printErrors();
|
||||
return string();
|
||||
}
|
||||
|
||||
return plugin.str();
|
||||
}
|
||||
|
||||
// Choose a show video or only measure fps
|
||||
cout << "_____________________________________" << '\n';
|
||||
cout << "Start measure frame per seconds (fps)" << '\n';
|
||||
cout << "Loading ..." << '\n';
|
||||
|
||||
Mat frame;
|
||||
vector<double> tick_counts;
|
||||
|
||||
cout << "Start decoding: " << arg_file_name;
|
||||
cout << " (" << arg_pipeline << ")" << endl;
|
||||
|
||||
while(true)
|
||||
// Choose a vaapi codec
|
||||
string getGstVaapiCodePlugin()
|
||||
{
|
||||
int64 temp_count_tick = getTickCount();
|
||||
cap >> frame;
|
||||
temp_count_tick = getTickCount() - temp_count_tick;
|
||||
if (frame.empty()) { break; }
|
||||
tick_counts.push_back(static_cast<double>(temp_count_tick));
|
||||
if (arg_fast_measure && (tick_counts.size() > 1000)) { break; }
|
||||
ostringstream plugin;
|
||||
if (mode == "decode")
|
||||
{
|
||||
plugin << "vaapidecodebin";
|
||||
if (container == "mkv") { plugin << " ! autovideoconvert"; }
|
||||
else { plugin << " ! video/x-raw, format=YV12"; }
|
||||
}
|
||||
else if (mode == "encode")
|
||||
{
|
||||
if (codec == "h264") { plugin << "vaapih264enc"; }
|
||||
else if (codec == "h265") { plugin << "vaapih265enc"; }
|
||||
else if (codec == "mpeg2") { plugin << "vaapimpeg2enc"; }
|
||||
else if (codec == "mjpeg") { plugin << "vaapijpegenc"; }
|
||||
else if (codec == "vp8") { plugin << "vaapivp8enc"; }
|
||||
else
|
||||
{
|
||||
cout << "Unsupported vaapi codec: " << codec << endl;
|
||||
cmd_parser->printErrors();
|
||||
return string();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "Unsupported mode: " << resolution << endl;
|
||||
cmd_parser->printErrors();
|
||||
return string();
|
||||
}
|
||||
return plugin.str();
|
||||
}
|
||||
|
||||
// Choose a default codec
|
||||
string getGstDefaultCodePlugin()
|
||||
{
|
||||
ostringstream plugin;
|
||||
if (mode == "decode")
|
||||
{
|
||||
plugin << " ! decodebin";
|
||||
}
|
||||
else if (mode == "encode")
|
||||
{
|
||||
if (codec == "h264") { plugin << "x264enc"; }
|
||||
else if (codec == "h265") { plugin << "x265enc"; }
|
||||
else if (codec == "mpeg2") { plugin << "mpeg2enc"; }
|
||||
else if (codec == "mjpeg") { plugin << "jpegenc"; }
|
||||
else if (codec == "vp8") { plugin << "vp8enc"; }
|
||||
else
|
||||
{
|
||||
cout << "Unsupported default codec: " << codec << endl;
|
||||
cmd_parser->printErrors();
|
||||
return string();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "Unsupported mode: " << resolution << endl;
|
||||
cmd_parser->printErrors();
|
||||
return string();
|
||||
}
|
||||
return plugin.str();
|
||||
}
|
||||
// Get fourcc for codec
|
||||
int getFourccCode()
|
||||
{
|
||||
if (codec == "h264") { return VideoWriter::fourcc('H','2','6','4'); }
|
||||
else if (codec == "h265") { return VideoWriter::fourcc('H','E','V','C'); }
|
||||
else if (codec == "mpeg2") { return VideoWriter::fourcc('M','P','E','G'); }
|
||||
else if (codec == "mpeg4") { return VideoWriter::fourcc('M','P','4','2'); }
|
||||
else if (codec == "mjpeg") { return VideoWriter::fourcc('M','J','P','G'); }
|
||||
else if (codec == "vp8") { return VideoWriter::fourcc('V','P','8','0'); }
|
||||
else
|
||||
{
|
||||
cout << "Unsupported ffmpeg codec: " << codec << endl;
|
||||
cmd_parser->printErrors();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
double time_fps = sum(tick_counts)[0] / getTickFrequency();
|
||||
|
||||
if (tick_counts.size() != 0)
|
||||
// Check bad configuration
|
||||
int checkConfiguration()
|
||||
{
|
||||
cout << "Finished: " << tick_counts.size() << " in " << time_fps <<" sec ~ " ;
|
||||
cout << tick_counts.size() / time_fps <<" fps " << endl;
|
||||
if ((codec == "mpeg2" && getGstMuxPlugin() == "qtmux") ||
|
||||
(codec == "h265" && getGstMuxPlugin() == "avimux") ||
|
||||
(pipeline == "gst-libav" && (codec == "h264" || codec == "h265")) ||
|
||||
(pipeline == "gst-vaapi1710" && codec=="mpeg2" && resolution=="4k") ||
|
||||
(pipeline == "gst-vaapi1710" && codec=="mpeg2" && resolution=="1080p" && fix_fps > 30))
|
||||
{
|
||||
cout << "Unsupported configuration" << endl;
|
||||
cmd_parser->printErrors();
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "Failed decoding: " << arg_file_name;
|
||||
cout << " (" << arg_pipeline << ")" << endl;
|
||||
return -5;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Choose a video container
|
||||
string getGstDemuxPlugin(string container) {
|
||||
if (container == "avi") { return "avidemux"; }
|
||||
else if (container == "mp4") { return "qtdemux"; }
|
||||
else if (container == "mov") { return "qtdemux"; }
|
||||
else if (container == "mkv") { return "matroskademux"; }
|
||||
return string();
|
||||
}
|
||||
|
||||
// Choose a codec
|
||||
string getGstAvDecodePlugin(string codec) {
|
||||
if (codec == "h264") { return "h264parse ! avdec_h264"; }
|
||||
else if (codec == "h265") { return "h265parse ! avdec_h265"; }
|
||||
else if (codec == "mpeg2") { return "avdec_mpeg2video"; }
|
||||
else if (codec == "mpeg4") { return "avdec_mpeg4"; }
|
||||
else if (codec == "mjpeg") { return "avdec_mjpeg"; }
|
||||
else if (codec == "vp8") { return "avdec_vp8"; }
|
||||
return string();
|
||||
|
||||
bool fast_measure; // fast measure fps
|
||||
string pipeline, // gstreamer pipeline type
|
||||
container, // container type
|
||||
mode, // coding mode
|
||||
codec, // codec type
|
||||
file_name, // path to videofile
|
||||
resolution; // video resolution
|
||||
int fix_fps; // fixed frame per second
|
||||
Size fix_size; // fixed frame size
|
||||
int exit_code;
|
||||
VideoWriter wrt;
|
||||
VideoCapture cap;
|
||||
ostringstream stream_pipeline;
|
||||
CommandLineParser* cmd_parser;
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
GStreamerPipeline pipe(argc, argv);
|
||||
return pipe.run();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
#include <opencv2/dnn.hpp>
|
||||
|
||||
//! [A custom layer interface]
|
||||
class MyLayer : public cv::dnn::Layer
|
||||
{
|
||||
public:
|
||||
//! [MyLayer::MyLayer]
|
||||
MyLayer(const cv::dnn::LayerParams ¶ms);
|
||||
//! [MyLayer::MyLayer]
|
||||
|
||||
//! [MyLayer::create]
|
||||
static cv::Ptr<cv::dnn::Layer> create(cv::dnn::LayerParams& params);
|
||||
//! [MyLayer::create]
|
||||
|
||||
//! [MyLayer::getMemoryShapes]
|
||||
virtual bool getMemoryShapes(const std::vector<std::vector<int> > &inputs,
|
||||
const int requiredOutputs,
|
||||
std::vector<std::vector<int> > &outputs,
|
||||
std::vector<std::vector<int> > &internals) const;
|
||||
//! [MyLayer::getMemoryShapes]
|
||||
|
||||
//! [MyLayer::forward]
|
||||
virtual void forward(std::vector<cv::Mat*> &inputs, std::vector<cv::Mat> &outputs, std::vector<cv::Mat> &internals);
|
||||
//! [MyLayer::forward]
|
||||
|
||||
//! [MyLayer::finalize]
|
||||
virtual void finalize(const std::vector<cv::Mat*> &inputs, std::vector<cv::Mat> &outputs);
|
||||
//! [MyLayer::finalize]
|
||||
|
||||
virtual void forward(cv::InputArrayOfArrays inputs, cv::OutputArrayOfArrays outputs, cv::OutputArrayOfArrays internals);
|
||||
};
|
||||
//! [A custom layer interface]
|
||||
|
||||
//! [InterpLayer]
|
||||
class InterpLayer : public cv::dnn::Layer
|
||||
{
|
||||
public:
|
||||
InterpLayer(const cv::dnn::LayerParams ¶ms) : Layer(params)
|
||||
{
|
||||
outWidth = params.get<int>("width", 0);
|
||||
outHeight = params.get<int>("height", 0);
|
||||
}
|
||||
|
||||
static cv::Ptr<cv::dnn::Layer> create(cv::dnn::LayerParams& params)
|
||||
{
|
||||
return cv::Ptr<cv::dnn::Layer>(new InterpLayer(params));
|
||||
}
|
||||
|
||||
virtual bool getMemoryShapes(const std::vector<std::vector<int> > &inputs,
|
||||
const int requiredOutputs,
|
||||
std::vector<std::vector<int> > &outputs,
|
||||
std::vector<std::vector<int> > &internals) const
|
||||
{
|
||||
CV_UNUSED(requiredOutputs); CV_UNUSED(internals);
|
||||
std::vector<int> outShape(4);
|
||||
outShape[0] = inputs[0][0]; // batch size
|
||||
outShape[1] = inputs[0][1]; // number of channels
|
||||
outShape[2] = outHeight;
|
||||
outShape[3] = outWidth;
|
||||
outputs.assign(1, outShape);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Implementation of this custom layer is based on https://github.com/cdmh/deeplab-public/blob/master/src/caffe/layers/interp_layer.cpp
|
||||
virtual void forward(std::vector<cv::Mat*> &inputs, std::vector<cv::Mat> &outputs, std::vector<cv::Mat> &internals)
|
||||
{
|
||||
CV_UNUSED(internals);
|
||||
cv::Mat& inp = *inputs[0];
|
||||
cv::Mat& out = outputs[0];
|
||||
const float* inpData = (float*)inp.data;
|
||||
float* outData = (float*)out.data;
|
||||
|
||||
const int batchSize = inp.size[0];
|
||||
const int numChannels = inp.size[1];
|
||||
const int inpHeight = inp.size[2];
|
||||
const int inpWidth = inp.size[3];
|
||||
|
||||
const float rheight = (outHeight > 1) ? static_cast<float>(inpHeight - 1) / (outHeight - 1) : 0.f;
|
||||
const float rwidth = (outWidth > 1) ? static_cast<float>(inpWidth - 1) / (outWidth - 1) : 0.f;
|
||||
for (int h2 = 0; h2 < outHeight; ++h2)
|
||||
{
|
||||
const float h1r = rheight * h2;
|
||||
const int h1 = static_cast<int>(h1r);
|
||||
const int h1p = (h1 < inpHeight - 1) ? 1 : 0;
|
||||
const float h1lambda = h1r - h1;
|
||||
const float h0lambda = 1.f - h1lambda;
|
||||
for (int w2 = 0; w2 < outWidth; ++w2)
|
||||
{
|
||||
const float w1r = rwidth * w2;
|
||||
const int w1 = static_cast<int>(w1r);
|
||||
const int w1p = (w1 < inpWidth - 1) ? 1 : 0;
|
||||
const float w1lambda = w1r - w1;
|
||||
const float w0lambda = 1.f - w1lambda;
|
||||
const float* pos1 = inpData + h1 * inpWidth + w1;
|
||||
float* pos2 = outData + h2 * outWidth + w2;
|
||||
for (int c = 0; c < batchSize * numChannels; ++c)
|
||||
{
|
||||
pos2[0] =
|
||||
h0lambda * (w0lambda * pos1[0] + w1lambda * pos1[w1p]) +
|
||||
h1lambda * (w0lambda * pos1[h1p * inpWidth] + w1lambda * pos1[h1p * inpWidth + w1p]);
|
||||
pos1 += inpWidth * inpHeight;
|
||||
pos2 += outWidth * outHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void forward(cv::InputArrayOfArrays, cv::OutputArrayOfArrays, cv::OutputArrayOfArrays) {}
|
||||
|
||||
private:
|
||||
int outWidth, outHeight;
|
||||
};
|
||||
//! [InterpLayer]
|
||||
|
||||
//! [ResizeBilinearLayer]
|
||||
class ResizeBilinearLayer : public cv::dnn::Layer
|
||||
{
|
||||
public:
|
||||
ResizeBilinearLayer(const cv::dnn::LayerParams ¶ms) : Layer(params)
|
||||
{
|
||||
CV_Assert(!params.get<bool>("align_corners", false));
|
||||
CV_Assert(blobs.size() == 1, blobs[0].type() == CV_32SC1);
|
||||
outHeight = blobs[0].at<int>(0, 0);
|
||||
outWidth = blobs[0].at<int>(0, 1);
|
||||
}
|
||||
|
||||
static cv::Ptr<cv::dnn::Layer> create(cv::dnn::LayerParams& params)
|
||||
{
|
||||
return cv::Ptr<cv::dnn::Layer>(new ResizeBilinearLayer(params));
|
||||
}
|
||||
|
||||
virtual bool getMemoryShapes(const std::vector<std::vector<int> > &inputs,
|
||||
const int requiredOutputs,
|
||||
std::vector<std::vector<int> > &outputs,
|
||||
std::vector<std::vector<int> > &internals) const
|
||||
{
|
||||
CV_UNUSED(requiredOutputs); CV_UNUSED(internals);
|
||||
std::vector<int> outShape(4);
|
||||
outShape[0] = inputs[0][0]; // batch size
|
||||
outShape[1] = inputs[0][1]; // number of channels
|
||||
outShape[2] = outHeight;
|
||||
outShape[3] = outWidth;
|
||||
outputs.assign(1, outShape);
|
||||
return false;
|
||||
}
|
||||
|
||||
// This implementation is based on a reference implementation from
|
||||
// https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/lite/kernels/internal/reference/reference_ops.h
|
||||
virtual void forward(std::vector<cv::Mat*> &inputs, std::vector<cv::Mat> &outputs, std::vector<cv::Mat> &internals)
|
||||
{
|
||||
CV_UNUSED(internals);
|
||||
cv::Mat& inp = *inputs[0];
|
||||
cv::Mat& out = outputs[0];
|
||||
const float* inpData = (float*)inp.data;
|
||||
float* outData = (float*)out.data;
|
||||
|
||||
const int batchSize = inp.size[0];
|
||||
const int numChannels = inp.size[1];
|
||||
const int inpHeight = inp.size[2];
|
||||
const int inpWidth = inp.size[3];
|
||||
|
||||
float heightScale = static_cast<float>(inpHeight) / outHeight;
|
||||
float widthScale = static_cast<float>(inpWidth) / outWidth;
|
||||
for (int b = 0; b < batchSize; ++b)
|
||||
{
|
||||
for (int y = 0; y < outHeight; ++y)
|
||||
{
|
||||
float input_y = y * heightScale;
|
||||
int y0 = static_cast<int>(std::floor(input_y));
|
||||
int y1 = std::min(y0 + 1, inpHeight - 1);
|
||||
for (int x = 0; x < outWidth; ++x)
|
||||
{
|
||||
float input_x = x * widthScale;
|
||||
int x0 = static_cast<int>(std::floor(input_x));
|
||||
int x1 = std::min(x0 + 1, inpWidth - 1);
|
||||
for (int c = 0; c < numChannels; ++c)
|
||||
{
|
||||
float interpolation =
|
||||
inpData[offset(inp.size, c, x0, y0, b)] * (1 - (input_y - y0)) * (1 - (input_x - x0)) +
|
||||
inpData[offset(inp.size, c, x0, y1, b)] * (input_y - y0) * (1 - (input_x - x0)) +
|
||||
inpData[offset(inp.size, c, x1, y0, b)] * (1 - (input_y - y0)) * (input_x - x0) +
|
||||
inpData[offset(inp.size, c, x1, y1, b)] * (input_y - y0) * (input_x - x0);
|
||||
outData[offset(out.size, c, x, y, b)] = interpolation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void forward(cv::InputArrayOfArrays, cv::OutputArrayOfArrays, cv::OutputArrayOfArrays) {}
|
||||
|
||||
private:
|
||||
static inline int offset(const cv::MatSize& size, int c, int x, int y, int b)
|
||||
{
|
||||
return x + size[3] * (y + size[2] * (c + size[1] * b));
|
||||
}
|
||||
|
||||
int outWidth, outHeight;
|
||||
};
|
||||
//! [ResizeBilinearLayer]
|
||||
|
||||
//! [Register a custom layer]
|
||||
#include <opencv2/dnn/layer.details.hpp> // CV_DNN_REGISTER_LAYER_CLASS macro
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
CV_DNN_REGISTER_LAYER_CLASS(MyType, MyLayer);
|
||||
// ...
|
||||
//! [Register a custom layer]
|
||||
CV_UNUSED(argc); CV_UNUSED(argv);
|
||||
//! [Register InterpLayer]
|
||||
CV_DNN_REGISTER_LAYER_CLASS(Interp, InterpLayer);
|
||||
cv::dnn::Net caffeNet = cv::dnn::readNet("/path/to/config.prototxt", "/path/to/weights.caffemodel");
|
||||
//! [Register InterpLayer]
|
||||
|
||||
//! [Register ResizeBilinearLayer]
|
||||
CV_DNN_REGISTER_LAYER_CLASS(ResizeBilinear, ResizeBilinearLayer);
|
||||
cv::dnn::Net tfNet = cv::dnn::readNet("/path/to/graph.pb");
|
||||
//! [Register ResizeBilinearLayer]
|
||||
}
|
||||
|
||||
cv::Ptr<cv::dnn::Layer> MyLayer::create(cv::dnn::LayerParams& params)
|
||||
{
|
||||
return cv::Ptr<cv::dnn::Layer>(new MyLayer(params));
|
||||
}
|
||||
MyLayer::MyLayer(const cv::dnn::LayerParams&) {}
|
||||
bool MyLayer::getMemoryShapes(const std::vector<std::vector<int> >&, const int,
|
||||
std::vector<std::vector<int> >&,
|
||||
std::vector<std::vector<int> >&) const { return false; }
|
||||
void MyLayer::forward(std::vector<cv::Mat*>&, std::vector<cv::Mat>&, std::vector<cv::Mat>&) {}
|
||||
void MyLayer::finalize(const std::vector<cv::Mat*>&, std::vector<cv::Mat>&) {}
|
||||
void MyLayer::forward(cv::InputArrayOfArrays, cv::OutputArrayOfArrays, cv::OutputArrayOfArrays) {}
|
||||
Reference in New Issue
Block a user