-
Notifications
You must be signed in to change notification settings - Fork 260
C++ Demo - Image Classification (PPResNet) #241
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 7 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
4a18415
Functional version of C++ demo.
ryan1288 4837106
Improve printout.
ryan1288 b21314c
Remove printouts and add README examples
ryan1288 3fe9e1b
Add goldfish example
ryan1288 45b1ef8
Add empty space at EOF
ryan1288 c10d3f9
Add empty line at EOF for Python
ryan1288 d387e34
Use the shared labels.txt file instead of having the entire list as a…
ryan1288 e0095f1
Address PR comments. Revert example and labels
ryan1288 e5d1db1
Use namespaces for brevity
ryan1288 51fb566
Follow OpenCV formatting
ryan1288 9a3c78f
Remove LoadLabel() and use a vector of strings instead of having redu…
ryan1288 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| cmake_minimum_required(VERSION 3.24) | ||
| set(project_name "opencv_zoo_image_classification_ppresnet") | ||
|
|
||
| PROJECT (${project_name}) | ||
|
|
||
| set(OPENCV_VERSION "4.9.0") | ||
| set(OPENCV_INSTALLATION_PATH "" CACHE PATH "Where to look for OpenCV installation") | ||
| find_package(OpenCV ${OPENCV_VERSION} REQUIRED HINTS ${OPENCV_INSTALLATION_PATH}) | ||
| # Find OpenCV, you may need to set OpenCV_DIR variable | ||
| # to the absolute path to the directory containing OpenCVConfig.cmake file | ||
| # via the command line or GUI | ||
|
|
||
| file(GLOB SourceFile | ||
| "demo.cpp") | ||
| # If the package has been found, several variables will | ||
| # be set, you can find the full list with descriptions | ||
| # in the OpenCVConfig.cmake file. | ||
| # Print some message showing some of them | ||
| message(STATUS "OpenCV library status:") | ||
| message(STATUS " config: ${OpenCV_DIR}") | ||
| message(STATUS " version: ${OpenCV_VERSION}") | ||
| message(STATUS " libraries: ${OpenCV_LIBS}") | ||
| message(STATUS " include path: ${OpenCV_INCLUDE_DIRS}") | ||
|
|
||
| # Declare the executable target built from your sources | ||
| add_executable(${project_name} ${SourceFile}) | ||
|
|
||
| # Set C++ compilation standard to C++11 | ||
| set(CMAKE_CXX_STANDARD 11) | ||
|
|
||
| # Link your application with OpenCV libraries | ||
| target_link_libraries(${project_name} PRIVATE ${OpenCV_LIBS}) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| #include <opencv2/opencv.hpp> | ||
| #include <opencv2/dnn.hpp> | ||
| #include <iostream> | ||
| #include <algorithm> | ||
| #include <fstream> | ||
|
|
||
| class PPResNet { | ||
| public: | ||
| PPResNet(const std::string& modelPath, int topK, int backendId, int targetId) | ||
| : _topK(topK) { | ||
| _model = cv::dnn::readNet(modelPath); | ||
| _model.setPreferableBackend(backendId); | ||
| _model.setPreferableTarget(targetId); | ||
| loadLabels(); | ||
| } | ||
|
|
||
| cv::Mat preprocess(const cv::Mat& image) { | ||
| cv::Mat floatImage; | ||
| image.convertTo(floatImage, CV_32F, 1.0 / 255.0); | ||
| cv::subtract(floatImage, _mean, floatImage); | ||
| cv::divide(floatImage, _std, floatImage); | ||
| return cv::dnn::blobFromImage(floatImage); | ||
| } | ||
|
|
||
| std::vector<std::string> infer(const cv::Mat& image) { | ||
| assert(image.rows == _inputSize.height && image.cols == _inputSize.width); | ||
| cv::Mat inputBlob = preprocess(image); | ||
| _model.setInput(inputBlob, _inputName); | ||
| cv::Mat outputBlob = _model.forward(_outputName); | ||
| std::vector<std::string> results = postprocess(outputBlob); | ||
| return results; | ||
| } | ||
|
|
||
| std::vector<std::string> postprocess(const cv::Mat& outputBlob) { | ||
| std::vector<int> class_id_list; | ||
| cv::sortIdx(outputBlob, class_id_list, cv::SORT_EVERY_ROW | cv::SORT_DESCENDING); | ||
| class_id_list.resize(std::min(_topK, static_cast<int>(outputBlob.cols))); | ||
| std::vector<std::string> predicted_labels; | ||
| for (int class_id : class_id_list) { | ||
| predicted_labels.push_back(_labels[class_id]); | ||
| } | ||
| return predicted_labels; | ||
| } | ||
|
|
||
| void loadLabels() { | ||
| std::ifstream labelsFile("labels.txt"); | ||
| if (labelsFile.is_open()) { | ||
| std::string line; | ||
| while (std::getline(labelsFile, line)) { | ||
| _labels.push_back(line); | ||
| } | ||
| labelsFile.close(); | ||
| } else { | ||
| std::cerr << "Unable to open labels file!" << std::endl; | ||
| } | ||
| } | ||
|
|
||
| private: | ||
| cv::dnn::Net _model; | ||
| int _topK; | ||
| std::vector<std::string> _labels; | ||
| const cv::Size _inputSize = cv::Size(224, 224); | ||
| const cv::Scalar _mean = cv::Scalar(0.485, 0.456, 0.406); | ||
| const cv::Scalar _std = cv::Scalar(0.229, 0.224, 0.225); | ||
| std::string _inputName = ""; | ||
| std::string _outputName = "save_infer_model/scale_0.tmp_0"; | ||
| }; | ||
|
|
||
| const std::vector<std::vector<int>> backend_target_pairs = { | ||
| {cv::dnn::DNN_BACKEND_OPENCV, cv::dnn::DNN_TARGET_CPU}, | ||
| {cv::dnn::DNN_BACKEND_CUDA, cv::dnn::DNN_TARGET_CUDA}, | ||
| {cv::dnn::DNN_BACKEND_CUDA, cv::dnn::DNN_TARGET_CUDA_FP16}, | ||
| {cv::dnn::DNN_BACKEND_TIMVX, cv::dnn::DNN_TARGET_NPU}, | ||
| {cv::dnn::DNN_BACKEND_CANN, cv::dnn::DNN_TARGET_NPU} | ||
| }; | ||
|
|
||
| int main(int argc, char** argv) { | ||
| cv::CommandLineParser parser(argc, argv, | ||
| "{ input i | | Set input path to a certain image, omit if using camera.}" | ||
| "{ model m | image_classification_ppresnet50_2022jan.onnx | Set model path.}" | ||
| "{ top_k k | 1 | Get top k predictions.}" | ||
| "{ backend_target bt | 0 | Choose one of computation backends: " | ||
| "0: (default) OpenCV implementation + CPU, " | ||
| "1: CUDA + GPU (CUDA), " | ||
| "2: CUDA + GPU (CUDA FP16), " | ||
| "3: TIM-VX + NPU, " | ||
| "4: CANN + NPU}"); | ||
|
|
||
| std::string inputPath = parser.get<std::string>("input"); | ||
| std::string modelPath = parser.get<std::string>("model"); | ||
| int backendTarget = parser.get<int>("backend_target"); | ||
| int topK = parser.get<int>("top_k"); | ||
|
|
||
| int backendId = backend_target_pairs[backendTarget][0]; | ||
| int targetId = backend_target_pairs[backendTarget][1]; | ||
|
|
||
| PPResNet model(modelPath, topK, backendId, targetId); | ||
|
|
||
| // Read image and get a 224x224 crop from a 256x256 resized | ||
| cv::Mat image = cv::imread(inputPath); | ||
| cv::cvtColor(image, image, cv::COLOR_BGR2RGB); | ||
| cv::resize(image, image, cv::Size(256, 256)); | ||
| image = image(cv::Rect(16, 16, 224, 224)); | ||
|
|
||
| // Inference | ||
| auto predictions = model.infer(image); | ||
|
|
||
| // Print result | ||
| if (topK == 1) | ||
| { | ||
| std::cout << "Predicted Label: " << predictions[0] << std::endl; | ||
| } | ||
| else | ||
| { | ||
| std::cout << "Predicted Top-K Labels (in decreasing confidence): " << std::endl; | ||
| for (size_t i = 0; i < predictions.size(); ++i) | ||
| { | ||
| std::cout << "(" << i+1 << ") " << predictions[i] << std::endl; | ||
| } | ||
| } | ||
|
|
||
| return 0; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 3 additions & 0 deletions
3
models/image_classification_ppresnet/example_input/goldfish.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need example input as it demonstrates nothing and it increase repo size.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed example 👍