|
|
#include <opencv2/opencv.hpp>
|
|
|
#include <vector>
|
|
|
#include <cmath>
|
|
|
#include <torch/torch.h>
|
|
|
#include <torch/script.h>
|
|
|
#include <filesystem>
|
|
|
#include <iostream>
|
|
|
#include <tuple>
|
|
|
|
|
|
|
|
|
class Normalize {
|
|
|
public:
|
|
|
Normalize() {
|
|
|
mean = {0.485, 0.456, 0.406};
|
|
|
std = {0.229, 0.224, 0.225};
|
|
|
}
|
|
|
|
|
|
|
|
|
cv::Mat operator()(const cv::Mat& image) {
|
|
|
cv::Mat normalizedImage;
|
|
|
image.convertTo(normalizedImage, CV_32F, 1.0/255.0);
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 3; ++i) {
|
|
|
normalizedImage.forEach<cv::Vec3f>([&](cv::Vec3f &pixel, const int * position) -> void {
|
|
|
pixel[i] = (pixel[i] - mean[i]) / std[i];
|
|
|
});
|
|
|
}
|
|
|
|
|
|
return normalizedImage;
|
|
|
}
|
|
|
|
|
|
private:
|
|
|
std::vector<float> mean;
|
|
|
std::vector<float> std;
|
|
|
};
|
|
|
|
|
|
|
|
|
namespace cv {
|
|
|
typedef Vec<float, 5> Vec5f;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const float MODEL_SCALE = 4;
|
|
|
|
|
|
|
|
|
std::pair<cv::Mat, cv::Mat> pred2box(const cv::Mat& hm, const torch::Tensor& offset, const torch::Tensor& regr, const torch::Tensor& cos_sin_hm, float thresh = 0.99) {
|
|
|
|
|
|
|
|
|
|
|
|
cv::Mat pred;
|
|
|
cv::compare(hm, thresh, pred, cv::CMP_GT);
|
|
|
|
|
|
std::vector<cv::Point> pred_center_points;
|
|
|
cv::findNonZero(pred, pred_center_points);
|
|
|
|
|
|
|
|
|
std::vector<cv::Vec2f> pred_r;
|
|
|
std::vector<cv::Vec2f> pred_angles;
|
|
|
for (auto& center : pred_center_points) {
|
|
|
float regr_val1 = regr.index({0,center.y, center.x}).item<float>();
|
|
|
float regr_val2 = regr.index({1,center.y, center.x}).item<float>();
|
|
|
|
|
|
float cos_sin_val1 = cos_sin_hm.index({0,center.y, center.x}).item<float>();
|
|
|
float cos_sin_val2 = cos_sin_hm.index({1,center.y, center.x}).item<float>();
|
|
|
|
|
|
|
|
|
pred_angles.push_back(cv::Vec2f(cos_sin_val1, cos_sin_val2));
|
|
|
pred_r.push_back(cv::Vec2f(regr_val1, regr_val2));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
std::vector<cv::Vec5f> boxes;
|
|
|
cv::Mat scores;
|
|
|
hm.convertTo(scores, CV_32F);
|
|
|
|
|
|
for (size_t i = 0; i < pred_center_points.size(); ++i) {
|
|
|
auto& center = pred_center_points[i];
|
|
|
const auto& b = pred_r[i];
|
|
|
const auto& pred_angle = pred_angles[i];
|
|
|
|
|
|
float offsetx = offset.index({0,center.y, center.x}).item<float>();
|
|
|
float offsety = offset.index({1,center.y, center.x}).item<float>();
|
|
|
|
|
|
cv::Vec2f offset_xy = cv::Vec2f(offsetx, offsety);
|
|
|
float angle = std::atan2(pred_angle[1], pred_angle[0]);
|
|
|
|
|
|
|
|
|
cv::Vec5f arr = {
|
|
|
(center.x + offset_xy[0]) * MODEL_SCALE,
|
|
|
(center.y + offset_xy[1]) * MODEL_SCALE,
|
|
|
b[0] * MODEL_SCALE,
|
|
|
b[1] * MODEL_SCALE,
|
|
|
angle
|
|
|
};
|
|
|
|
|
|
boxes.push_back(arr);
|
|
|
}
|
|
|
|
|
|
|
|
|
cv::Mat boxes_mat(boxes.size(), 1, CV_32FC(5), boxes.data());
|
|
|
cv::Mat scores_mat;
|
|
|
for (const auto& center : pred_center_points) {
|
|
|
scores_mat.push_back(scores.at<float>(center));
|
|
|
}
|
|
|
|
|
|
|
|
|
return {boxes_mat, scores_mat};
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cv::Mat select(cv::Mat& hm, float threshold) {
|
|
|
cv::Mat pred;
|
|
|
cv::compare(hm, threshold, pred, cv::CMP_GT);
|
|
|
|
|
|
std::vector<cv::Point> pred_centers;
|
|
|
cv::findNonZero(pred, pred_centers);
|
|
|
|
|
|
for (size_t i = 0; i < pred_centers.size(); ++i) {
|
|
|
for (size_t j = i + 1; j < pred_centers.size(); ++j) {
|
|
|
const cv::Point& ci = pred_centers[i];
|
|
|
const cv::Point& cj = pred_centers[j];
|
|
|
|
|
|
float distance = cv::norm(ci - cj);
|
|
|
if (distance <= 2) {
|
|
|
float score_i = hm.at<float>(ci);
|
|
|
float score_j = hm.at<float>(cj);
|
|
|
|
|
|
if (score_i > score_j) {
|
|
|
hm.at<float>(cj) = 0;
|
|
|
} else {
|
|
|
hm.at<float>(ci) = 0;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return hm;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::tuple<cv::Mat, float, int, int> resize_and_pad(const cv::Mat& image, const cv::Size& target_size = cv::Size(512, 512)) {
|
|
|
int original_height = image.rows;
|
|
|
int original_width = image.cols;
|
|
|
int target_width = target_size.width;
|
|
|
int target_height = target_size.height;
|
|
|
|
|
|
|
|
|
float scale = std::min(static_cast<float>(target_width) / original_width, static_cast<float>(target_height) / original_height);
|
|
|
|
|
|
|
|
|
int new_width = static_cast<int>(original_width * scale);
|
|
|
int new_height = static_cast<int>(original_height * scale);
|
|
|
|
|
|
|
|
|
cv::Mat resized_image;
|
|
|
cv::resize(image, resized_image, cv::Size(new_width, new_height));
|
|
|
|
|
|
|
|
|
int delta_w = target_width - new_width;
|
|
|
int delta_h = target_height - new_height;
|
|
|
int top = delta_h / 2;
|
|
|
int bottom = delta_h - top;
|
|
|
int left = delta_w / 2;
|
|
|
int right = delta_w - left;
|
|
|
|
|
|
cv::Mat padded_image;
|
|
|
cv::copyMakeBorder(resized_image, padded_image, top, bottom, left, right, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0));
|
|
|
|
|
|
return std::make_tuple(padded_image, scale, left, top);
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<cv::Point> pred4corner(const cv::Mat& hm, float thresh = 0.99) {
|
|
|
float threshold = 0.2;
|
|
|
cv::Mat thresholded_heatmap;
|
|
|
cv::threshold(hm, thresholded_heatmap, threshold, 1, cv::THRESH_BINARY);
|
|
|
|
|
|
cv::Mat thresholded_heatmap_8u;
|
|
|
thresholded_heatmap.convertTo(thresholded_heatmap_8u, CV_8UC1);
|
|
|
|
|
|
std::vector<std::vector<cv::Point>> contours;
|
|
|
cv::findContours(thresholded_heatmap_8u, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
|
|
|
|
|
|
std::vector<cv::Point> keypoints;
|
|
|
for (const auto& cnt : contours) {
|
|
|
cv::Moments M = cv::moments(cnt);
|
|
|
if (M.m00 != 0) {
|
|
|
int cx = static_cast<int>(M.m10 / M.m00);
|
|
|
int cy = static_cast<int>(M.m01 / M.m00);
|
|
|
keypoints.push_back(cv::Point(cx, cy));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return keypoints;
|
|
|
}
|
|
|
|
|
|
|
|
|
cv::Mat showbox(const cv::Mat& img, const cv::Mat& hm, const torch::Tensor& offset, const torch::Tensor& regr, const torch::Tensor& cos_sin_hm, float thresh = 0.9) {
|
|
|
auto [boxes, _] = pred2box(hm, offset, regr, cos_sin_hm, thresh);
|
|
|
|
|
|
cv::Mat sample = img.clone();
|
|
|
|
|
|
for (int i = 0; i < boxes.rows; ++i) {
|
|
|
const float* box_data = boxes.ptr<float>(i);
|
|
|
|
|
|
cv::Point center(box_data[0], box_data[1]);
|
|
|
|
|
|
float cos_angle = std::cos(box_data[4]);
|
|
|
float sin_angle = std::sin(box_data[4]);
|
|
|
|
|
|
cv::Mat rot = (cv::Mat_<float>(2, 2) << cos_angle, sin_angle, -sin_angle, cos_angle);
|
|
|
|
|
|
cv::Mat half_size = (cv::Mat_<float>(2, 1) << box_data[2] / 2, box_data[3] / 2);
|
|
|
|
|
|
cv::Mat bottom_right = rot * half_size;
|
|
|
cv::Mat top_right = rot * (cv::Mat_<float>(2, 1) << box_data[2] / 2, -box_data[3] / 2);
|
|
|
cv::Mat top_left = rot * (cv::Mat_<float>(2, 1) << -box_data[2] / 2, -box_data[3] / 2);
|
|
|
cv::Mat bottom_left = rot * (cv::Mat_<float>(2, 1) << -box_data[2] / 2, box_data[3] / 2);
|
|
|
|
|
|
int thickness = 3;
|
|
|
cv::line(sample, center + cv::Point(bottom_right), center + cv::Point(top_right), cv::Scalar(0, 220, 0), thickness);
|
|
|
cv::line(sample, center + cv::Point(bottom_right), center + cv::Point(bottom_left), cv::Scalar(220, 220, 0), thickness);
|
|
|
cv::line(sample, center + cv::Point(top_left), center + cv::Point(bottom_left), cv::Scalar(220, 220, 0), thickness);
|
|
|
cv::line(sample, center + cv::Point(top_left), center + cv::Point(top_right), cv::Scalar(220, 220, 0), thickness);
|
|
|
}
|
|
|
|
|
|
return sample;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
|
|
int main() {
|
|
|
|
|
|
torch::jit::script::Module model;
|
|
|
try {
|
|
|
model = torch::jit::load("C:/Users/John/Desktop/CPP_Centernet/hardnet_angle_4c_centernet_jit.pth");
|
|
|
model.to(torch::kCUDA);
|
|
|
} catch (const c10::Error& e) {
|
|
|
std::cerr << "Error loading the model: " << e.what() << std::endl;
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
bool image = false;
|
|
|
|
|
|
|
|
|
std::string test_folder = "C:/Users/John/Desktop/rotated_barcode/roboflow_barcode/test/images/";
|
|
|
float threshold = 0.2;
|
|
|
bool half = false;
|
|
|
|
|
|
if (image) {
|
|
|
|
|
|
for (const auto& entry : fs::directory_iterator(test_folder)) {
|
|
|
if (entry.is_regular_file() && entry.path().extension() == ".jpg") {
|
|
|
cv::Mat image = cv::imread(entry.path().string());
|
|
|
|
|
|
|
|
|
auto [resized_image, scale, left, top] = resize_and_pad(image);
|
|
|
cv::Mat img = resized_image.clone();
|
|
|
cv::Mat imgshow = img.clone();
|
|
|
img = Normalize()(img);
|
|
|
|
|
|
torch::Tensor tensor = torch::from_blob(img.data, {1, img.rows, img.cols, 3}, torch::kFloat).permute({0, 3, 1, 2});
|
|
|
|
|
|
if (half) {
|
|
|
tensor = tensor.to(torch::kCUDA).to(torch::kHalf);
|
|
|
} else {
|
|
|
tensor = tensor.to(torch::kCUDA);
|
|
|
}
|
|
|
|
|
|
|
|
|
torch::NoGradGuard no_grad;
|
|
|
|
|
|
auto outputs = model.forward({tensor}).toTuple();
|
|
|
torch::Tensor hm = outputs->elements()[0].toTensor().to(torch::kCPU).squeeze(0);
|
|
|
torch::Tensor offset_tensor = outputs->elements()[1].toTensor().to(torch::kCPU).squeeze(0);
|
|
|
torch::Tensor wh_tensor = outputs->elements()[2].toTensor().to(torch::kCPU).squeeze(0);
|
|
|
torch::Tensor angle_tensor = outputs->elements()[3].toTensor().to(torch::kCPU).squeeze(0);
|
|
|
|
|
|
|
|
|
|
|
|
hm = torch::sigmoid(hm);
|
|
|
cv::Mat hm_mat(hm.size(1), hm.size(2), CV_32F, hm[0].data_ptr<float>());
|
|
|
cv::Mat hm_mat_corner(hm.size(1), hm.size(2), CV_32F, hm[1].data_ptr<float>());
|
|
|
|
|
|
|
|
|
cv::imshow("heatmap",hm_mat);
|
|
|
cv::imshow("heatmap corner",hm_mat_corner);
|
|
|
|
|
|
|
|
|
hm_mat = select(hm_mat, threshold);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cv::Mat sample = showbox(imgshow, hm_mat, offset_tensor, wh_tensor, angle_tensor, threshold);
|
|
|
|
|
|
cv::resize(hm_mat_corner,hm_mat_corner,cv::Size(512,512));
|
|
|
auto kpoints = pred4corner(hm_mat_corner,threshold);
|
|
|
|
|
|
for (auto element: kpoints) {
|
|
|
|
|
|
cv::circle(sample,cv::Point(element.x,element.y),5,cv::Scalar(255,255,255),-1);
|
|
|
}
|
|
|
|
|
|
cv::imshow("output", sample);
|
|
|
|
|
|
|
|
|
char ch = cv::waitKey(0);
|
|
|
if (ch == 'q') {
|
|
|
cv::destroyAllWindows();
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
|
cv::VideoCapture cap(0);
|
|
|
cap.set(cv::CAP_PROP_FOURCC,1196444237);
|
|
|
cap.set(cv::CAP_PROP_FRAME_WIDTH,1280);
|
|
|
cap.set(cv::CAP_PROP_FRAME_HEIGHT,720);
|
|
|
cap.set(cv::CAP_PROP_AUTO_EXPOSURE, 3);
|
|
|
cap.set(cv::CAP_PROP_AUTO_EXPOSURE, 1);
|
|
|
|
|
|
cap.set(cv::CAP_PROP_EXPOSURE,-5);
|
|
|
cap.set(cv::CAP_PROP_FPS,30);
|
|
|
|
|
|
if (!cap.isOpened()) {
|
|
|
std::cerr << "Error opening webcam" << std::endl;
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
while (true) {
|
|
|
cv::Mat frame;
|
|
|
cap >> frame;
|
|
|
|
|
|
cv::Mat image = frame.clone();
|
|
|
|
|
|
|
|
|
auto [resized_image, scale, left, top] = resize_and_pad(image);
|
|
|
cv::Mat img = resized_image.clone();
|
|
|
cv::Mat imgshow = img.clone();
|
|
|
img = Normalize()(img);
|
|
|
|
|
|
torch::Tensor tensor = torch::from_blob(img.data, {1, img.rows, img.cols, 3}, torch::kFloat).permute({0, 3, 1, 2});
|
|
|
|
|
|
if (half) {
|
|
|
tensor = tensor.to(torch::kCUDA).to(torch::kHalf);
|
|
|
} else {
|
|
|
tensor = tensor.to(torch::kCUDA);
|
|
|
}
|
|
|
|
|
|
|
|
|
torch::NoGradGuard no_grad;
|
|
|
|
|
|
auto start_time = std::chrono::high_resolution_clock::now();
|
|
|
auto outputs = model.forward({tensor}).toTuple();
|
|
|
auto end_time = std::chrono::high_resolution_clock::now();
|
|
|
std::chrono::duration<double> elapsed_seconds = end_time - start_time;
|
|
|
double fps = 1.0 / elapsed_seconds.count();
|
|
|
|
|
|
|
|
|
|
|
|
torch::Tensor hm = outputs->elements()[0].toTensor().to(torch::kCPU).squeeze(0);
|
|
|
torch::Tensor offset_tensor = outputs->elements()[1].toTensor().to(torch::kCPU).squeeze(0);
|
|
|
torch::Tensor wh_tensor = outputs->elements()[2].toTensor().to(torch::kCPU).squeeze(0);
|
|
|
torch::Tensor angle_tensor = outputs->elements()[3].toTensor().to(torch::kCPU).squeeze(0);
|
|
|
|
|
|
|
|
|
|
|
|
hm = torch::sigmoid(hm);
|
|
|
cv::Mat hm_mat(hm.size(1), hm.size(2), CV_32F, hm[0].data_ptr<float>());
|
|
|
cv::Mat hm_mat_corner(hm.size(1), hm.size(2), CV_32F, hm[1].data_ptr<float>());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
hm_mat = select(hm_mat, threshold);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cv::Mat sample = showbox(imgshow, hm_mat, offset_tensor, wh_tensor, angle_tensor, threshold);
|
|
|
cv::putText(sample, "FPS: " + std::to_string(fps), cv::Point(10, 30), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 255, 0), 2);
|
|
|
|
|
|
cv::resize(hm_mat_corner,hm_mat_corner,cv::Size(512,512));
|
|
|
auto kpoints = pred4corner(hm_mat_corner,threshold);
|
|
|
|
|
|
for (auto element: kpoints) {
|
|
|
|
|
|
cv::circle(sample,cv::Point(element.x,element.y),5,cv::Scalar(255,255,255),-1);
|
|
|
}
|
|
|
|
|
|
cv::imshow("output", sample);
|
|
|
|
|
|
|
|
|
char ch = cv::waitKey(1);
|
|
|
if (ch == 'q') {
|
|
|
cv::destroyAllWindows();
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|