PaddleOCR/deploy/lite/utils/db_post_process.cpp

371 lines
10 KiB
C++

// Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <iostream>
#include <vector>
#include <math.h>
#include "opencv2/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include "clipper.hpp"
#include "clipper.cpp"
void getcontourarea(float ** box, float unclip_ratio, float & distance){
int pts_num=4;
float area = 0.0f;
float dist = 0.0f;
for (int i=0; i<pts_num; i++){
area += box[i][0] * box[(i+1)%pts_num][1] - box[i][1] * box[(i + 1) % pts_num][0];
dist += sqrtf( (box[i][0] - box[(i + 1) % pts_num][0]) * (box[i][0] - box[(i + 1) % pts_num][0]) + (box[i][1] - box[(i + 1) % pts_num][1]) * (box[i][1] - box[(i + 1) % pts_num][1]) );
}
area = fabs(float(area/2.0));
distance = area * unclip_ratio / dist;
}
cv::RotatedRect unclip(float ** box){
float unclip_ratio = 2.0;
float distance = 1.0;
getcontourarea(box, unclip_ratio, distance);
ClipperLib::ClipperOffset offset;
ClipperLib::Path p;
p << ClipperLib::IntPoint(int(box[0][0]), int(box[0][1])) << ClipperLib::IntPoint(int(box[1][0]), int(box[1][1])) <<
ClipperLib::IntPoint(int(box[2][0]), int(box[2][1])) << ClipperLib::IntPoint(int(box[3][0]), int(box[3][1]));
offset.AddPath(p, ClipperLib::jtRound, ClipperLib::etClosedPolygon);
ClipperLib::Paths soln;
offset.Execute(soln, distance);
std::vector<cv::Point2f> points;
for (int j=0; j<soln.size(); j++){
for (int i=0; i< soln[soln.size()-1].size(); i++){
points.emplace_back(soln[j][i].X, soln[j][i].Y);
}
}
cv::RotatedRect res = cv::minAreaRect(points);
return res;
}
float ** Mat2Vec(cv::Mat mat)
{
auto **array = new float*[mat.rows];
for (int i = 0; i<mat.rows; ++i)
array[i] = new float[mat.cols];
for (int i = 0; i < mat.rows; ++i)
{
for (int j = 0; j < mat.cols; ++j)
{
array[i][j] = mat.at<float>(i, j);
}
}
return array;
}
void quickSort(float ** s, int l, int r)
{
if (l < r)
{
int i = l, j = r;
float x = s[l][0];
float * xp = s[l];
while (i < j)
{
while(i < j && s[j][0]>= x)
j--;
if(i < j)
std::swap(s[i++], s[j]);
while(i < j && s[i][0]< x)
i++;
if(i < j)
std::swap(s[j--], s[i]);
}
s[i] = xp;
quickSort(s, l, i - 1);
quickSort(s, i + 1, r);
}
}
void quickSort_vector(std::vector<std::vector<int>> & box, int l, int r, int axis){
if (l < r){
int i = l, j = r;
int x = box[l][axis];
std::vector<int> xp (box[l]);
while (i < j)
{
while(i < j && box[j][axis]>= x)
j--;
if(i < j)
std::swap(box[i++], box[j]);
while(i < j && box[i][axis]< x)
i++;
if(i < j)
std::swap(box[j--], box[i]);
}
box[i] = xp;
quickSort_vector(box, l, i - 1, axis);
quickSort_vector(box, i + 1, r, axis);
}
}
std::vector<std::vector<int>> order_points_clockwise(std::vector<std::vector<int>> pts){
std::vector<std::vector<int>> box = pts;
quickSort_vector(box, 0, int(box.size()-1), 0);
std::vector<std::vector<int>> leftmost = {box[0], box[1]};
std::vector<std::vector<int>> rightmost = {box[2], box[3]};
if (leftmost[0][1]>leftmost[1][1])
std::swap(leftmost[0], leftmost[1]);
if (rightmost[0][1]> rightmost[1][1])
std::swap(rightmost[0], rightmost[1]);
std::vector<std::vector<int>> rect = {leftmost[0], rightmost[0], rightmost[1], leftmost[1]};
return rect;
}
float ** get_mini_boxes(cv::RotatedRect box, float & ssid){
ssid = box.size.width>=box.size.height?box.size.height:box.size.width;
cv::Mat points;
cv::boxPoints(box, points);
// sorted box points
auto array = Mat2Vec(points);
quickSort(array, 0, 3);
float * idx1=array[0], *idx2=array[1], *idx3=array[2], *idx4=array[3];
if (array[3][1]<=array[2][1]) {
idx2 = array[3];
idx3 = array[2];
}
else{
idx2 = array[2];
idx3 = array[3];
}
if (array[1][1]<=array[0][1]){
idx1 = array[1];
idx4 = array[0];
}
else{
idx1 = array[0];
idx4 = array[1];
}
array[0] = idx1;
array[1] = idx2;
array[2] = idx3;
array[3] = idx4;
return array;
}
template<class T>
T clamp(T x, T min, T max)
{
if (x > max)
return max;
if (x < min)
return min;
return x;
}
float clampf(float x, float min, float max){
if (x > max)
return max;
if (x < min)
return min;
return x;
}
float box_score_fast(float ** box_array, cv::Mat pred){
auto array=box_array;
int width = pred.cols;
int height = pred.rows;
float box_x[4]={array[0][0], array[1][0], array[2][0], array[3][0]};
float box_y[4]={array[0][1], array[1][1], array[2][1], array[3][1]};
int xmin = clamp(int(std::floorf(*(std::min_element(box_x, box_x+4)))), 0, width - 1);
int xmax = clamp(int(std::ceilf(*(std::max_element(box_x, box_x+4)))), 0, width - 1);
int ymin = clamp(int(std::floorf(*(std::min_element(box_y, box_y+4)))), 0, height - 1);
int ymax = clamp(int(std::ceilf(*(std::max_element(box_y, box_y+4)))), 0, height - 1);
cv::Mat mask;
mask = cv::Mat::zeros(ymax - ymin + 1, xmax - xmin + 1, CV_8UC1);
cv::Point root_point[4];
root_point[0] = cv::Point(int(array[0][0])-xmin, int(array[0][1])-ymin);
root_point[1] = cv::Point(int(array[1][0])-xmin, int(array[1][1])-ymin);
root_point[2] = cv::Point(int(array[2][0])-xmin, int(array[2][1])-ymin);
root_point[3] = cv::Point(int(array[3][0])-xmin, int(array[3][1])-ymin);
const cv::Point* ppt[1] = {root_point};
int npt[] = {4};
cv::fillPoly(mask, ppt, npt, 1, cv::Scalar(1));
cv::Mat croppedImg;
pred(cv::Rect(xmin, ymin, xmax-xmin+1,ymax-ymin+1)).copyTo(croppedImg);
auto score = cv::mean(croppedImg, mask)[0];
return score;
}
std::vector<std::vector<std::vector<int>>> boxes_from_bitmap(const cv::Mat pred, const cv::Mat bitmap) {
const int min_size=3;
const int max_candidates = 1000;
const float box_thresh=0.5;
int width = bitmap.cols;
int height = bitmap.rows;
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(bitmap, contours, hierarchy, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE);
int num_contours = contours.size() >= max_candidates ? max_candidates : contours.size();
std::vector<std::vector<std::vector<int>>> boxes;
for (int _i = 0; _i < num_contours; _i++) {
float ssid;
cv::RotatedRect box = cv::minAreaRect(contours[_i]);
auto array = get_mini_boxes(box, ssid);
auto box_for_unclip = array;
//end get_mini_box
if (ssid< min_size) {
continue;
}
float score;
score = box_score_fast(array, pred);
//end box_score_fast
if (score < box_thresh)
continue;
// start for unclip
cv::RotatedRect points = unclip(box_for_unclip);
// end for unclip
cv::RotatedRect clipbox = points;
auto cliparray = get_mini_boxes(clipbox, ssid);
if (ssid < min_size+2) continue;
int dest_width=pred.cols;
int dest_height=pred.rows;
std::vector<std::vector<int>> intcliparray;
for (int num_pt=0; num_pt<4; num_pt++){
std::vector<int> a { int( clampf(roundf(cliparray[num_pt][0]/float(width)*float(dest_width)), 0, float(dest_width)) ),
int( clampf(roundf(cliparray[num_pt][1]/float(height)*float(dest_height)), 0, float(dest_height)) )};
intcliparray.push_back(a);
}
boxes.push_back(intcliparray);
}//end for
return boxes;
}
int _max(int a, int b){
return a>=b?a:b;
}
int _min(int a, int b){
return a>=b?b:a;
}
std::vector<std::vector<std::vector<int>>> filter_tag_det_res(std::vector<std::vector<std::vector<int>>> boxes,
float ratio_h, float ratio_w, cv::Mat srcimg){
int oriimg_h = srcimg.rows;
int oriimg_w = srcimg.cols;
std::vector<std::vector<std::vector<int>>> root_points;
for (int n=0; n<boxes.size(); n++){
boxes[n] = order_points_clockwise(boxes[n]);
for (int m=0; m< boxes[0].size(); m++){
boxes[n][m][0] /= ratio_w;
boxes[n][m][1] /= ratio_h;
boxes[n][m][0] = int(_min(_max(boxes[n][m][0], 0), oriimg_w-1));
boxes[n][m][1] = int(_min(_max(boxes[n][m][1], 0), oriimg_h-1));
}
}
for(int n=0; n<boxes.size(); n++){
int rect_width, rect_height;
rect_width = int(sqrt(pow(boxes[n][0][0] - boxes[n][1][0], 2) + pow(boxes[n][0][1] - boxes[n][1][1], 2)));
rect_height = int(sqrt(pow(boxes[n][0][0] - boxes[n][3][0], 2) + pow(boxes[n][0][1] - boxes[n][3][1], 2)));
if (rect_width <=10 || rect_height<=10)
continue;
root_points.push_back(boxes[n]);
}
return root_points;
}
/*
using namespace std;
// read data from txt file
cv::Mat readtxt2(std::string path, int imgw, int imgh, int imgc) {
std::cout << "read data file from txt file! " << std::endl;
ifstream in(path);
string line;
int count = 0;
int i = 0, j = 0;
std::vector<float> img_mean = {0.485, 0.456, 0.406};
std::vector<float> img_std = {0.229, 0.224, 0.225};
float trainData[imgh][imgw*imgc];
while (getline(in, line)) {
stringstream ss(line);
double x;
while (ss >> x) {
// trainData[i][j] = float(x) * img_std[j % 3] + img_mean[j % 3];
trainData[i][j] = float(x);
j++;
}
i++;
j = 0;
}
cv::Mat pred_map(imgh, imgw*imgc, CV_32FC1, (float *) trainData);
cv::Mat reshape_img = pred_map.reshape(imgc, imgh);
return reshape_img;
}
*/
//using namespace std;
//
//void writetxt(vector<vector<float>> data, std::string save_path){
//
// ofstream fout(save_path);
//
// for (int i = 0; i < data.size(); i++) {
// for (int j=0; j< data[0].size(); j++){
// fout << data[i][j] << " ";
// }
// fout << endl;
// }
// fout << endl;
// fout.close();
//}