diff --git a/modules/dnn/include/opencv2/dnn/dnn.hpp b/modules/dnn/include/opencv2/dnn/dnn.hpp index bac8032481..d055cadf42 100644 --- a/modules/dnn/include/opencv2/dnn/dnn.hpp +++ b/modules/dnn/include/opencv2/dnn/dnn.hpp @@ -545,6 +545,33 @@ CV__DNN_INLINE_NS_BEGIN virtual void setProg(const std::vector >& newprog) = 0; }; + /** @brief Single entry in a @ref PerfProfile. + * + * In DNN_PROFILE_DETAILED mode, @p label is "layer_name (type)" and @p count is 1. + * In DNN_PROFILE_SUMMARY mode, @p label is the layer type and @p count is the + * number of layers of that type that contributed to @p timeMs. + */ + struct CV_EXPORTS_W_SIMPLE PerfProfileEntry + { + CV_WRAP PerfProfileEntry() : timeMs(0.0), count(0) {} + CV_PROP_RW String label; + CV_PROP_RW double timeMs; + CV_PROP_RW int count; + }; + + /** @brief Self-describing snapshot of profiling data from one inference. + * + * Carries the @ref ProfilingMode it was captured in so it can be saved, kept across + * runs (e.g. best-of-N by total time), and printed later via @ref Net::printPerfProfile + * without needing access to the originating @ref Net. + */ + struct CV_EXPORTS_W_SIMPLE PerfProfile + { + CV_WRAP PerfProfile() : mode(DNN_PROFILE_NONE) {} + CV_PROP_RW ProfilingMode mode; + CV_PROP_RW std::vector entries; + }; + /** @brief This class allows to create and manipulate comprehensive artificial neural networks. * * Neural network is presented as directed acyclic graph (DAG), where vertices are Layer instances, @@ -1027,6 +1054,21 @@ CV__DNN_INLINE_NS_BEGIN */ CV_WRAP int64 getPerfProfile(CV_OUT std::vector& timings); + /** @brief Returns profiling data captured during the last forward pass. + * + * Entries are sorted by time in descending order. Empty vectors are returned + * if profiling is disabled (DNN_PROFILE_NONE). + */ + CV_WRAP void getPerfProfile(CV_OUT std::vector& names, CV_OUT std::vector& timems, CV_OUT std::vector& counts) const; + + /** @brief Prints the profile captured during the last forward pass in a formatted table using CV_LOG_INFO. + * + * In DNN_PROFILE_DETAILED mode, prints per-layer label, time, and percentage. + * In DNN_PROFILE_SUMMARY mode, prints per-type count, time, and percentage. + * Does nothing if profiling is disabled (DNN_PROFILE_NONE) or all timings are zero. + */ + CV_WRAP void printPerfProfile() const; + // Get the main model graph Ptr getMainGraph() const; diff --git a/modules/dnn/src/net.cpp b/modules/dnn/src/net.cpp index 4e6e26808d..42bfed7802 100644 --- a/modules/dnn/src/net.cpp +++ b/modules/dnn/src/net.cpp @@ -446,6 +446,20 @@ int64 Net::getPerfProfile(std::vector& timings) return impl->getPerfProfile(timings); } +void Net::getPerfProfile(std::vector& names, + std::vector& timems, + std::vector& counts) const +{ + CV_Assert(impl); + impl->getPerfProfile(names, timems, counts); +} + +void Net::printPerfProfile() const +{ + CV_Assert(impl); + impl->printPerfProfile(); +} + bool Net::isConstArg(Arg arg) const { return argKind(arg) == DNN_ARG_CONST; diff --git a/modules/dnn/src/net_impl.cpp b/modules/dnn/src/net_impl.cpp index 611eb859af..210e04b487 100644 --- a/modules/dnn/src/net_impl.cpp +++ b/modules/dnn/src/net_impl.cpp @@ -6,6 +6,12 @@ #include "net_impl.hpp" +#ifdef HAVE_ONNXRUNTIME +#include +#include +#include +#endif + namespace cv { namespace dnn { CV__DNN_INLINE_NS_BEGIN @@ -2642,6 +2648,251 @@ int64 Net::Impl::getPerfProfile(std::vector& timings) const return total; } +void Net::Impl::collectLayerInfo(std::vector& names, std::vector& types) const +{ + if (mainGraph) { + names.reserve(totalLayers); + types.reserve(totalLayers); + for (const Ptr& graph : allgraphs) { + const std::vector>& prog = graph->prog(); + for (const Ptr& layer : prog) { + names.push_back(layer ? layer->name : "null"); + types.push_back(layer ? layer->type : "null"); + } + } + } else { + for (MapIdToLayerData::const_iterator it = layers.begin(); it != layers.end(); ++it) { + if (it->second.id) { // skip Data layer (id==0) + names.push_back(it->second.name); + types.push_back(it->second.type); + } + } + } +} + +#ifdef HAVE_ONNXRUNTIME +static void parseOrtProfileJson(const std::string& text, + std::map>& out_ms) +{ + const std::string wrapped = "{\"events\":" + text + "}"; + FileStorage fs(wrapped, FileStorage::READ | FileStorage::MEMORY | FileStorage::FORMAT_JSON); + if (!fs.isOpened()) { + CV_LOG_WARNING(NULL, "DNN/ORT: failed to parse profile JSON"); + return; + } + + static const std::string KT = "_kernel_time"; + FileNode events = fs["events"]; + for (FileNodeIterator it = events.begin(); it != events.end(); ++it) { + FileNode entry = *it; + if (!entry.isMap()) continue; + if ((std::string)entry["cat"] != "Node") continue; + + const std::string name = (std::string)entry["name"]; + if (name.size() < KT.size() || + name.compare(name.size() - KT.size(), KT.size(), KT) != 0) + continue; + + FileNode args = entry["args"]; + if (args.empty()) continue; + const std::string op = (std::string)args["op_name"]; + const double dur_us = (double)entry["dur"]; + if (op.empty() || dur_us <= 0) continue; + + const std::string canonical = name.substr(0, name.size() - KT.size()); + auto& slot = out_ms[canonical]; + slot.first = op; + slot.second += dur_us / 1000.0; + } +} + +void Net::Impl::collectOrtProfileData() const +{ + if (ort_profile_collected) return; + ort_profile_collected = true; + if (!ort_session || ort_profile_path_prefix.empty()) return; + + // EndProfiling closes the file ORT has been writing to and returns its path. + Ort::AllocatorWithDefaultOptions allocator; + Ort::AllocatedStringPtr profile_path = ort_session->EndProfilingAllocated(allocator); + if (!profile_path) { + CV_LOG_WARNING(NULL, "DNN/ORT: EndProfiling did not return a path (prefix=" << ort_profile_path_prefix << ")"); + return; + } + + // Read the JSON entirely into memory, then parse it from the string. + std::ifstream in(profile_path.get()); + if (!in.is_open()) { + CV_LOG_WARNING(NULL, "DNN/ORT: failed to open profile JSON " << profile_path.get()); + return; + } + std::stringstream ss; ss << in.rdbuf(); + const std::string text = ss.str(); + + std::map> by_name; + parseOrtProfileJson(text, by_name); + + const int runs = ort_profile_runs > 0 ? ort_profile_runs : 1; + ort_profile_data.clear(); + ort_profile_data.reserve(by_name.size()); + for (auto& kv : by_name) { + const String name = kv.first; + const String type = kv.second.first; + const double ms_per_run = kv.second.second / runs; + ort_profile_data.emplace_back(name, type, ms_per_run); + } +} +#endif + +PerfProfile Net::Impl::getPerfProfile() const +{ + PerfProfile result; + result.mode = profilingMode; + + if (profilingMode == DNN_PROFILE_NONE) + return result; + +#ifdef HAVE_ONNXRUNTIME + if (useOrtEngine && ort_session) { + collectOrtProfileData(); + if (profilingMode == DNN_PROFILE_DETAILED) { + for (const auto& t : ort_profile_data) { + if (std::get<2>(t) <= 0) continue; + PerfProfileEntry e; + e.label = std::get<0>(t) + " (" + std::get<1>(t) + ")"; + e.timeMs = std::get<2>(t); + e.count = 1; + result.entries.push_back(e); + } + } else if (profilingMode == DNN_PROFILE_SUMMARY) { + std::map typeTimings; + std::map typeCounts; + for (const auto& t : ort_profile_data) { + if (std::get<2>(t) <= 0) continue; + typeTimings[std::get<1>(t)] += std::get<2>(t); + typeCounts[std::get<1>(t)]++; + } + result.entries.reserve(typeTimings.size()); + for (auto it = typeTimings.begin(); it != typeTimings.end(); ++it) { + PerfProfileEntry e; + e.label = it->first; + e.timeMs = it->second; + e.count = typeCounts[it->first]; + result.entries.push_back(e); + } + } + std::sort(result.entries.begin(), result.entries.end(), + [](const PerfProfileEntry& a, const PerfProfileEntry& b) { + return a.timeMs > b.timeMs; + }); + return result; + } +#endif + + std::vector timings(layersTimings.begin() + 1, layersTimings.end()); + double tickFreq = getTickFrequency(); + + std::vector names; + std::vector types; + collectLayerInfo(names, types); + + size_t n = std::min(timings.size(), names.size()); + + if (profilingMode == DNN_PROFILE_DETAILED) { + for (size_t i = 0; i < n; i++) { + if (timings[i] > 0) { + PerfProfileEntry e; + e.label = names[i] + " (" + types[i] + ")"; + e.timeMs = timings[i] * 1000.0 / tickFreq; + e.count = 1; + result.entries.push_back(e); + } + } + } else if (profilingMode == DNN_PROFILE_SUMMARY) { + std::map typeTimings; + std::map typeCounts; + for (size_t i = 0; i < n; i++) { + if (timings[i] > 0) { + typeTimings[types[i]] += timings[i] * 1000.0 / tickFreq; + typeCounts[types[i]]++; + } + } + result.entries.reserve(typeTimings.size()); + for (auto it = typeTimings.begin(); it != typeTimings.end(); ++it) { + PerfProfileEntry e; + e.label = it->first; + e.timeMs = it->second; + e.count = typeCounts[it->first]; + result.entries.push_back(e); + } + } + + std::sort(result.entries.begin(), result.entries.end(), + [](const PerfProfileEntry& a, const PerfProfileEntry& b) { + return a.timeMs > b.timeMs; + }); + + return result; +} + +void Net::Impl::getPerfProfile(std::vector& names, + std::vector& timems, + std::vector& counts) const +{ + PerfProfile profile = getPerfProfile(); + names.clear(); + timems.clear(); + counts.clear(); + names.reserve(profile.entries.size()); + timems.reserve(profile.entries.size()); + counts.reserve(profile.entries.size()); + for (const PerfProfileEntry& e : profile.entries) { + names.push_back(e.label); + timems.push_back(cv::format("%.3f", e.timeMs)); + counts.push_back(cv::format("%d", e.count)); + } +} + +void Net::Impl::printPerfProfile() const +{ + PerfProfile profile = getPerfProfile(); + if (profile.mode == DNN_PROFILE_NONE) + return; + + double totalMs = 0.0; + for (const PerfProfileEntry& e : profile.entries) + totalMs += e.timeMs; + if (totalMs <= 0.0) + return; + + if (profile.mode == DNN_PROFILE_DETAILED) { + CV_LOG_INFO(NULL, "\n=== DNN Layer Profiling (Detailed) ==="); + CV_LOG_INFO(NULL, cv::format("%-5s %-60s %10s %8s", "ID", "Layer (Type)", "Time (ms)", " (%)")); + CV_LOG_INFO(NULL, "-----------------------------------------------------------------------------------------------"); + for (size_t i = 0; i < profile.entries.size(); i++) { + const PerfProfileEntry& e = profile.entries[i]; + double pct = e.timeMs * 100.0 / totalMs; + CV_LOG_INFO(NULL, cv::format("%-5zu %-60s %10.3f %7.1f%%", + i, e.label.c_str(), e.timeMs, pct)); + } + CV_LOG_INFO(NULL, "-----------------------------------------------------------------------------------------------"); + CV_LOG_INFO(NULL, cv::format("%-5s %-60s %10.3f %7s", "", "TOTAL", totalMs, "100.0%")); + CV_LOG_INFO(NULL, ""); + } else if (profile.mode == DNN_PROFILE_SUMMARY) { + CV_LOG_INFO(NULL, "\n=== DNN Layer Profiling (Summary by Type) ==="); + CV_LOG_INFO(NULL, cv::format("%-25s %6s %10s %8s", "Layer Type", "Count", "Time (ms)", " (%)")); + CV_LOG_INFO(NULL, "-----------------------------------------------------------"); + for (const PerfProfileEntry& e : profile.entries) { + double pct = e.timeMs * 100.0 / totalMs; + CV_LOG_INFO(NULL, cv::format("%-25s %6d %10.3f %7.1f%%", + e.label.c_str(), e.count, e.timeMs, pct)); + } + CV_LOG_INFO(NULL, "-----------------------------------------------------------"); + CV_LOG_INFO(NULL, cv::format("%-25s %6s %10.3f %7s", "TOTAL", "", totalMs, "100.0%")); + CV_LOG_INFO(NULL, ""); + } +} + void Net::Impl::getMemoryConsumption( const std::vector& netInputShapes, const std::vector& netInputTypes, diff --git a/modules/dnn/src/net_impl.hpp b/modules/dnn/src/net_impl.hpp index eb60202932..c5b3159191 100644 --- a/modules/dnn/src/net_impl.hpp +++ b/modules/dnn/src/net_impl.hpp @@ -254,12 +254,17 @@ struct Net::Impl : public detail::NetImplBase void finalizeOrt(); void refreshOrtMainGraphOutputs(); void applyStagedOrtInputs(); + void collectOrtProfileData() const; std::vector> ort_staged_inputs; std::shared_ptr ort_env; std::shared_ptr ort_session; std::shared_ptr ort_names_cache; bool useOrtEngine = false; // true only when user explicitly selected ENGINE_ORT bool ortNeedsReinit = false; // session needs (re)creation on next finalizeNet + std::string ort_profile_path_prefix; // prefix passed to EnableProfiling + mutable bool ort_profile_collected = false; // EndProfiling was already called once + mutable int ort_profile_runs = 0; // number of session.Run calls since profiling started + mutable std::vector> ort_profile_data; // (name, type, ms_per_run) #endif void allocateLayer(int lid, const LayersShapesMap& layersShapes); @@ -330,6 +335,10 @@ struct Net::Impl : public detail::NetImplBase std::vector& layerIds, std::vector& weights, std::vector& blobs) /*const*/; int64 getPerfProfile(std::vector& timings) const; + void collectLayerInfo(std::vector& names, std::vector& types) const; + PerfProfile getPerfProfile() const; + void getPerfProfile(std::vector& names, std::vector& timems, std::vector& counts) const; + void printPerfProfile() const; // TODO drop LayerPin getLatestLayerPin(const std::vector& pins) const; diff --git a/modules/dnn/src/net_impl2.cpp b/modules/dnn/src/net_impl2.cpp index 773d8512e0..b35c740206 100644 --- a/modules/dnn/src/net_impl2.cpp +++ b/modules/dnn/src/net_impl2.cpp @@ -221,6 +221,7 @@ std::vector Net::Impl::runOrtSession(std::vector inputBlobs, const std Ort::RunOptions{nullptr}, in_names.data(), input_tensors.data(), input_tensors.size(), out_names.data(), out_names.size()); + if (profilingMode != DNN_PROFILE_NONE) ort_profile_runs++; CV_CheckEQ(output_tensors.size(), out_names.size(), "DNN/ORT: ORT returned unexpected number of outputs"); diff --git a/modules/dnn/src/net_impl_backend.cpp b/modules/dnn/src/net_impl_backend.cpp index 659cf8927e..0e4bdf1f41 100644 --- a/modules/dnn/src/net_impl_backend.cpp +++ b/modules/dnn/src/net_impl_backend.cpp @@ -88,6 +88,17 @@ void Net::Impl::finalizeOrt() } } + // If the user set DNN_PROFILE_*, turn on ORT's session profiler. The JSON + // file is parsed lazily by collectOrtProfileData() inside getPerfProfile()/printPerfProfile(). + ort_profile_path_prefix.clear(); + ort_profile_collected = false; + ort_profile_runs = 0; + ort_profile_data.clear(); + if (profilingMode != DNN_PROFILE_NONE) { + ort_profile_path_prefix = cv::tempfile("opencv_ort_profile_"); + opts.EnableProfiling(ort_profile_path_prefix.c_str()); + } + #ifdef _WIN32 std::wstring wpath(modelFileName.begin(), modelFileName.end()); ort_session = std::make_shared(*ort_env, wpath.c_str(), opts); diff --git a/samples/dnn/classification.cpp b/samples/dnn/classification.cpp index 35ff331206..dd505fac18 100644 --- a/samples/dnn/classification.cpp +++ b/samples/dnn/classification.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "common.hpp" @@ -91,6 +92,8 @@ static bool readStringList( const string& filename, vector& l ) int main(int argc, char** argv) { + utils::logging::setLogLevel(utils::logging::LOG_LEVEL_INFO); + CommandLineParser parser(argc, argv, keys); if (!parser.has("@alias") || parser.has("help")) @@ -150,6 +153,7 @@ int main(int argc, char** argv) Net net = readNetFromONNX(model, engine); net.setPreferableBackend(getBackendID(backend)); net.setPreferableTarget(getTargetID(target)); + net.setProfilingMode(DNN_PROFILE_SUMMARY); //! [Read and initialize network] // Create a window @@ -229,6 +233,7 @@ int main(int argc, char** argv) timeRecorder.start(); prob = net.forward(); timeRecorder.stop(); + net.printPerfProfile(); //! [Make forward pass] //! [Get a class with a highest score] diff --git a/samples/dnn/classification.py b/samples/dnn/classification.py index bdf8a73541..c80f35240c 100644 --- a/samples/dnn/classification.py +++ b/samples/dnn/classification.py @@ -72,6 +72,7 @@ def main(func_args=None): help() exit(1) + cv.utils.logging.setLogLevel(cv.utils.logging.LOG_LEVEL_INFO) args.model = findModel(args.model, args.sha1) args.labels = findFile(args.labels) @@ -88,6 +89,8 @@ def main(func_args=None): net = cv.dnn.readNetFromONNX(args.model, engine) net.setPreferableBackend(get_backend_id(args.backend)) net.setPreferableTarget(get_target_id(args.target)) + if hasattr(cv.dnn, 'DNN_PROFILE_SUMMARY'): + net.setProfilingMode(cv.dnn.DNN_PROFILE_SUMMARY) winName = 'Deep learning image classification in OpenCV' cv.namedWindow(winName, cv.WINDOW_NORMAL) @@ -138,6 +141,7 @@ def main(func_args=None): t0 = cv.getTickCount() out = net.forward() t = (cv.getTickCount() - t0) / cv.getTickFrequency() + net.printPerfProfile() (h, w, _) = frame.shape roi_rows = min(300, h) diff --git a/samples/dnn/object_detection.cpp b/samples/dnn/object_detection.cpp index f8ae3c06b1..683bd1e028 100644 --- a/samples/dnn/object_detection.cpp +++ b/samples/dnn/object_detection.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -161,6 +162,8 @@ private: int main(int argc, char** argv) { + utils::logging::setLogLevel(utils::logging::LOG_LEVEL_INFO); + CommandLineParser parser(argc, argv, keys); string zooFile = parser.get("zoo"); @@ -225,6 +228,7 @@ int main(int argc, char** argv) int backend = getBackendID(parser.get("backend")); net.setPreferableBackend(backend); net.setPreferableTarget(getTargetID(parser.get("target"))); + net.setProfilingMode(DNN_PROFILE_SUMMARY); //![read_net] // Create a window @@ -302,6 +306,7 @@ int main(int argc, char** argv) //![forward] vector outs; net.forward(outs, net.getUnconnectedOutLayersNames()); + net.printPerfProfile(); predictionsQueue.push(outs); //![forward] } @@ -372,6 +377,7 @@ int main(int argc, char** argv) tickMeter.start(); net.forward(outs, net.getUnconnectedOutLayersNames()); tickMeter.stop(); + net.printPerfProfile(); classIds.clear(); confidences.clear(); diff --git a/samples/dnn/object_detection.py b/samples/dnn/object_detection.py index 9d4f47ae8a..4388205ed5 100644 --- a/samples/dnn/object_detection.py +++ b/samples/dnn/object_detection.py @@ -71,6 +71,7 @@ if args.alias is None or hasattr(args, 'help'): help() exit(1) +cv.utils.logging.setLogLevel(cv.utils.logging.LOG_LEVEL_INFO) args.model = findModel(args.model, args.sha1) if args.config is not None: args.config = findModel(args.config, args.config_sha1) @@ -104,6 +105,8 @@ if args.backend != "default" or args.target != "cpu": net = cv.dnn.readNet(args.model, args.config, "", engine) net.setPreferableBackend(get_backend_id(args.backend)) net.setPreferableTarget(get_target_id(args.target)) +if hasattr(cv.dnn, 'DNN_PROFILE_SUMMARY'): + net.setProfilingMode(cv.dnn.DNN_PROFILE_SUMMARY) outNames = net.getUnconnectedOutLayersNames() confThreshold = args.thr @@ -340,6 +343,7 @@ def processingThreadBody(): futureOutputs.append(net.forwardAsync()) else: outs = net.forward(outNames) + net.printPerfProfile() predictionsQueue.put(copy.deepcopy(outs)) while futureOutputs and futureOutputs[0].wait_for(0): @@ -408,6 +412,7 @@ else: net.setInput(blob) outs = net.forward(outNames) + net.printPerfProfile() boxes, classIds, confidences, indices = postprocess(frame, outs) drawPred(classIds, confidences, boxes, indices, (stdSize*max(frame.shape[:2]))/stdImgSize, (stdWeight*max(frame.shape[:2]))//stdImgSize) diff --git a/samples/dnn/segmentation.cpp b/samples/dnn/segmentation.cpp index 9797013270..622ad1f505 100644 --- a/samples/dnn/segmentation.cpp +++ b/samples/dnn/segmentation.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "common.hpp" @@ -139,6 +140,8 @@ static void showLegend(FontFace fontFace) int main(int argc, char **argv) { + utils::logging::setLogLevel(utils::logging::LOG_LEVEL_INFO); + CommandLineParser parser(argc, argv, keys); const string modelName = parser.get("@alias"); @@ -218,7 +221,8 @@ int main(int argc, char **argv) Net net = readNetFromONNX(model, engine); net.setPreferableBackend(getBackendID(backend)); net.setPreferableTarget(getTargetID(target)); - //! [Read and initialize network] + net.setProfilingMode(DNN_PROFILE_SUMMARY); + //! [Read and initialize network] // Create a window static const string kWinName = "Deep learning semantic segmentation in OpenCV"; namedWindow(kWinName, WINDOW_AUTOSIZE); @@ -263,6 +267,7 @@ int main(int argc, char **argv) { vector output; net.forward(output, net.getUnconnectedOutLayersNames()); + net.printPerfProfile(); Mat pred = output[0].reshape(1, output[0].size[2]); pred.convertTo(pred, CV_8U, 255.0); @@ -284,6 +289,7 @@ int main(int argc, char **argv) { //! [Make forward pass] Mat score = net.forward(); + net.printPerfProfile(); //! [Make forward pass] Mat segm; colorizeSegmentation(score, segm); diff --git a/samples/dnn/segmentation.py b/samples/dnn/segmentation.py index 348c75ad0f..d681a723b0 100644 --- a/samples/dnn/segmentation.py +++ b/samples/dnn/segmentation.py @@ -73,6 +73,7 @@ def main(func_args=None): help() exit(1) + cv.utils.logging.setLogLevel(cv.utils.logging.LOG_LEVEL_INFO) args.model = findModel(args.model, args.sha1) if args.labels is not None: args.labels = findFile(args.labels) @@ -105,6 +106,8 @@ def main(func_args=None): net = cv.dnn.readNetFromONNX(args.model, engine) net.setPreferableBackend(get_backend_id(args.backend)) net.setPreferableTarget(get_target_id(args.target)) + if hasattr(cv.dnn, 'DNN_PROFILE_SUMMARY'): + net.setProfilingMode(cv.dnn.DNN_PROFILE_SUMMARY) winName = 'Deep learning semantic segmentation in OpenCV' cv.namedWindow(winName, cv.WINDOW_AUTOSIZE) @@ -138,6 +141,7 @@ def main(func_args=None): t0 = cv.getTickCount() if args.alias == 'u2netp': output = net.forward(net.getUnconnectedOutLayersNames()) + net.printPerfProfile() pred = output[0][0, 0, :, :] mask = (pred * 255).astype(np.uint8) mask = cv.resize(mask, (frame.shape[1], frame.shape[0]), interpolation=cv.INTER_AREA) @@ -149,6 +153,7 @@ def main(func_args=None): frame = cv.addWeighted(frame, 0.25, foreground_overlay, 0.75, 0) else: score = net.forward() + net.printPerfProfile() numClasses = score.shape[1] height = score.shape[2] @@ -176,4 +181,4 @@ def main(func_args=None): cv.imshow(winName, frame) if __name__ == "__main__": - main() \ No newline at end of file + main()