Refactor project structure

This commit is contained in:
2026-03-28 19:56:39 +11:00
parent 1d267378b2
commit 8a2e721058
511 changed files with 59 additions and 48 deletions

View File

@@ -0,0 +1,15 @@
#pragma once
#include <gflags/gflags.h>
DECLARE_string(input);
DECLARE_string(type);
DECLARE_string(output_dir);
DECLARE_string(det_model_dir);
DECLARE_string(cls_model_dir);
DECLARE_string(rec_model_dir);
DECLARE_string(lay_model_dir);
DECLARE_string(tab_model_dir);
DECLARE_string(label_dir);
DECLARE_string(layout_dict_dir);
DECLARE_string(table_dict_dir);

View File

@@ -0,0 +1,425 @@
/*******************************************************************************
* *
* Author : Angus Johnson *
* Version : 6.4.2 *
* Date : 27 February 2017 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2017 *
* *
* License: *
* Use, modification & distribution is subject to Boost Software License Ver 1. *
* http://www.boost.org/LICENSE_1_0.txt *
* *
* Attributions: *
* The code in this library is an extension of Bala Vatti's clipping algorithm: *
* "A generic solution to polygon clipping" *
* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. *
* http://portal.acm.org/citation.cfm?id=129906 *
* *
* Computer graphics and geometric modeling: implementation and algorithms *
* By Max K. Agoston *
* Springer; 1 edition (January 4, 2005) *
* http://books.google.com/books?q=vatti+clipping+agoston *
* *
* See also: *
* "Polygon Offsetting by Computing Winding Numbers" *
* Paper no. DETC2005-85513 pp. 565-575 *
* ASME 2005 International Design Engineering Technical Conferences *
* and Computers and Information in Engineering Conference (IDETC/CIE2005) *
* September 24-28, 2005 , Long Beach, California, USA *
* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf *
* *
*******************************************************************************/
#pragma once
#ifndef clipper_hpp
#define clipper_hpp
#define CLIPPER_VERSION "6.4.2"
// use_int32: When enabled 32bit ints are used instead of 64bit ints. This
// improve performance but coordinate values are limited to the range +/- 46340
//#define use_int32
// use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance.
//#define use_xyz
// use_lines: Enables line clipping. Adds a very minor cost to performance.
#define use_lines
// use_deprecated: Enables temporary support for the obsolete functions
//#define use_deprecated
#include <cstdlib>
#include <cstring>
#include <functional>
#include <list>
#include <ostream>
#include <queue>
#include <set>
#include <stdexcept>
#include <vector>
namespace ClipperLib {
enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };
enum PolyType { ptSubject, ptClip };
// By far the most widely used winding rules for polygon filling are
// EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32)
// Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL)
// see http://glprogramming.com/red/chapter11.html
enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
#ifdef use_int32
typedef int cInt;
static cInt const loRange = 0x7FFF;
static cInt const hiRange = 0x7FFF;
#else
typedef signed long long cInt;
static cInt const loRange = 0x3FFFFFFF;
static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL;
typedef signed long long long64; // used by Int128 class
typedef unsigned long long ulong64;
#endif
struct IntPoint {
cInt X;
cInt Y;
#ifdef use_xyz
cInt Z;
IntPoint(cInt x = 0, cInt y = 0, cInt z = 0) : X(x), Y(y), Z(z){};
#else
IntPoint(cInt x = 0, cInt y = 0) : X(x), Y(y){};
#endif
friend inline bool operator==(const IntPoint &a, const IntPoint &b) {
return a.X == b.X && a.Y == b.Y;
}
friend inline bool operator!=(const IntPoint &a, const IntPoint &b) {
return a.X != b.X || a.Y != b.Y;
}
};
//------------------------------------------------------------------------------
typedef std::vector<IntPoint> Path;
typedef std::vector<Path> Paths;
inline Path &operator<<(Path &poly, const IntPoint &p) {
poly.push_back(p);
return poly;
}
inline Paths &operator<<(Paths &polys, const Path &p) {
polys.push_back(p);
return polys;
}
std::ostream &operator<<(std::ostream &s, const IntPoint &p);
std::ostream &operator<<(std::ostream &s, const Path &p);
std::ostream &operator<<(std::ostream &s, const Paths &p);
struct DoublePoint {
double X;
double Y;
DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {}
DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {}
};
//------------------------------------------------------------------------------
#ifdef use_xyz
typedef void (*ZFillCallback)(IntPoint &e1bot, IntPoint &e1top, IntPoint &e2bot,
IntPoint &e2top, IntPoint &pt);
#endif
enum InitOptions {
ioReverseSolution = 1,
ioStrictlySimple = 2,
ioPreserveCollinear = 4
};
enum JoinType { jtSquare, jtRound, jtMiter };
enum EndType {
etClosedPolygon,
etClosedLine,
etOpenButt,
etOpenSquare,
etOpenRound
};
class PolyNode;
typedef std::vector<PolyNode *> PolyNodes;
class PolyNode {
public:
PolyNode();
virtual ~PolyNode(){};
Path Contour;
PolyNodes Childs;
PolyNode *Parent;
PolyNode *GetNext() const;
bool IsHole() const;
bool IsOpen() const;
int ChildCount() const;
private:
// PolyNode& operator =(PolyNode& other);
unsigned Index; // node index in Parent.Childs
bool m_IsOpen;
JoinType m_jointype;
EndType m_endtype;
PolyNode *GetNextSiblingUp() const;
void AddChild(PolyNode &child);
friend class Clipper; // to access Index
friend class ClipperOffset;
};
class PolyTree : public PolyNode {
public:
~PolyTree() { Clear(); };
PolyNode *GetFirst() const;
void Clear();
int Total() const;
private:
// PolyTree& operator =(PolyTree& other);
PolyNodes AllNodes;
friend class Clipper; // to access AllNodes
};
bool Orientation(const Path &poly);
double Area(const Path &poly);
int PointInPolygon(const IntPoint &pt, const Path &path);
void SimplifyPolygon(const Path &in_poly, Paths &out_polys,
PolyFillType fillType = pftEvenOdd);
void SimplifyPolygons(const Paths &in_polys, Paths &out_polys,
PolyFillType fillType = pftEvenOdd);
void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd);
void CleanPolygon(const Path &in_poly, Path &out_poly, double distance = 1.415);
void CleanPolygon(Path &poly, double distance = 1.415);
void CleanPolygons(const Paths &in_polys, Paths &out_polys,
double distance = 1.415);
void CleanPolygons(Paths &polys, double distance = 1.415);
void MinkowskiSum(const Path &pattern, const Path &path, Paths &solution,
bool pathIsClosed);
void MinkowskiSum(const Path &pattern, const Paths &paths, Paths &solution,
bool pathIsClosed);
void MinkowskiDiff(const Path &poly1, const Path &poly2, Paths &solution);
void PolyTreeToPaths(const PolyTree &polytree, Paths &paths);
void ClosedPathsFromPolyTree(const PolyTree &polytree, Paths &paths);
void OpenPathsFromPolyTree(PolyTree &polytree, Paths &paths);
void ReversePath(Path &p);
void ReversePaths(Paths &p);
struct IntRect {
cInt left;
cInt top;
cInt right;
cInt bottom;
};
// enums that are used internally ...
enum EdgeSide { esLeft = 1, esRight = 2 };
// forward declarations (for stuff used internally) ...
struct TEdge;
struct IntersectNode;
struct LocalMinimum;
struct OutPt;
struct OutRec;
struct Join;
typedef std::vector<OutRec *> PolyOutList;
typedef std::vector<TEdge *> EdgeList;
typedef std::vector<Join *> JoinList;
typedef std::vector<IntersectNode *> IntersectList;
//------------------------------------------------------------------------------
// ClipperBase is the ancestor to the Clipper class. It should not be
// instantiated directly. This class simply abstracts the conversion of sets of
// polygon coordinates into edge objects that are stored in a LocalMinima list.
class ClipperBase {
public:
ClipperBase();
virtual ~ClipperBase();
virtual bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed);
bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed);
virtual void Clear();
IntRect GetBounds();
bool PreserveCollinear() { return m_PreserveCollinear; };
void PreserveCollinear(bool value) { m_PreserveCollinear = value; };
protected:
void DisposeLocalMinimaList();
TEdge *AddBoundsToLML(TEdge *e, bool IsClosed);
virtual void Reset();
TEdge *ProcessBound(TEdge *E, bool IsClockwise);
void InsertScanbeam(const cInt Y);
bool PopScanbeam(cInt &Y);
bool LocalMinimaPending();
bool PopLocalMinima(cInt Y, const LocalMinimum *&locMin);
OutRec *CreateOutRec();
void DisposeAllOutRecs();
void DisposeOutRec(PolyOutList::size_type index);
void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);
void DeleteFromAEL(TEdge *e);
void UpdateEdgeIntoAEL(TEdge *&e);
typedef std::vector<LocalMinimum> MinimaList;
MinimaList::iterator m_CurrentLM;
MinimaList m_MinimaList;
bool m_UseFullRange;
EdgeList m_edges;
bool m_PreserveCollinear;
bool m_HasOpenPaths;
PolyOutList m_PolyOuts;
TEdge *m_ActiveEdges;
typedef std::priority_queue<cInt> ScanbeamList;
ScanbeamList m_Scanbeam;
};
//------------------------------------------------------------------------------
class Clipper : public virtual ClipperBase {
public:
Clipper(int initOptions = 0);
bool Execute(ClipType clipType, Paths &solution,
PolyFillType fillType = pftEvenOdd);
bool Execute(ClipType clipType, Paths &solution, PolyFillType subjFillType,
PolyFillType clipFillType);
bool Execute(ClipType clipType, PolyTree &polytree,
PolyFillType fillType = pftEvenOdd);
bool Execute(ClipType clipType, PolyTree &polytree, PolyFillType subjFillType,
PolyFillType clipFillType);
bool ReverseSolution() { return m_ReverseOutput; };
void ReverseSolution(bool value) { m_ReverseOutput = value; };
bool StrictlySimple() { return m_StrictSimple; };
void StrictlySimple(bool value) { m_StrictSimple = value; };
// set the callback function for z value filling on intersections (otherwise Z
// is 0)
#ifdef use_xyz
void ZFillFunction(ZFillCallback zFillFunc);
#endif
protected:
virtual bool ExecuteInternal();
private:
JoinList m_Joins;
JoinList m_GhostJoins;
IntersectList m_IntersectList;
ClipType m_ClipType;
typedef std::list<cInt> MaximaList;
MaximaList m_Maxima;
TEdge *m_SortedEdges;
bool m_ExecuteLocked;
PolyFillType m_ClipFillType;
PolyFillType m_SubjFillType;
bool m_ReverseOutput;
bool m_UsingPolyTree;
bool m_StrictSimple;
#ifdef use_xyz
ZFillCallback m_ZFill; // custom callback
#endif
void SetWindingCount(TEdge &edge);
bool IsEvenOddFillType(const TEdge &edge) const;
bool IsEvenOddAltFillType(const TEdge &edge) const;
void InsertLocalMinimaIntoAEL(const cInt botY);
void InsertEdgeIntoAEL(TEdge *edge, TEdge *startEdge);
void AddEdgeToSEL(TEdge *edge);
bool PopEdgeFromSEL(TEdge *&edge);
void CopyAELToSEL();
void DeleteFromSEL(TEdge *e);
void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2);
bool IsContributing(const TEdge &edge) const;
bool IsTopHorz(const cInt XPos);
void DoMaxima(TEdge *e);
void ProcessHorizontals();
void ProcessHorizontal(TEdge *horzEdge);
void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
OutPt *AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
OutRec *GetOutRec(int idx);
void AppendPolygon(TEdge *e1, TEdge *e2);
void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt);
OutPt *AddOutPt(TEdge *e, const IntPoint &pt);
OutPt *GetLastOutPt(TEdge *e);
bool ProcessIntersections(const cInt topY);
void BuildIntersectList(const cInt topY);
void ProcessIntersectList();
void ProcessEdgesAtTopOfScanbeam(const cInt topY);
void BuildResult(Paths &polys);
void BuildResult2(PolyTree &polytree);
void SetHoleState(TEdge *e, OutRec *outrec);
void DisposeIntersectNodes();
bool FixupIntersectionOrder();
void FixupOutPolygon(OutRec &outrec);
void FixupOutPolyline(OutRec &outrec);
bool IsHole(TEdge *e);
bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl);
void FixHoleLinkage(OutRec &outrec);
void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt);
void ClearJoins();
void ClearGhostJoins();
void AddGhostJoin(OutPt *op, const IntPoint offPt);
bool JoinPoints(Join *j, OutRec *outRec1, OutRec *outRec2);
void JoinCommonEdges();
void DoSimplePolygons();
void FixupFirstLefts1(OutRec *OldOutRec, OutRec *NewOutRec);
void FixupFirstLefts2(OutRec *InnerOutRec, OutRec *OuterOutRec);
void FixupFirstLefts3(OutRec *OldOutRec, OutRec *NewOutRec);
#ifdef use_xyz
void SetZ(IntPoint &pt, TEdge &e1, TEdge &e2);
#endif
};
//------------------------------------------------------------------------------
class ClipperOffset {
public:
ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25);
~ClipperOffset();
void AddPath(const Path &path, JoinType joinType, EndType endType);
void AddPaths(const Paths &paths, JoinType joinType, EndType endType);
void Execute(Paths &solution, double delta);
void Execute(PolyTree &solution, double delta);
void Clear();
double MiterLimit;
double ArcTolerance;
private:
Paths m_destPolys;
Path m_srcPoly;
Path m_destPoly;
std::vector<DoublePoint> m_normals;
double m_delta, m_sinA, m_sin, m_cos;
double m_miterLim, m_StepsPerRad;
IntPoint m_lowest;
PolyNode m_polyNodes;
void FixOrientations();
void DoOffset(double delta);
void OffsetPoint(int j, int &k, JoinType jointype);
void DoSquare(int j, int k);
void DoMiter(int j, int k, double r);
void DoRound(int j, int k);
};
//------------------------------------------------------------------------------
class clipperException : public std::exception {
public:
clipperException(const char *description) : m_descr(description) {}
virtual ~clipperException() throw() {}
virtual const char *what() const throw() { return m_descr.c_str(); }
private:
std::string m_descr;
};
//------------------------------------------------------------------------------
} // ClipperLib namespace
#endif // clipper_hpp

View File

@@ -0,0 +1,45 @@
#include "opencv2/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include <chrono>
#include <iomanip>
#include <iostream>
#include <ostream>
#include <vector>
#include <cstring>
#include <fstream>
#include <numeric>
#include <include/preprocess_op.h>
#include <include/postprocess_op.h>
#include <openvino/openvino.hpp>
#include <openvino/core/preprocess/pre_post_process.hpp>
namespace PaddleOCR {
class Classifier
{
public:
explicit Classifier(std::string model_path);
void Run(std::vector<cv::Mat> img_list, std::vector<OCRPredictResult>& ocr_results);
void SetParameters(int cls_batch_num, double cls_thresh);
void GetParameters(int& cls_batch_num, double& cls_thresh);
private:
ov::InferRequest infer_request;
std::string model_path;
std::shared_ptr<ov::Model> model;
ov::CompiledModel compiled_model;
std::recursive_mutex _mutex;
double e = 1.0 / 255.0;
std::vector<float> mean_ = { 0.5f, 0.5f, 0.5f };
std::vector<float> scale_ = { 0.5f, 0.5f, 0.5f };
int cls_batch_num_ = 1;
double cls_thresh = 0.9;
std::vector<size_t> cls_image_shape = { 3, 48, 192 };
std::string GetOpenVINODevice();
// resize
ClsResizeImg resize_op_;
};
}

View File

@@ -0,0 +1,71 @@
#include "opencv2/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include <chrono>
#include <iomanip>
#include <iostream>
#include <ostream>
#include <vector>
#include <cstring>
#include <fstream>
#include <numeric>
#include <include/paddleocr_utility.h>
#include <include/preprocess_op.h>
#include <include/postprocess_op.h>
#include <openvino/openvino.hpp>
namespace PaddleOCR {
class Detector
{
public:
explicit Detector(std::string model_path);
void Run(const cv::Mat& src_img, std::vector<OCRPredictResult>& ocr_results);
void SetParameters(std::string limit_type,
std::string det_db_score_mode,
bool is_scale,
double det_db_thresh,
double det_db_box_thresh,
double det_db_unclip_ratio,
bool use_dilation);
void GetParameters(std::string& limit_type,
std::string& det_db_score_mode,
bool& is_scale,
double& det_db_thresh,
double& det_db_box_thresh,
double& det_db_unclip_ratio,
bool& use_dilation);
private:
ov::InferRequest infer_request;
std::string model_path;
cv::Mat src_img;
std::shared_ptr<ov::Model> model;
ov::CompiledModel compiled_model;
std::recursive_mutex _mutex;
float ratio_h{};
float ratio_w{};
std::vector<float> mean_ = { 0.485f, 0.456f, 0.406f };
std::vector<float> scale_ = { 1 / 0.229f, 1 / 0.224f, 1 / 0.225f };
cv::Mat resize_img;
double e = 1.0 / 255.0;
std::string limit_type_ = "max";
std::string det_db_score_mode_ = "slow";
int limit_side_len_ = 960;
bool is_scale_ = true;
double det_db_thresh_ = 0.3;
double det_db_box_thresh_ = 0.6;
double det_db_unclip_ratio_ = 1.5;
bool use_dilation_ = false;
// pre-process
ResizeImgType0 resize_op_;
Normalize normalize_op_;
Permute permute_op_;
// post-process
DBPostProcessor post_processor_;
};
}

View File

@@ -0,0 +1,46 @@
#include "opencv2/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include <chrono>
#include <iomanip>
#include <iostream>
#include <ostream>
#include <vector>
#include <cstring>
#include <fstream>
#include <numeric>
#include <include/paddleocr_utility.h>
#include <include/preprocess_op.h>
#include <include/postprocess_op.h>
#include <openvino/openvino.hpp>
namespace PaddleOCR {
class Recognizer
{
public:
explicit Recognizer(std::string model_path, const std::string& label_path);
void Run(const std::vector<cv::Mat>& img_list, std::vector<OCRPredictResult>& ocr_results);
void SetParameters(int rec_batch_num);
void GetParameters(int& rec_batch_num);
private:
ov::InferRequest infer_request;
std::string model_path;
std::shared_ptr<ov::Model> model;
ov::CompiledModel compiled_model;
std::recursive_mutex _mutex;
std::vector<float> mean_ = { 0.5f, 0.5f, 0.5f };
std::vector<float> scale_ = { 1 / 0.5f, 1 / 0.5f, 1 / 0.5f };
bool is_scale_ = true;
std::vector<std::string> label_list_;
int rec_img_h_ = 48;
int rec_img_w_ = 320;
std::vector<int> rec_image_shape_ = { 3, rec_img_h_, rec_img_w_ };
int rec_batch_num_ = 1;
CrnnResizeImg resize_op_;
Normalize normalize_op_;
PermuteBatch permute_op_;
};
}

View File

@@ -0,0 +1,68 @@
// 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.
#pragma once
#include <include/ocr_cls.h>
#include <include/ocr_det.h>
#include <include/ocr_rec.h>
namespace PaddleOCR {
class PPOCR {
public:
explicit PPOCR();
~PPOCR();
std::vector<OCRPredictResult> ocr(const cv::Mat& img);
bool Initialize(std::string detectionModelDir, std::string classifierModelDir, std::string recognizerModelDir, std::string labelDir);
void SetParameters(std::string limit_type,
std::string det_db_score_mode,
bool is_scale,
double det_db_thresh,
double det_db_box_thresh,
double det_db_unclip_ratio,
bool use_dilation,
int cls_batch_num,
double cls_thresh,
int rec_batch_num);
void GetParameters(std::string& limit_type,
std::string& det_db_score_mode,
bool& is_scale,
double& det_db_thresh,
double& det_db_box_thresh,
double& det_db_unclip_ratio,
bool& use_dilation,
int& cls_batch_num,
double& cls_thresh,
int& rec_batch_num);
protected:
std::unique_ptr<Detector> detector_ = nullptr;
std::unique_ptr<Classifier> classifier_ = nullptr;
std::unique_ptr<Recognizer> recognizer_ = nullptr;
std::recursive_mutex _mutex;
std::string _limit_type;
std::string _det_db_score_mode;
bool _is_scale;
double _det_db_thresh;
double _det_db_box_thresh;
double _det_db_unclip_ratio;
bool _use_dilation;
int _cls_batch_num;
double _cls_thresh;
int _rec_batch_num;
};
} // namespace PaddleOCR

View File

@@ -0,0 +1,110 @@
// 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.
#pragma once
#include <chrono>
#include <iomanip>
#include <iostream>
#include <ostream>
#include <stdlib.h>
#include <vector>
#include <algorithm>
#include <cstring>
#include <fstream>
#include <numeric>
#include "opencv2/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
namespace PaddleOCR {
struct OCRPredictResult {
std::vector<std::vector<int>> box;
std::string text;
float score = -1.0;
float cls_score;
int cls_label = -1;
};
struct StructurePredictResult {
std::vector<float> box;
std::vector<std::vector<int>> cell_box;
std::string type;
std::vector<OCRPredictResult> text_res;
std::string html;
float html_score = -1;
float confidence;
};
class Utility {
public:
static std::vector<std::string> ReadDict(const std::string &path);
static void VisualizeBboxes(const cv::Mat &srcimg,
const std::vector<OCRPredictResult> &ocr_result,
const std::string &save_path);
static void VisualizeBboxes(const cv::Mat &srcimg,
const StructurePredictResult &structure_result,
const std::string &save_path);
template <class ForwardIterator>
inline static size_t argmax(ForwardIterator first, ForwardIterator last) {
return std::distance(first, std::max_element(first, last));
}
static void GetAllFiles(const char *dir_name,
std::vector<std::string> &all_inputs);
static cv::Mat GetRotateCropImage(const cv::Mat &srcimage,
std::vector<std::vector<int>> box);
static std::vector<int> argsort(const std::vector<float> &array);
static std::string basename(const std::string &filename);
static bool PathExists(const std::string &path);
static void CreateDir(const std::string &path);
static void print_result(const std::vector<OCRPredictResult> &ocr_result);
static cv::Mat crop_image(cv::Mat &img, const std::vector<int> &area);
static cv::Mat crop_image(cv::Mat &img, const std::vector<float> &area);
static void sorted_boxes(std::vector<OCRPredictResult> &ocr_result);
static std::vector<int> xyxyxyxy2xyxy(std::vector<std::vector<int>> &box);
static std::vector<int> xyxyxyxy2xyxy(std::vector<int> &box);
static float fast_exp(float x);
static std::vector<float>
activation_function_softmax(std::vector<float> &src);
static float iou(std::vector<int> &box1, std::vector<int> &box2);
static float iou(std::vector<float> &box1, std::vector<float> &box2);
private:
static bool comparison_box(const OCRPredictResult &result1,
const OCRPredictResult &result2) {
if (result1.box[0][1] < result2.box[0][1]) {
return true;
} else if (result1.box[0][1] == result2.box[0][1]) {
return result1.box[0][0] < result2.box[0][0];
} else {
return false;
}
}
};
} // namespace PaddleOCR

View File

@@ -0,0 +1,53 @@
// Copyright (c) 2022 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.
#pragma once
#include <include/paddleocr.h>
#include <include/structure_layout.h>
#include <include/structure_table.h>
namespace PaddleOCR {
class PaddleStructure : public PPOCR {
public:
explicit PaddleStructure();
~PaddleStructure();
bool Initialize(std::string layModelDir, std::string layModelDic, std::string tabModelDir, std::string tabModelDic);
std::vector<StructurePredictResult> structure(cv::Mat img);
private:
Layout *layout_model_ = nullptr;
Table *table_model_ = nullptr;
std::string rebuild_table(std::vector<std::string> rec_html_tags,
std::vector<std::vector<int>> rec_boxes,
std::vector<OCRPredictResult> &ocr_result);
float dis(std::vector<int> &box1, std::vector<int> &box2);
static bool comparison_dis(const std::vector<float> &dis1,
const std::vector<float> &dis2) {
if (dis1[1] < dis2[1]) {
return true;
} else if (dis1[1] == dis2[1]) {
return dis1[0] < dis2[0];
} else {
return false;
}
}
};
} // namespace PaddleOCR

View File

@@ -0,0 +1,119 @@
// 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.
#pragma once
#include "include/clipper.h"
#include "include/paddleocr_utility.h"
#include <openvino/openvino.hpp>
namespace PaddleOCR {
class DBPostProcessor {
public:
void GetContourArea(const std::vector<std::vector<float>> &box,
float unclip_ratio, float &distance);
cv::RotatedRect UnClip(std::vector<std::vector<float>> box,
const float &unclip_ratio);
float **Mat2Vec(cv::Mat mat);
std::vector<std::vector<int>>
OrderPointsClockwise(std::vector<std::vector<int>> pts);
std::vector<std::vector<float>> GetMiniBoxes(cv::RotatedRect box,
float &ssid);
float BoxScoreFast(std::vector<std::vector<float>> box_array, cv::Mat pred);
float PolygonScoreAcc(std::vector<cv::Point> contour, cv::Mat pred);
std::vector<std::vector<std::vector<int>>>
BoxesFromBitmap(const cv::Mat pred, const cv::Mat bitmap,
const float &box_thresh, const float &det_db_unclip_ratio,
const std::string &det_db_score_mode);
std::vector<std::vector<std::vector<int>>>
FilterTagDetRes(std::vector<std::vector<std::vector<int>>> boxes,
float ratio_h, float ratio_w, cv::Mat srcimg);
private:
static bool XsortInt(std::vector<int> a, std::vector<int> b);
static bool XsortFp32(std::vector<float> a, std::vector<float> b);
std::vector<std::vector<float>> Mat2Vector(cv::Mat mat);
inline int _max(int a, int b) { return a >= b ? a : b; }
inline int _min(int a, int b) { return a >= b ? b : a; }
template <class T> inline T clamp(T x, T min, T max) {
if (x > max)
return max;
if (x < min)
return min;
return x;
}
inline float clampf(float x, float min, float max) {
if (x > max)
return max;
if (x < min)
return min;
return x;
}
};
class TablePostProcessor {
public:
void init(std::string label_path, bool merge_no_span_structure = true);
void Run(std::vector<float> &loc_preds, std::vector<float> &structure_probs,
std::vector<float> &rec_scores, ov::Shape &loc_preds_shape,
ov::Shape &structure_probs_shape,
std::vector<std::vector<std::string>> &rec_html_tag_batch,
std::vector<std::vector<std::vector<int>>> &rec_boxes_batch,
std::vector<int> &width_list, std::vector<int> &height_list);
private:
std::vector<std::string> label_list_;
std::string end = "eos";
std::string beg = "sos";
};
class PicodetPostProcessor {
public:
void init(std::string label_path, const double score_threshold = 0.4,
const double nms_threshold = 0.5,
const std::vector<int> &fpn_stride = {8, 16, 32, 64});
void Run(std::vector<StructurePredictResult> &results,
std::vector<std::vector<float>> outs, std::vector<int> ori_shape,
std::vector<int> resize_shape, int eg_max);
std::vector<int> fpn_stride_ = {8, 16, 32, 64};
private:
StructurePredictResult disPred2Bbox(std::vector<float> bbox_pred, int label,
float score, int x, int y, int stride,
std::vector<int> im_shape, int reg_max);
void nms(std::vector<StructurePredictResult> &input_boxes,
float nms_threshold);
std::vector<std::string> label_list_;
double score_threshold_ = 0.4;
double nms_threshold_ = 0.5;
int num_class_ = 5;
};
} // namespace PaddleOCR

View File

@@ -0,0 +1,80 @@
// 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.
#pragma once
#include <iostream>
#include <vector>
#include "opencv2/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
namespace PaddleOCR {
class Normalize {
public:
virtual void Run(cv::Mat *im, const std::vector<float> &mean,
const std::vector<float> &scale, const bool is_scale = true);
};
// RGB -> CHW
class Permute {
public:
virtual void Run(const cv::Mat *im, float *data);
};
class PermuteBatch {
public:
virtual void Run(const std::vector<cv::Mat> imgs, float *data);
};
class ResizeImgType0 {
public:
virtual void Run(const cv::Mat &img, cv::Mat &resize_img,
std::string limit_type, int limit_side_len, float &ratio_h,
float &ratio_w);
};
class CrnnResizeImg {
public:
virtual void Run(const cv::Mat &img, cv::Mat &resize_img, float wh_ratio,
const std::vector<int> &rec_image_shape = {3, 32, 320});
};
class ClsResizeImg {
public:
virtual void Run(const cv::Mat &img, cv::Mat &resize_img,
const std::vector<size_t> &rec_image_shape = {3, 48, 192});
};
class TableResizeImg {
public:
virtual void Run(const cv::Mat &img, cv::Mat &resize_img,
const int max_len = 488);
};
class TablePadImg {
public:
virtual void Run(const cv::Mat &img, cv::Mat &resize_img,
const int max_len = 488);
};
class Resize {
public:
virtual void Run(const cv::Mat &img, cv::Mat &resize_img, const int h,
const int w);
};
} // namespace PaddleOCR

View File

@@ -0,0 +1,50 @@
#include "opencv2/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include <chrono>
#include <iomanip>
#include <iostream>
#include <ostream>
#include <vector>
#include <cstring>
#include <fstream>
#include <numeric>
#include <include/paddleocr_utility.h>
#include <include/preprocess_op.h>
#include <include/postprocess_op.h>
#include <openvino/openvino.hpp>
#include <openvino/core/preprocess/pre_post_process.hpp>
namespace PaddleOCR {
class Layout
{
public:
explicit Layout(std::string model_path, std::string layout_dict_path);
void Run(cv::Mat &src_img, std::vector<StructurePredictResult> &structure_result);
private:
ov::InferRequest infer_request;
std::string model_path;
std::shared_ptr<ov::Model> model;
ov::CompiledModel compiled_model;
cv::Mat src_img;
cv::Mat resize_img;
double e = 1.0 / 255.0;
const int layout_img_h_ = 800;
const int layout_img_w_ = 608;
double layout_nms_threshold = 0.5;
double layout_score_threshold = 0.5;
std::vector<float> mean_ = {0.485f, 0.456f, 0.406f};
std::vector<float> scale_ = {0.229f, 0.224f, 0.225f};
// resize
Resize resize_op_;
// post-process
PicodetPostProcessor post_processor_;
};
}

View File

@@ -0,0 +1,56 @@
#include "opencv2/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include <chrono>
#include <iomanip>
#include <iostream>
#include <ostream>
#include <vector>
#include <cstring>
#include <fstream>
#include <numeric>
#include <include/paddleocr_utility.h>
#include <include/preprocess_op.h>
#include <include/postprocess_op.h>
#include <openvino/openvino.hpp>
namespace PaddleOCR {
class Table
{
public:
explicit Table(std::string model_path, const std::string table_char_dict_path);
void Run(std::vector<cv::Mat> img_list,
std::vector<std::vector<std::string>> &structure_html_tags,
std::vector<float> &structure_scores,
std::vector<std::vector<std::vector<int>>> &structure_boxes);
private:
ov::InferRequest infer_request;
std::string model_path;
std::shared_ptr<ov::Model> model;
ov::CompiledModel compiled_model;
cv::Mat src_img;
cv::Mat resize_img;
const std::string table_char_dict_path;
int table_batch_num_ = 1;
int table_max_len_ = 488;
std::vector<float> mean_ = {0.485f, 0.456f, 0.406f};
std::vector<float> scale_ = {1 / 0.229f, 1 / 0.224f, 1 / 0.225f};
bool is_scale_ = true;
// pre-process
TableResizeImg resize_op_;
Normalize normalize_op_;
PermuteBatch permute_op_;
TablePadImg pad_op_;
// post-process
TablePostProcessor post_processor_;
};
}

View File

@@ -0,0 +1,17 @@
#include <gflags/gflags.h>
DEFINE_string(input, "", "Required. Path to image file");
DEFINE_string(type, "", "Required. Task type ('ocr' or 'structure')");
DEFINE_string(output_dir, "./", "Path to output results.");
DEFINE_string(det_model_dir, "", "Path to detection model file");
DEFINE_string(cls_model_dir, "", "Path to classification model file");
DEFINE_string(rec_model_dir, "", "Path to recognition model file");
DEFINE_string(lay_model_dir, "", "Path to layout model file");
DEFINE_string(tab_model_dir, "", "Path to table model file");
DEFINE_string(label_dir, "", "Required. Path to label file");
DEFINE_string(layout_dict_dir,
"/home/ethan/PaddleOCR_OpenVINO_CPP/data/layout_publaynet_dict.txt",
"Path of dictionary.");
DEFINE_string(table_dict_dir,
"/home/ethan/PaddleOCR_OpenVINO_CPP/data/table_structure_dict.txt",
"Path of dictionary.");

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,115 @@
#include "include/ocr_cls.h"
namespace PaddleOCR {
std::string Classifier::GetOpenVINODevice() {
ov::Core core;
std::vector<std::string> available_devices = core.get_available_devices();
// Prioritize devices: NPU > GPU > CPU
std::vector<std::string> priority_devices = { "GPU", "CPU" };
for (const auto& device : priority_devices) {
if (std::find(available_devices.begin(), available_devices.end(), device) != available_devices.end()) {
return device; // Return the first available device based on priority
}
}
return "CPU";
}
Classifier::Classifier(std::string model_path)
{
this->model_path = model_path;
ov::Core core;
this->model = core.read_model(this->model_path);
// dimension of batch size is dynamic
this->model->reshape({ {ov::Dimension(1, 6), cls_image_shape[0], cls_image_shape[1], cls_image_shape[2]} });
// preprocessing API
ov::preprocess::PrePostProcessor prep(this->model);
// declare section of desired application's input format
prep.input().tensor()
.set_layout("NHWC")
.set_color_format(ov::preprocess::ColorFormat::BGR);
// specify actual model layout
prep.input().model()
.set_layout("NCHW");
prep.input().preprocess()
.mean(this->mean_)
.scale(this->scale_);
std::string deviceName = GetOpenVINODevice();
this->model = prep.build();
//core.set_property(deviceName, ov::hint::performance_mode(ov::hint::PerformanceMode::THROUGHPUT));
this->compiled_model = core.compile_model(this->model, deviceName);
this->infer_request = compiled_model.create_infer_request();
}
void Classifier::SetParameters(int cls_batch_num, double cls_thresh) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
this->cls_batch_num_ = cls_batch_num;
this->cls_thresh = cls_thresh;
}
void Classifier::GetParameters(int& cls_batch_num, double& cls_thresh) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
cls_batch_num = this->cls_batch_num_;
cls_thresh = this->cls_thresh;
}
void Classifier::Run(std::vector<cv::Mat> img_list, std::vector<OCRPredictResult>& ocr_results)
{
try {
std::lock_guard<std::recursive_mutex> lock(_mutex);
std::vector<int> cls_labels(img_list.size(), 0);
std::vector<float> cls_scores(img_list.size(), 0);
std::vector<double> cls_times;
auto input_port = this->compiled_model.input();
int img_num = img_list.size();
for (int beg_img_no = 0; beg_img_no < img_num; beg_img_no += this->cls_batch_num_) {
int end_img_no = std::min(img_num, beg_img_no + this->cls_batch_num_);
size_t batch_num = end_img_no - beg_img_no;
std::vector<ov::Tensor> batch_tensors;
ov::Shape intput_shape = { batch_num, cls_image_shape[1], cls_image_shape[2],3 };
for (int ino = beg_img_no; ino < end_img_no; ino++) {
cv::Mat srcimg;
img_list[ino].copyTo(srcimg);
cv::Mat resize_img;
// preprocess
this->resize_op_.Run(srcimg, resize_img, this->cls_image_shape);
resize_img.convertTo(resize_img, CV_32FC3, e);
if (resize_img.cols < cls_image_shape[2]) {
cv::copyMakeBorder(resize_img, resize_img, 0, 0, 0,
cls_image_shape[2] - resize_img.cols,
cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0));
}
// prepare input tensor
ov::Tensor input_tensor(input_port.get_element_type(), intput_shape, (float*)resize_img.data);
batch_tensors.push_back(input_tensor);
}
// set batched input tensors
this->infer_request.set_input_tensors(batch_tensors);
// start inference
//this->infer_request.start_async();
//this->infer_request.wait();
this->infer_request.infer();
// get output tensor
auto output = this->infer_request.get_output_tensor();
const float* out_data = output.data<const float>();
for (size_t batch_idx = 0; batch_idx < output.get_size() / 2; batch_idx++) {
int label = int(
Utility::argmax(&out_data[batch_idx * 2],
&out_data[(batch_idx + 1) * 2]));
float score = float(*std::max_element(
&out_data[batch_idx * 2],
&out_data[(batch_idx + 1) * 2]));
cls_labels[beg_img_no + batch_idx] = label;
cls_scores[beg_img_no + batch_idx] = score;
}
}
for (int i = 0; i < cls_labels.size(); i++) {
ocr_results[i].cls_label = cls_labels[i];
ocr_results[i].cls_score = cls_scores[i];
}
}
catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
}
}

View File

@@ -0,0 +1,124 @@
#include "include/ocr_det.h"
namespace PaddleOCR {
Detector::Detector(std::string model_path)
{
ov::Core core;
this->model_path = model_path;
this->model = core.read_model(this->model_path);
this->model->reshape({ 1, 3, ov::Dimension(32, this->limit_side_len_), ov::Dimension(1, this->limit_side_len_) });
//core.set_property("CPU", ov::hint::performance_mode(ov::hint::PerformanceMode::THROUGHPUT));
this->compiled_model = core.compile_model(this->model, "CPU");
//this->compiled_model = core.compile_model(this->model, "CPU");
this->infer_request = this->compiled_model.create_infer_request();
}
void Detector::SetParameters(std::string limit_type,
std::string det_db_score_mode,
bool is_scale,
double det_db_thresh,
double det_db_box_thresh,
double det_db_unclip_ratio,
bool use_dilation)
{
std::lock_guard<std::recursive_mutex> lock(_mutex);
this->limit_type_ = limit_type;
this->det_db_score_mode_ = det_db_score_mode;
this->is_scale_ = is_scale;
this->det_db_thresh_ = det_db_thresh;
this->det_db_box_thresh_ = det_db_box_thresh;
this->det_db_unclip_ratio_ = det_db_unclip_ratio;
this->use_dilation_ = use_dilation;
}
void Detector::GetParameters(std::string& limit_type,
std::string& det_db_score_mode,
bool& is_scale,
double& det_db_thresh,
double& det_db_box_thresh,
double& det_db_unclip_ratio,
bool& use_dilation)
{
std::lock_guard<std::recursive_mutex> lock(_mutex);
limit_type = this->limit_type_;
det_db_score_mode = this->det_db_score_mode_;
is_scale = this->is_scale_;
det_db_thresh = this->det_db_thresh_;
det_db_box_thresh = this->det_db_box_thresh_;
det_db_unclip_ratio = this->det_db_unclip_ratio_;
use_dilation = this->use_dilation_;
}
void Detector::Run(const cv::Mat& src_img, std::vector<OCRPredictResult>& ocr_results)
{
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
this->src_img = src_img;
this->resize_op_.Run(this->src_img, this->resize_img, this->limit_type_,
this->limit_side_len_, this->ratio_h, this->ratio_w);
this->normalize_op_.Run(&resize_img, this->mean_, this->scale_,
this->is_scale_);
std::vector<float> input(1 * 3 * resize_img.rows * resize_img.cols, 0.0f);
ov::Shape intput_shape = { 1, 3, (size_t)resize_img.rows, (size_t)resize_img.cols };
this->permute_op_.Run(&resize_img, input.data());
std::vector<std::vector<std::vector<int>>> boxes;
auto input_port = this->compiled_model.input();
// -------- set input --------
ov::Tensor input_tensor(input_port.get_element_type(), intput_shape, input.data());
this->infer_request.set_input_tensor(input_tensor);
// -------- start inference --------
/* this->infer_request.start_async();
this->infer_request.wait();*/
this->infer_request.infer();
auto output = this->infer_request.get_output_tensor(0);
const float* out_data = output.data<const float>();
ov::Shape output_shape = output.get_shape();
const size_t n2 = output_shape[2];
const size_t n3 = output_shape[3];
const int n = n2 * n3;
std::vector<float> pred(n, 0.0);
std::vector<unsigned char> cbuf(n, ' ');
for (int i = 0; i < n; i++) {
pred[i] = float(out_data[i]);
cbuf[i] = (unsigned char)((out_data[i]) * 255);
}
cv::Mat cbuf_map(n2, n3, CV_8UC1, (unsigned char*)cbuf.data());
cv::Mat pred_map(n2, n3, CV_32F, (float*)pred.data());
const double threshold = this->det_db_thresh_ * 255;
const double maxvalue = 255;
cv::Mat bit_map;
cv::threshold(cbuf_map, bit_map, threshold, maxvalue, cv::THRESH_BINARY);
if (this->use_dilation_) {
cv::Mat dila_ele =
cv::getStructuringElement(cv::MORPH_RECT, cv::Size(2, 2));
cv::dilate(bit_map, bit_map, dila_ele);
}
boxes = post_processor_.BoxesFromBitmap(
pred_map, bit_map, this->det_db_box_thresh_, this->det_db_unclip_ratio_,
this->det_db_score_mode_);
boxes = post_processor_.FilterTagDetRes(boxes, this->ratio_h, this->ratio_w, this->src_img);
for (int i = 0; i < boxes.size(); i++) {
OCRPredictResult res;
res.box = boxes[i];
ocr_results.push_back(res);
}
// sort boex from top to bottom, from left to right
Utility::sorted_boxes(ocr_results);
}
catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
}
}

View File

@@ -0,0 +1,130 @@
#include "include/ocr_rec.h"
using namespace std;
namespace PaddleOCR {
Recognizer::Recognizer(string model_path, const string& label_path) {
ov::Core core;
this->model_path = model_path;
this->model = core.read_model(this->model_path);
// reshape the model for dynamic batch size and sentence width
this->model->reshape({ {ov::Dimension(1, 6), this->rec_image_shape_[0], this->rec_image_shape_[1], -1} });
//core.set_property("CPU", ov::hint::performance_mode(ov::hint::PerformanceMode::THROUGHPUT));
this->compiled_model = core.compile_model(this->model, "CPU");
//this->compiled_model = core.compile_model(this->model, "CPU");
this->infer_request = this->compiled_model.create_infer_request();
this->label_list_ = Utility::ReadDict(label_path);
this->label_list_.insert(this->label_list_.begin(),
"#"); // blank char for ctc
this->label_list_.push_back(" ");
}
void Recognizer::SetParameters(int rec_batch_num) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
this->rec_batch_num_ = rec_batch_num;
}
void Recognizer::GetParameters(int& rec_batch_num) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
rec_batch_num = this->rec_batch_num_;
}
void Recognizer::Run(const std::vector<cv::Mat> &img_list, std::vector<OCRPredictResult>& ocr_results) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
std::vector<std::string> rec_texts(img_list.size(), "");
std::vector<float> rec_text_scores(img_list.size(), 0);
int img_num = img_list.size();
std::vector<float> width_list;
for (int i = 0; i < img_num; i++) {
width_list.push_back(float(img_list[i].cols) / img_list[i].rows);
}
std::vector<int> indices = Utility::argsort(width_list);
for (int beg_img_no = 0; beg_img_no < img_num;
beg_img_no += this->rec_batch_num_) {
int end_img_no = std::min(img_num, beg_img_no + this->rec_batch_num_);
size_t batch_num = end_img_no - beg_img_no;
size_t imgH = this->rec_image_shape_[1];
size_t imgW = this->rec_image_shape_[2];
float max_wh_ratio = imgW * 1.0 / imgH;
for (int ino = beg_img_no; ino < end_img_no; ino++) {
int h = img_list[indices[ino]].rows;
int w = img_list[indices[ino]].cols;
float wh_ratio = w * 1.0 / h;
max_wh_ratio = std::max(max_wh_ratio, wh_ratio);
}
int batch_width = imgW;
std::vector<cv::Mat> norm_img_batch;
for (int ino = beg_img_no; ino < end_img_no; ino++) {
cv::Mat srcimg;
img_list[indices[ino]].copyTo(srcimg);
cv::Mat resize_img;
// preprocess
this->resize_op_.Run(srcimg, resize_img, max_wh_ratio, this->rec_image_shape_);
this->normalize_op_.Run(&resize_img, this->mean_, this->scale_,
this->is_scale_);
norm_img_batch.push_back(resize_img);
batch_width = std::max(resize_img.cols, batch_width);
}
// prepare input tensor
std::vector<float> input(batch_num * 3 * imgH * batch_width, 0.0f);
ov::Shape intput_shape = { batch_num, 3, imgH, (size_t)batch_width };
this->permute_op_.Run(norm_img_batch, input.data());
auto input_port = this->compiled_model.input();
ov::Tensor input_tensor(input_port.get_element_type(), intput_shape, input.data());
this->infer_request.set_input_tensor(input_tensor);
// start inference
/* this->infer_request.start_async();
this->infer_request.wait();*/
this->infer_request.infer();
auto output = this->infer_request.get_output_tensor();
const float* out_data = output.data<const float>();
auto predict_shape = output.get_shape();
// predict_batch is the result of Last FC with softmax
for (int m = 0; m < predict_shape[0]; m++) {
std::string str_res;
int argmax_idx;
int last_index = 0;
float score = 0.f;
int count = 0;
float max_value = 0.0f;
for (int n = 0; n < predict_shape[1]; n++) {
// get idx
argmax_idx = int(Utility::argmax(
&out_data[(m * predict_shape[1] + n) * predict_shape[2]],
&out_data[(m * predict_shape[1] + n + 1) * predict_shape[2]]));
// get score
max_value = float(*std::max_element(
&out_data[(m * predict_shape[1] + n) * predict_shape[2]],
&out_data[(m * predict_shape[1] + n + 1) * predict_shape[2]]));
if (argmax_idx > 0 && (!(n > 0 && argmax_idx == last_index))) {
score += max_value;
count += 1;
str_res += this->label_list_[argmax_idx];
}
last_index = argmax_idx;
}
score /= count;
if (std::isnan(score)) {
continue;
}
rec_texts[indices[beg_img_no + m]] = str_res;
rec_text_scores[indices[beg_img_no + m]] = score;
}
}
// sort boex from top to bottom, from left to right
for (int i = 0; i < rec_texts.size(); i++) {
ocr_results[i].text = rec_texts[i];
ocr_results[i].score = rec_text_scores[i];
}
}
catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
}
}

View File

@@ -0,0 +1,192 @@
// 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 <include/paddleocr.h>
namespace PaddleOCR {
PPOCR::PPOCR() {
this->_limit_type = "max";
this->_det_db_score_mode = "slow";
this->_is_scale = true;
this->_det_db_thresh = 0.3;
this->_det_db_box_thresh = 0.6;
this->_det_db_unclip_ratio = 1.5;
this->_use_dilation = false;
this->_cls_batch_num = 1;
this->_cls_thresh = 0.9;
this->_rec_batch_num = 1;
};
bool PPOCR::Initialize(std::string detectionModelDir, std::string classifierModelDir, std::string recognizerModelDir, std::string labelDir) {
this->detector_ = std::make_unique<Detector>(detectionModelDir);
if (!classifierModelDir.empty()) {
this->classifier_ = std::make_unique<Classifier>(classifierModelDir);
}
this->recognizer_ = std::make_unique<Recognizer>(recognizerModelDir, labelDir);
if (detector_) detector_->SetParameters(_limit_type, _det_db_score_mode, _is_scale, _det_db_thresh, _det_db_box_thresh, _det_db_unclip_ratio, _use_dilation);
if (classifier_) classifier_->SetParameters(_cls_batch_num, _cls_thresh);
if (recognizer_) recognizer_->SetParameters(_rec_batch_num);
return true;
}
void PPOCR::SetParameters(std::string limit_type,
std::string det_db_score_mode,
bool is_scale,
double det_db_thresh,
double det_db_box_thresh,
double det_db_unclip_ratio,
bool use_dilation,
int cls_batch_num,
double cls_thresh,
int rec_batch_num)
{
std::lock_guard<std::recursive_mutex> lock(_mutex);
this->_limit_type = limit_type;
this->_det_db_score_mode = det_db_score_mode;
this->_is_scale = is_scale;
this->_det_db_thresh = det_db_thresh;
this->_det_db_box_thresh = det_db_box_thresh;
this->_det_db_unclip_ratio = det_db_unclip_ratio;
this->_use_dilation = use_dilation;
this->_cls_batch_num = cls_batch_num;
this->_cls_thresh = cls_thresh;
this->_rec_batch_num = rec_batch_num;
if (detector_) detector_->SetParameters(limit_type, det_db_score_mode, is_scale, det_db_thresh, det_db_box_thresh, det_db_unclip_ratio, use_dilation);
if (classifier_) classifier_->SetParameters(cls_batch_num, cls_thresh);
if (recognizer_) recognizer_->SetParameters(rec_batch_num);
}
void PPOCR::GetParameters(std::string& limit_type,
std::string& det_db_score_mode,
bool& is_scale,
double& det_db_thresh,
double& det_db_box_thresh,
double& det_db_unclip_ratio,
bool& use_dilation,
int& cls_batch_num,
double& cls_thresh,
int& rec_batch_num)
{
std::lock_guard<std::recursive_mutex> lock(_mutex);
if (detector_) detector_->GetParameters(limit_type, det_db_score_mode, is_scale, det_db_thresh, det_db_box_thresh, det_db_unclip_ratio, use_dilation);
if (classifier_) classifier_->GetParameters(cls_batch_num, cls_thresh);
if (recognizer_) recognizer_->GetParameters(rec_batch_num);
}
//std::vector<OCRPredictResult> PPOCR::ocr(cv::Mat img)
//{
// std::lock_guard<std::recursive_mutex> lock(_mutex);
// try {
// std::vector<OCRPredictResult> ocr_result;
// // detect the sentence in input image
// this->detector_->Run(img, ocr_result);
// // crop image
// std::vector<cv::Mat> img_list;
// for (int j = 0; j < ocr_result.size(); j++) {
// cv::Mat crop_img;
// crop_img = Utility::GetRotateCropImage(img, ocr_result[j].box);
// img_list.push_back(crop_img);
// }
// if (this->classifier_ != nullptr) {
// // find the reversed sentence and flip it
// this->classifier_->Run(img_list, ocr_result);
// for (int i = 0; i < img_list.size(); i++) {
// if (ocr_result[i].cls_label % 2 == 1 &&
// ocr_result[i].cls_score > _cls_thresh) {
// cv::rotate(img_list[i], img_list[i], 1);
// }
// }
// }
// // recognize the words in sentence and print them
// this->recognizer_->Run(img_list, ocr_result);
// return ocr_result;
// }
// catch (const std::exception& e) {
// std::cerr << e.what() << std::endl;
// return std::vector<OCRPredictResult>();
// }
//}
std::vector<OCRPredictResult> PPOCR::ocr(const cv::Mat& img) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
std::vector<OCRPredictResult> ocr_result;
try {
if (img.empty()) {
std::cerr << "[PPOCR] Input image is empty!" << std::endl;
return ocr_result;
}
if (!this->detector_ || !this->recognizer_) {
std::cerr << "[PPOCR] Detector or recognizer not initialized!" << std::endl;
return ocr_result;
}
// Run detector
this->detector_->Run(img, ocr_result);
// Crop each detected region
std::vector<cv::Mat> img_list;
for (const auto& result : ocr_result) {
try {
cv::Mat crop_img = Utility::GetRotateCropImage(img, result.box);
img_list.push_back(crop_img);
}
catch (const std::exception& e) {
std::cerr << "[PPOCR] Error cropping region: " << e.what() << std::endl;
img_list.push_back(cv::Mat()); // Push empty mat to preserve indexing
}
}
// Run classifier if available
if (this->classifier_) {
try {
this->classifier_->Run(img_list, ocr_result);
for (size_t i = 0; i < img_list.size() && i < ocr_result.size(); ++i) {
if (!img_list[i].empty() &&
(ocr_result[i].cls_label % 2 == 1) &&
(ocr_result[i].cls_score > _cls_thresh)) {
cv::rotate(img_list[i], img_list[i], cv::ROTATE_180); // same as rotate(img, img, 1)
}
}
}
catch (const std::exception& e) {
std::cerr << "[PPOCR] Classifier error: " << e.what() << std::endl;
}
}
// Run recognizer
try {
this->recognizer_->Run(img_list, ocr_result);
}
catch (const std::exception& e) {
std::cerr << "[PPOCR] Recognizer error: " << e.what() << std::endl;
}
}
catch (const std::exception& e) {
std::cerr << "[PPOCR] General exception: " << e.what() << std::endl;
}
catch (...) {
std::cerr << "[PPOCR] Unknown exception occurred!" << std::endl;
}
return ocr_result;
}
PPOCR::~PPOCR() {
if (detector_) detector_.reset();
if (classifier_) classifier_.reset();
if (recognizer_) recognizer_.reset();
}
} // namespace PaddleOCR

View File

@@ -0,0 +1,431 @@
// 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 <dirent.h>
#include <include/paddleocr_utility.h>
#include <iostream>
#include <ostream>
#include <vector>
#ifdef _WIN32
#include <direct.h>
#else
#include <sys/stat.h>
#endif
namespace PaddleOCR {
std::vector<std::string> Utility::ReadDict(const std::string& path) {
std::ifstream in(path);
std::string line;
std::vector<std::string> m_vec;
if (in) {
while (getline(in, line)) {
m_vec.push_back(line);
}
}
else {
std::cout << "no such label file: " << path << ", exit the program..."
<< std::endl;
exit(1);
}
return m_vec;
}
void Utility::VisualizeBboxes(const cv::Mat& srcimg,
const std::vector<OCRPredictResult>& ocr_result,
const std::string& save_path) {
cv::Mat img_vis;
srcimg.copyTo(img_vis);
for (int n = 0; n < ocr_result.size(); n++) {
cv::Point rook_points[4];
for (int m = 0; m < ocr_result[n].box.size(); m++) {
rook_points[m] =
cv::Point(int(ocr_result[n].box[m][0]), int(ocr_result[n].box[m][1]));
}
const cv::Point* ppt[1] = { rook_points };
int npt[] = { 4 };
cv::polylines(img_vis, ppt, npt, 1, 1, CV_RGB(0, 255, 0), 2, 8, 0);
}
cv::imwrite(save_path, img_vis);
std::cout << "The detection visualized image saved in " + save_path
<< std::endl;
}
void Utility::VisualizeBboxes(const cv::Mat& srcimg,
const StructurePredictResult& structure_result,
const std::string& save_path) {
cv::Mat img_vis;
srcimg.copyTo(img_vis);
img_vis = crop_image(img_vis, structure_result.box);
for (int n = 0; n < structure_result.cell_box.size(); n++) {
if (structure_result.cell_box[n].size() == 8) {
cv::Point rook_points[4];
for (int m = 0; m < structure_result.cell_box[n].size(); m += 2) {
rook_points[m / 2] =
cv::Point(int(structure_result.cell_box[n][m]),
int(structure_result.cell_box[n][m + 1]));
}
const cv::Point* ppt[1] = { rook_points };
int npt[] = { 4 };
cv::polylines(img_vis, ppt, npt, 1, 1, CV_RGB(0, 255, 0), 2, 8, 0);
}
else if (structure_result.cell_box[n].size() == 4) {
cv::Point rook_points[2];
rook_points[0] = cv::Point(int(structure_result.cell_box[n][0]),
int(structure_result.cell_box[n][1]));
rook_points[1] = cv::Point(int(structure_result.cell_box[n][2]),
int(structure_result.cell_box[n][3]));
cv::rectangle(img_vis, rook_points[0], rook_points[1], CV_RGB(0, 255, 0),
2, 8, 0);
}
}
cv::imwrite(save_path, img_vis);
std::cout << "The table visualized image saved in " + save_path << std::endl;
}
// list all files under a directory
void Utility::GetAllFiles(const char* dir_name,
std::vector<std::string>& all_inputs) {
if (NULL == dir_name) {
std::cout << " dir_name is null ! " << std::endl;
return;
}
struct stat s;
stat(dir_name, &s);
if (!S_ISDIR(s.st_mode)) {
std::cout << "dir_name is not a valid directory !" << std::endl;
all_inputs.push_back(dir_name);
return;
}
else {
struct dirent* filename; // return value for readdir()
DIR* dir; // return value for opendir()
dir = opendir(dir_name);
if (NULL == dir) {
std::cout << "Can not open dir " << dir_name << std::endl;
return;
}
std::cout << "Successfully opened the dir !" << std::endl;
while ((filename = readdir(dir)) != NULL) {
if (strcmp(filename->d_name, ".") == 0 ||
strcmp(filename->d_name, "..") == 0)
continue;
// img_dir + std::string("/") + all_inputs[0];
all_inputs.push_back(dir_name + std::string("/") +
std::string(filename->d_name));
}
}
}
cv::Mat Utility::GetRotateCropImage(const cv::Mat& srcimage,
std::vector<std::vector<int>> box) {
cv::Mat image;
srcimage.copyTo(image);
std::vector<std::vector<int>> points = box;
int x_collect[4] = { box[0][0], box[1][0], box[2][0], box[3][0] };
int y_collect[4] = { box[0][1], box[1][1], box[2][1], box[3][1] };
int left = int(*std::min_element(x_collect, x_collect + 4));
int right = int(*std::max_element(x_collect, x_collect + 4));
int top = int(*std::min_element(y_collect, y_collect + 4));
int bottom = int(*std::max_element(y_collect, y_collect + 4));
cv::Mat img_crop;
image(cv::Rect(left, top, right - left, bottom - top)).copyTo(img_crop);
for (int i = 0; i < points.size(); i++) {
points[i][0] -= left;
points[i][1] -= top;
}
int img_crop_width = int(sqrt(pow(points[0][0] - points[1][0], 2) +
pow(points[0][1] - points[1][1], 2)));
int img_crop_height = int(sqrt(pow(points[0][0] - points[3][0], 2) +
pow(points[0][1] - points[3][1], 2)));
cv::Point2f pts_std[4];
pts_std[0] = cv::Point2f(0., 0.);
pts_std[1] = cv::Point2f(img_crop_width, 0.);
pts_std[2] = cv::Point2f(img_crop_width, img_crop_height);
pts_std[3] = cv::Point2f(0.f, img_crop_height);
cv::Point2f pointsf[4];
pointsf[0] = cv::Point2f(points[0][0], points[0][1]);
pointsf[1] = cv::Point2f(points[1][0], points[1][1]);
pointsf[2] = cv::Point2f(points[2][0], points[2][1]);
pointsf[3] = cv::Point2f(points[3][0], points[3][1]);
cv::Mat M = cv::getPerspectiveTransform(pointsf, pts_std);
cv::Mat dst_img;
cv::warpPerspective(img_crop, dst_img, M,
cv::Size(img_crop_width, img_crop_height),
cv::BORDER_REPLICATE);
if (float(dst_img.rows) >= float(dst_img.cols) * 1.5) {
cv::Mat srcCopy = cv::Mat(dst_img.rows, dst_img.cols, dst_img.depth());
cv::transpose(dst_img, srcCopy);
cv::flip(srcCopy, srcCopy, 0);
return srcCopy;
}
else {
return dst_img;
}
}
std::vector<int> Utility::argsort(const std::vector<float>& array) {
const int array_len(array.size());
std::vector<int> array_index(array_len, 0);
for (int i = 0; i < array_len; ++i)
array_index[i] = i;
std::sort(
array_index.begin(), array_index.end(),
[&array](int pos1, int pos2) { return (array[pos1] < array[pos2]); });
return array_index;
}
std::string Utility::basename(const std::string& filename) {
if (filename.empty()) {
return "";
}
auto len = filename.length();
auto index = filename.find_last_of("/\\");
if (index == std::string::npos) {
return filename;
}
if (index + 1 >= len) {
len--;
index = filename.substr(0, len).find_last_of("/\\");
if (len == 0) {
return filename;
}
if (index == 0) {
return filename.substr(1, len - 1);
}
if (index == std::string::npos) {
return filename.substr(0, len);
}
return filename.substr(index + 1, len - index - 1);
}
return filename.substr(index + 1, len - index);
}
bool Utility::PathExists(const std::string& path) {
#ifdef _WIN32
struct _stat buffer;
return (_stat(path.c_str(), &buffer) == 0);
#else
struct stat buffer;
return (stat(path.c_str(), &buffer) == 0);
#endif // !_WIN32
}
void Utility::CreateDir(const std::string& path) {
#ifdef _WIN32
_mkdir(path.c_str());
#else
mkdir(path.c_str(), 0777);
#endif // !_WIN32
}
void Utility::print_result(const std::vector<OCRPredictResult>& ocr_result) {
for (int i = 0; i < ocr_result.size(); i++) {
std::cout << i << "\t";
// det
std::vector<std::vector<int>> boxes = ocr_result[i].box;
if (boxes.size() > 0) {
std::cout << "det boxes: [";
for (int n = 0; n < boxes.size(); n++) {
std::cout << '[' << boxes[n][0] << ',' << boxes[n][1] << "]";
if (n != boxes.size() - 1) {
std::cout << ',';
}
}
std::cout << "] ";
}
// rec
if (ocr_result[i].score != -1.0) {
std::cout << "rec text: " << ocr_result[i].text
<< " rec score: " << ocr_result[i].score << " ";
}
// cls
if (ocr_result[i].cls_label != -1) {
std::cout << "cls label: " << ocr_result[i].cls_label
<< " cls score: " << ocr_result[i].cls_score;
}
std::cout << std::endl;
}
}
cv::Mat Utility::crop_image(cv::Mat& img, const std::vector<int>& box) {
cv::Mat crop_im;
int crop_x1 = std::max(0, box[0]);
int crop_y1 = std::max(0, box[1]);
int crop_x2 = std::min(img.cols - 1, box[2] - 1);
int crop_y2 = std::min(img.rows - 1, box[3] - 1);
crop_im = cv::Mat::zeros(box[3] - box[1], box[2] - box[0], 16);
cv::Mat crop_im_window =
crop_im(cv::Range(crop_y1 - box[1], crop_y2 + 1 - box[1]),
cv::Range(crop_x1 - box[0], crop_x2 + 1 - box[0]));
cv::Mat roi_img =
img(cv::Range(crop_y1, crop_y2 + 1), cv::Range(crop_x1, crop_x2 + 1));
crop_im_window += roi_img;
return crop_im;
}
cv::Mat Utility::crop_image(cv::Mat& img, const std::vector<float>& box) {
std::vector<int> box_int = { (int)box[0], (int)box[1], (int)box[2],
(int)box[3] };
return crop_image(img, box_int);
}
void Utility::sorted_boxes(std::vector<OCRPredictResult>& ocr_result) {
std::sort(ocr_result.begin(), ocr_result.end(), Utility::comparison_box);
if (ocr_result.size() > 0) {
for (int i = 0; i < ocr_result.size() - 1; i++) {
for (int j = i; j > 0; j--) {
if (abs(ocr_result[j + 1].box[0][1] - ocr_result[j].box[0][1]) < 10 &&
(ocr_result[j + 1].box[0][0] < ocr_result[j].box[0][0])) {
std::swap(ocr_result[i], ocr_result[i + 1]);
}
}
}
}
}
std::vector<int> Utility::xyxyxyxy2xyxy(std::vector<std::vector<int>>& box) {
int x_collect[4] = { box[0][0], box[1][0], box[2][0], box[3][0] };
int y_collect[4] = { box[0][1], box[1][1], box[2][1], box[3][1] };
int left = int(*std::min_element(x_collect, x_collect + 4));
int right = int(*std::max_element(x_collect, x_collect + 4));
int top = int(*std::min_element(y_collect, y_collect + 4));
int bottom = int(*std::max_element(y_collect, y_collect + 4));
std::vector<int> box1(4, 0);
box1[0] = left;
box1[1] = top;
box1[2] = right;
box1[3] = bottom;
return box1;
}
std::vector<int> Utility::xyxyxyxy2xyxy(std::vector<int>& box) {
int x_collect[4] = { box[0], box[2], box[4], box[6] };
int y_collect[4] = { box[1], box[3], box[5], box[7] };
int left = int(*std::min_element(x_collect, x_collect + 4));
int right = int(*std::max_element(x_collect, x_collect + 4));
int top = int(*std::min_element(y_collect, y_collect + 4));
int bottom = int(*std::max_element(y_collect, y_collect + 4));
std::vector<int> box1(4, 0);
box1[0] = left;
box1[1] = top;
box1[2] = right;
box1[3] = bottom;
return box1;
}
float Utility::fast_exp(float x) {
union {
uint32_t i;
float f;
} v{};
v.i = (1 << 23) * (1.4426950409 * x + 126.93490512f);
return v.f;
}
std::vector<float>
Utility::activation_function_softmax(std::vector<float>& src) {
int length = src.size();
std::vector<float> dst;
dst.resize(length);
const float alpha = float(*std::max_element(&src[0], &src[0 + length]));
float denominator{ 0 };
for (int i = 0; i < length; ++i) {
dst[i] = fast_exp(src[i] - alpha);
denominator += dst[i];
}
for (int i = 0; i < length; ++i) {
dst[i] /= denominator;
}
return dst;
}
float Utility::iou(std::vector<int>& box1, std::vector<int>& box2) {
int area1 = std::max(0, box1[2] - box1[0]) * std::max(0, box1[3] - box1[1]);
int area2 = std::max(0, box2[2] - box2[0]) * std::max(0, box2[3] - box2[1]);
// computing the sum_area
int sum_area = area1 + area2;
// find the each point of intersect rectangle
int x1 = std::max(box1[0], box2[0]);
int y1 = std::max(box1[1], box2[1]);
int x2 = std::min(box1[2], box2[2]);
int y2 = std::min(box1[3], box2[3]);
// judge if there is an intersect
if (y1 >= y2 || x1 >= x2) {
return 0.0;
}
else {
int intersect = (x2 - x1) * (y2 - y1);
return intersect / (sum_area - intersect + 0.00000001);
}
}
float Utility::iou(std::vector<float>& box1, std::vector<float>& box2) {
float area1 = std::max((float)0.0, box1[2] - box1[0]) *
std::max((float)0.0, box1[3] - box1[1]);
float area2 = std::max((float)0.0, box2[2] - box2[0]) *
std::max((float)0.0, box2[3] - box2[1]);
// computing the sum_area
float sum_area = area1 + area2;
// find the each point of intersect rectangle
float x1 = std::max(box1[0], box2[0]);
float y1 = std::max(box1[1], box2[1]);
float x2 = std::min(box1[2], box2[2]);
float y2 = std::min(box1[3], box2[3]);
// judge if there is an intersect
if (y1 >= y2 || x1 >= x2) {
return 0.0;
}
else {
float intersect = (x2 - x1) * (y2 - y1);
return intersect / (sum_area - intersect + 0.00000001);
}
}
} // namespace PaddleOCR

View File

@@ -0,0 +1,200 @@
// 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 <include/paddlestructure.h>
namespace PaddleOCR {
PaddleStructure::PaddleStructure() {
};
bool PaddleStructure::Initialize(std::string layModelDir, std::string layModelDic, std::string tabModelDir, std::string tabModelDic) {
this->layout_model_ = new Layout(layModelDir, layModelDic);
this->table_model_ = new Table(tabModelDir, tabModelDic);
return true;
}
std::vector<StructurePredictResult> PaddleStructure::structure(cv::Mat src_img) {
std::vector<StructurePredictResult> structure_results;
this->layout_model_->Run(src_img, structure_results);
cv::Mat roi_img;
for (int i = 0; i < structure_results.size(); i++) {
// crop image
roi_img = Utility::crop_image(src_img, structure_results[i].box);
if (structure_results[i].type == "table") {
std::vector<std::vector<std::string>> structure_html_tags;
std::vector<float> structure_scores(1, 0);
std::vector<std::vector<std::vector<int>>> structure_boxes;
std::vector<cv::Mat> img_list;
img_list.push_back(roi_img);
this->table_model_->Run(img_list, structure_html_tags, structure_scores, structure_boxes);
std::vector<OCRPredictResult> ocr_result;
std::string html;
int expand_pixel = 3;
for (int j = 0; j < img_list.size(); j++) {
this->detector_->Run(img_list[j], ocr_result);
// crop image
std::vector<cv::Mat> rec_img_list;
std::vector<int> ocr_box;
for (int k = 0; k < ocr_result.size(); k++) {
ocr_box = Utility::xyxyxyxy2xyxy(ocr_result[k].box);
ocr_box[0] = std::max(0, ocr_box[0] - expand_pixel);
ocr_box[1] = std::max(0, ocr_box[1] - expand_pixel),
ocr_box[2] = std::min(img_list[j].cols, ocr_box[2] + expand_pixel);
ocr_box[3] = std::min(img_list[j].rows, ocr_box[3] + expand_pixel);
cv::Mat crop_img = Utility::crop_image(img_list[j], ocr_box);
rec_img_list.push_back(crop_img);
}
// rec
this->recognizer_->Run(rec_img_list, ocr_result);
// rebuild table
html = this->rebuild_table(structure_html_tags[j], structure_boxes[j],
ocr_result);
structure_results[i].html = html;
structure_results[i].cell_box = structure_boxes[j];
structure_results[i].html_score = structure_scores[j];
}
}
else {
structure_results[i].text_res = ocr(roi_img);
}
}
return structure_results;
};
std::string
PaddleStructure::rebuild_table(std::vector<std::string> structure_html_tags,
std::vector<std::vector<int>> structure_boxes,
std::vector<OCRPredictResult>& ocr_result) {
// match text in same cell
std::vector<std::vector<std::string>> matched(structure_boxes.size(),
std::vector<std::string>());
std::vector<int> ocr_box;
std::vector<int> structure_box;
for (int i = 0; i < ocr_result.size(); i++) {
ocr_box = Utility::xyxyxyxy2xyxy(ocr_result[i].box);
ocr_box[0] -= 1;
ocr_box[1] -= 1;
ocr_box[2] += 1;
ocr_box[3] += 1;
std::vector<std::vector<float>> dis_list(structure_boxes.size(),
std::vector<float>(3, 100000.0));
for (int j = 0; j < structure_boxes.size(); j++) {
if (structure_boxes[i].size() == 8) {
structure_box = Utility::xyxyxyxy2xyxy(structure_boxes[j]);
}
else {
structure_box = structure_boxes[j];
}
dis_list[j][0] = this->dis(ocr_box, structure_box);
dis_list[j][1] = 1 - Utility::iou(ocr_box, structure_box);
dis_list[j][2] = j;
}
// find min dis idx
std::sort(dis_list.begin(), dis_list.end(),
PaddleStructure::comparison_dis);
matched[dis_list[0][2]].push_back(ocr_result[i].text);
}
// get pred html
std::string html_str = "";
int td_tag_idx = 0;
for (int i = 0; i < structure_html_tags.size(); i++) {
if (structure_html_tags[i].find("</td>") != std::string::npos) {
if (structure_html_tags[i].find("<td></td>") != std::string::npos) {
html_str += "<td>";
}
if (matched[td_tag_idx].size() > 0) {
bool b_with = false;
if (matched[td_tag_idx][0].find("<b>") != std::string::npos &&
matched[td_tag_idx].size() > 1) {
b_with = true;
html_str += "<b>";
}
for (int j = 0; j < matched[td_tag_idx].size(); j++) {
std::string content = matched[td_tag_idx][j];
if (matched[td_tag_idx].size() > 1) {
// remove blank, <b> and </b>
if (content.length() > 0 && content.at(0) == ' ') {
content = content.substr(0);
}
if (content.length() > 2 && content.substr(0, 3) == "<b>") {
content = content.substr(3);
}
if (content.length() > 4 &&
content.substr(content.length() - 4) == "</b>") {
content = content.substr(0, content.length() - 4);
}
if (content.empty()) {
continue;
}
// add blank
if (j != matched[td_tag_idx].size() - 1 &&
content.at(content.length() - 1) != ' ') {
content += ' ';
}
}
html_str += content;
}
if (b_with) {
html_str += "</b>";
}
}
if (structure_html_tags[i].find("<td></td>") != std::string::npos) {
html_str += "</td>";
}
else {
html_str += structure_html_tags[i];
}
td_tag_idx += 1;
}
else {
html_str += structure_html_tags[i];
}
}
return html_str;
}
float PaddleStructure::dis(std::vector<int>& box1, std::vector<int>& box2) {
int x1_1 = box1[0];
int y1_1 = box1[1];
int x2_1 = box1[2];
int y2_1 = box1[3];
int x1_2 = box2[0];
int y1_2 = box2[1];
int x2_2 = box2[2];
int y2_2 = box2[3];
float dis =
abs(x1_2 - x1_1) + abs(y1_2 - y1_1) + abs(x2_2 - x2_1) + abs(y2_2 - y2_1);
float dis_2 = abs(x1_2 - x1_1) + abs(y1_2 - y1_1);
float dis_3 = abs(x2_2 - x2_1) + abs(y2_2 - y2_1);
return dis + std::min(dis_2, dis_3);
}
PaddleStructure::~PaddleStructure() {
if (this->layout_model_ != nullptr) {
delete this->layout_model_;
}
if (this->table_model_ != nullptr) {
delete this->table_model_;
}
};
} // namespace PaddleOCR

View File

@@ -0,0 +1,580 @@
// 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 <include/postprocess_op.h>
namespace PaddleOCR {
void DBPostProcessor::GetContourArea(const std::vector<std::vector<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 DBPostProcessor::UnClip(std::vector<std::vector<float>> box,
const float& unclip_ratio) {
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;
if (points.size() <= 0) {
res = cv::RotatedRect(cv::Point2f(0, 0), cv::Size2f(1, 1), 0);
}
else {
res = cv::minAreaRect(points);
}
return res;
}
float** DBPostProcessor::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;
}
std::vector<std::vector<int>>
DBPostProcessor::OrderPointsClockwise(std::vector<std::vector<int>> pts) {
std::vector<std::vector<int>> box = pts;
std::sort(box.begin(), box.end(), XsortInt);
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;
}
std::vector<std::vector<float>> DBPostProcessor::Mat2Vector(cv::Mat mat) {
std::vector<std::vector<float>> img_vec;
std::vector<float> tmp;
for (int i = 0; i < mat.rows; ++i) {
tmp.clear();
for (int j = 0; j < mat.cols; ++j) {
tmp.push_back(mat.at<float>(i, j));
}
img_vec.push_back(tmp);
}
return img_vec;
}
bool DBPostProcessor::XsortFp32(std::vector<float> a, std::vector<float> b) {
if (a[0] != b[0])
return a[0] < b[0];
return false;
}
bool DBPostProcessor::XsortInt(std::vector<int> a, std::vector<int> b) {
if (a[0] != b[0])
return a[0] < b[0];
return false;
}
std::vector<std::vector<float>>
DBPostProcessor::GetMiniBoxes(cv::RotatedRect box, float& ssid) {
ssid = std::max(box.size.width, box.size.height);
cv::Mat points;
cv::boxPoints(box, points);
auto array = Mat2Vector(points);
std::sort(array.begin(), array.end(), XsortFp32);
std::vector<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;
}
float DBPostProcessor::PolygonScoreAcc(std::vector<cv::Point> contour,
cv::Mat pred) {
int width = pred.cols;
int height = pred.rows;
std::vector<float> box_x;
std::vector<float> box_y;
for (int i = 0; i < contour.size(); ++i) {
box_x.push_back(contour[i].x);
box_y.push_back(contour[i].y);
}
int xmin =
clamp(int(std::floor(*(std::min_element(box_x.begin(), box_x.end())))), 0,
width - 1);
int xmax =
clamp(int(std::ceil(*(std::max_element(box_x.begin(), box_x.end())))), 0,
width - 1);
int ymin =
clamp(int(std::floor(*(std::min_element(box_y.begin(), box_y.end())))), 0,
height - 1);
int ymax =
clamp(int(std::ceil(*(std::max_element(box_y.begin(), box_y.end())))), 0,
height - 1);
cv::Mat mask;
mask = cv::Mat::zeros(ymax - ymin + 1, xmax - xmin + 1, CV_8UC1);
cv::Point* rook_point = new cv::Point[contour.size()];
for (int i = 0; i < contour.size(); ++i) {
rook_point[i] = cv::Point(int(box_x[i]) - xmin, int(box_y[i]) - ymin);
}
const cv::Point* ppt[1] = { rook_point };
int npt[] = { int(contour.size()) };
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);
float score = cv::mean(croppedImg, mask)[0];
delete[] rook_point;
return score;
}
float DBPostProcessor::BoxScoreFast(std::vector<std::vector<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::floor(*(std::min_element(box_x, box_x + 4)))), 0,
width - 1);
int xmax = clamp(int(std::ceil(*(std::max_element(box_x, box_x + 4)))), 0,
width - 1);
int ymin = clamp(int(std::floor(*(std::min_element(box_y, box_y + 4)))), 0,
height - 1);
int ymax = clamp(int(std::ceil(*(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>>> DBPostProcessor::BoxesFromBitmap(
const cv::Mat pred, const cv::Mat bitmap, const float& box_thresh,
const float& det_db_unclip_ratio, const std::string& det_db_score_mode) {
const int min_size = 3;
const int max_candidates = 1000;
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++) {
if (contours[_i].size() <= 2) {
continue;
}
float ssid;
cv::RotatedRect box = cv::minAreaRect(contours[_i]);
auto array = GetMiniBoxes(box, ssid);
auto box_for_unclip = array;
// end get_mini_box
if (ssid < min_size) {
continue;
}
float score;
if (det_db_score_mode == "slow")
/* compute using polygon*/
score = PolygonScoreAcc(contours[_i], pred);
else
score = BoxScoreFast(array, pred);
if (score < box_thresh)
continue;
// start for unclip
cv::RotatedRect points = UnClip(box_for_unclip, det_db_unclip_ratio);
if (points.size.height < 1.001 && points.size.width < 1.001) {
continue;
}
// end for unclip
cv::RotatedRect clipbox = points;
auto cliparray = GetMiniBoxes(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;
}
std::vector<std::vector<std::vector<int>>> DBPostProcessor::FilterTagDetRes(
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] = OrderPointsClockwise(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 <= 4 || rect_height <= 4)
continue;
root_points.push_back(boxes[n]);
}
return root_points;
}
void TablePostProcessor::init(std::string label_path,
bool merge_no_span_structure) {
this->label_list_ = Utility::ReadDict(label_path);
if (merge_no_span_structure) {
this->label_list_.push_back("<td></td>");
std::vector<std::string>::iterator it;
for (it = this->label_list_.begin(); it != this->label_list_.end();) {
if (*it == "<td>") {
it = this->label_list_.erase(it);
}
else {
++it;
}
}
}
// add_special_char
this->label_list_.insert(this->label_list_.begin(), this->beg);
this->label_list_.push_back(this->end);
}
void TablePostProcessor::Run(
std::vector<float>& loc_preds, std::vector<float>& structure_probs,
std::vector<float>& rec_scores, ov::Shape& loc_preds_shape,
ov::Shape& structure_probs_shape,
std::vector<std::vector<std::string>>& rec_html_tag_batch,
std::vector<std::vector<std::vector<int>>>& rec_boxes_batch,
std::vector<int>& width_list, std::vector<int>& height_list) {
for (int batch_idx = 0; batch_idx < structure_probs_shape[0]; batch_idx++) {
// image tags and boxs
std::vector<std::string> rec_html_tags;
std::vector<std::vector<int>> rec_boxes;
float score = 0.f;
int count = 0;
float char_score = 0.f;
int char_idx = 0;
// step
for (int step_idx = 0; step_idx < structure_probs_shape[1]; step_idx++) {
std::string html_tag;
std::vector<int> rec_box;
// html tag
int step_start_idx = (batch_idx * structure_probs_shape[1] + step_idx) *
structure_probs_shape[2];
char_idx = int(Utility::argmax(
&structure_probs[step_start_idx],
&structure_probs[step_start_idx + structure_probs_shape[2]]));
char_score = float(*std::max_element(
&structure_probs[step_start_idx],
&structure_probs[step_start_idx + structure_probs_shape[2]]));
html_tag = this->label_list_[char_idx];
if (step_idx > 0 && html_tag == this->end) {
break;
}
if (html_tag == this->beg) {
continue;
}
count += 1;
score += char_score;
rec_html_tags.push_back(html_tag);
// box
if (html_tag == "<td>" || html_tag == "<td" || html_tag == "<td></td>") {
for (int point_idx = 0; point_idx < loc_preds_shape[2]; point_idx++) {
step_start_idx = (batch_idx * structure_probs_shape[1] + step_idx) *
loc_preds_shape[2] +
point_idx;
float point = loc_preds[step_start_idx];
if (point_idx % 2 == 0) {
point = int(point * width_list[batch_idx]);
}
else {
point = int(point * height_list[batch_idx]);
}
rec_box.push_back(point);
}
rec_boxes.push_back(rec_box);
}
}
score /= count;
if (std::isnan(score) || rec_boxes.size() == 0) {
score = -1;
}
rec_scores.push_back(score);
rec_boxes_batch.push_back(rec_boxes);
rec_html_tag_batch.push_back(rec_html_tags);
}
}
void PicodetPostProcessor::init(std::string label_path,
const double score_threshold,
const double nms_threshold,
const std::vector<int>& fpn_stride) {
this->label_list_ = Utility::ReadDict(label_path);
this->score_threshold_ = score_threshold;
this->nms_threshold_ = nms_threshold;
this->num_class_ = label_list_.size();
this->fpn_stride_ = fpn_stride;
}
void PicodetPostProcessor::Run(std::vector<StructurePredictResult>& results,
std::vector<std::vector<float>> outs,
std::vector<int> ori_shape,
std::vector<int> resize_shape, int reg_max) {
int in_h = resize_shape[0];
int in_w = resize_shape[1];
float scale_factor_h = resize_shape[0] / float(ori_shape[0]);
float scale_factor_w = resize_shape[1] / float(ori_shape[1]);
std::vector<std::vector<StructurePredictResult>> bbox_results;
bbox_results.resize(this->num_class_);
for (int i = 0; i < this->fpn_stride_.size(); ++i) {
int feature_h = std::ceil((float)in_h / this->fpn_stride_[i]);
int feature_w = std::ceil((float)in_w / this->fpn_stride_[i]);
for (int idx = 0; idx < feature_h * feature_w; idx++) {
// score and label
float score = 0;
int cur_label = 0;
for (int label = 0; label < this->num_class_; label++) {
if (outs[i][idx * this->num_class_ + label] > score) {
score = outs[i][idx * this->num_class_ + label];
cur_label = label;
}
}
// bbox
if (score > this->score_threshold_) {
int row = idx / feature_w;
int col = idx % feature_w;
std::vector<float> bbox_pred(
outs[i + this->fpn_stride_.size()].begin() + idx * 4 * reg_max,
outs[i + this->fpn_stride_.size()].begin() +
(idx + 1) * 4 * reg_max);
bbox_results[cur_label].push_back(
this->disPred2Bbox(bbox_pred, cur_label, score, col, row,
this->fpn_stride_[i], resize_shape, reg_max));
}
}
}
for (int i = 0; i < bbox_results.size(); i++) {
bool flag = bbox_results[i].size() <= 0;
}
for (int i = 0; i < bbox_results.size(); i++) {
bool flag = bbox_results[i].size() <= 0;
if (bbox_results[i].size() <= 0) {
continue;
}
this->nms(bbox_results[i], this->nms_threshold_);
for (auto box : bbox_results[i]) {
box.box[0] = box.box[0] / scale_factor_w;
box.box[2] = box.box[2] / scale_factor_w;
box.box[1] = box.box[1] / scale_factor_h;
box.box[3] = box.box[3] / scale_factor_h;
results.push_back(box);
}
}
}
StructurePredictResult
PicodetPostProcessor::disPred2Bbox(std::vector<float> bbox_pred, int label,
float score, int x, int y, int stride,
std::vector<int> im_shape, int reg_max) {
float ct_x = (x + 0.5) * stride;
float ct_y = (y + 0.5) * stride;
std::vector<float> dis_pred;
dis_pred.resize(4);
for (int i = 0; i < 4; i++) {
float dis = 0;
std::vector<float> bbox_pred_i(bbox_pred.begin() + i * reg_max,
bbox_pred.begin() + (i + 1) * reg_max);
std::vector<float> dis_after_sm =
Utility::activation_function_softmax(bbox_pred_i);
for (int j = 0; j < reg_max; j++) {
dis += j * dis_after_sm[j];
}
dis *= stride;
dis_pred[i] = dis;
}
float xmin = (std::max)(ct_x - dis_pred[0], .0f);
float ymin = (std::max)(ct_y - dis_pred[1], .0f);
float xmax = (std::min)(ct_x + dis_pred[2], (float)im_shape[1]);
float ymax = (std::min)(ct_y + dis_pred[3], (float)im_shape[0]);
StructurePredictResult result_item;
result_item.box = { xmin, ymin, xmax, ymax };
result_item.type = this->label_list_[label];
result_item.confidence = score;
return result_item;
}
void PicodetPostProcessor::nms(std::vector<StructurePredictResult>& input_boxes,
float nms_threshold) {
std::sort(input_boxes.begin(), input_boxes.end(),
[](StructurePredictResult a, StructurePredictResult b) {
return a.confidence > b.confidence;
});
std::vector<int> picked(input_boxes.size(), 1);
for (int i = 0; i < input_boxes.size(); ++i) {
if (picked[i] == 0) {
continue;
}
for (int j = i + 1; j < input_boxes.size(); ++j) {
if (picked[j] == 0) {
continue;
}
float iou = Utility::iou(input_boxes[i].box, input_boxes[j].box);
if (iou > nms_threshold) {
picked[j] = 0;
}
}
}
std::vector<StructurePredictResult> input_boxes_nms;
for (int i = 0; i < input_boxes.size(); ++i) {
if (picked[i] == 1) {
input_boxes_nms.push_back(input_boxes[i]);
}
}
input_boxes = input_boxes_nms;
}
} // namespace PaddleOCR

View File

@@ -0,0 +1,165 @@
// 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 <include/preprocess_op.h>
namespace PaddleOCR {
void Permute::Run(const cv::Mat* im, float* data) {
int rh = im->rows;
int rw = im->cols;
int rc = im->channels();
for (int i = 0; i < rc; ++i) {
cv::extractChannel(*im, cv::Mat(rh, rw, CV_32FC1, data + i * rh * rw), i);
}
}
void PermuteBatch::Run(const std::vector<cv::Mat> imgs, float* data) {
for (int j = 0; j < imgs.size(); j++) {
int rh = imgs[j].rows;
int rw = imgs[j].cols;
int rc = imgs[j].channels();
for (int i = 0; i < rc; ++i) {
cv::extractChannel(
imgs[j], cv::Mat(rh, rw, CV_32FC1, data + (j * rc + i) * rh * rw), i);
}
}
}
void Normalize::Run(cv::Mat* im, const std::vector<float>& mean,
const std::vector<float>& scale, const bool is_scale) {
double e = 1.0;
if (is_scale) {
e /= 255.0;
}
(*im).convertTo(*im, CV_32FC3, e);
std::vector<cv::Mat> bgr_channels(3);
cv::split(*im, bgr_channels);
for (auto i = 0; i < bgr_channels.size(); i++) {
bgr_channels[i].convertTo(bgr_channels[i], CV_32FC1, 1.0 * scale[i],
(0.0 - mean[i]) * scale[i]);
}
cv::merge(bgr_channels, *im);
}
void ResizeImgType0::Run(const cv::Mat& img, cv::Mat& resize_img,
std::string limit_type, int limit_side_len,
float& ratio_h, float& ratio_w) {
int w = img.cols;
int h = img.rows;
float ratio = 1.f;
if (limit_type == "min") {
int min_wh = std::min(h, w);
if (min_wh < limit_side_len) {
if (h < w) {
ratio = float(limit_side_len) / float(h);
}
else {
ratio = float(limit_side_len) / float(w);
}
}
}
else {
int max_wh = std::max(h, w);
if (max_wh > limit_side_len) {
if (h > w) {
ratio = float(limit_side_len) / float(h);
}
else {
ratio = float(limit_side_len) / float(w);
}
}
}
int resize_h = int(float(h) * ratio);
int resize_w = int(float(w) * ratio);
resize_h = std::max(int(round(float(resize_h) / 32) * 32), 32);
resize_w = std::max(int(round(float(resize_w) / 32) * 32), 32);
cv::resize(img, resize_img, cv::Size(resize_w, resize_h));
ratio_h = float(resize_h) / float(h);
ratio_w = float(resize_w) / float(w);
}
void CrnnResizeImg::Run(const cv::Mat& img, cv::Mat& resize_img, float wh_ratio,
const std::vector<int>& rec_image_shape) {
int imgC, imgH, imgW;
imgC = rec_image_shape[0];
imgH = rec_image_shape[1];
imgW = rec_image_shape[2];
imgW = int(imgH * wh_ratio);
float ratio = float(img.cols) / float(img.rows);
int resize_w, resize_h;
if (ceilf(imgH * ratio) > imgW)
resize_w = imgW;
else
resize_w = int(ceilf(imgH * ratio));
cv::resize(img, resize_img, cv::Size(resize_w, imgH), 0.f, 0.f,
cv::INTER_LINEAR);
cv::copyMakeBorder(resize_img, resize_img, 0, 0, 0,
int(imgW - resize_img.cols), cv::BORDER_CONSTANT,
{ 127, 127, 127 });
}
void ClsResizeImg::Run(const cv::Mat& img, cv::Mat& resize_img,
const std::vector<size_t>& rec_image_shape) {
int imgC, imgH, imgW;
imgC = rec_image_shape[0];
imgH = rec_image_shape[1];
imgW = rec_image_shape[2];
float ratio = float(img.cols) / float(img.rows);
int resize_w, resize_h;
if (ceilf(imgH * ratio) > imgW)
resize_w = imgW;
else
resize_w = int(ceilf(imgH * ratio));
cv::resize(img, resize_img, cv::Size(resize_w, imgH), 0.f, 0.f,
cv::INTER_LINEAR);
}
void TableResizeImg::Run(const cv::Mat& img, cv::Mat& resize_img,
const int max_len) {
int w = img.cols;
int h = img.rows;
int max_wh = w >= h ? w : h;
float ratio = w >= h ? float(max_len) / float(w) : float(max_len) / float(h);
int resize_h = int(float(h) * ratio);
int resize_w = int(float(w) * ratio);
cv::resize(img, resize_img, cv::Size(resize_w, resize_h));
}
void TablePadImg::Run(const cv::Mat& img, cv::Mat& resize_img,
const int max_len) {
int w = img.cols;
int h = img.rows;
cv::copyMakeBorder(img, resize_img, 0, max_len - h, 0, max_len - w,
cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0));
}
void Resize::Run(const cv::Mat& img, cv::Mat& resize_img, const int h,
const int w) {
cv::resize(img, resize_img, cv::Size(w, h));
}
} // namespace PaddleOCR

View File

@@ -0,0 +1,172 @@
#include "include/rec.h"
namespace PaddleOCR {
Rec::Rec() {}
Rec::~Rec() {}
bool Rec::init(string model_path, const string &label_path)
{
this->model_path = model_path;
this->model = this->core.read_model(this->model_path);
// -------- Step 3. Preprocessing API--------
ov::preprocess::PrePostProcessor prep(this->model);
// Declare section of desired application's input format
prep.input().tensor()
.set_layout("NHWC")
.set_color_format(ov::preprocess::ColorFormat::BGR);
// Specify actual model layout
prep.input().model()
.set_layout("NCHW");
prep.input().preprocess()
.mean({0.5f, 0.5f, 0.5f})
.scale({0.5f, 0.5f, 0.5f});
// Dump preprocessor
std::cout << "Preprocessor: " << prep << std::endl;
this->model = prep.build();
this->label_list_ = Utility::ReadDict(label_path);
this->label_list_.insert(this->label_list_.begin(),
"#"); // blank char for ctc
this->label_list_.push_back(" ");
return true;
}
bool Rec::run(std::vector<cv::Mat> img_list, std::vector<OCRPredictResult> &ocr_results)
{
std::vector<std::string> rec_texts(img_list.size(), "");
std::vector<float> rec_text_scores(img_list.size(), 0);
int img_num = img_list.size();
std::vector<float> width_list;
for (int i = 0; i < img_num; i++) {
width_list.push_back(float(img_list[i].cols) / img_list[i].rows);
}
std::vector<int> indices = Utility::argsort(width_list);
for (int beg_img_no = 0; beg_img_no < img_num;
beg_img_no += this->rec_batch_num_) {
int end_img_no = std::min(img_num, beg_img_no + this->rec_batch_num_);
int batch_num = end_img_no - beg_img_no;
int imgH = this->rec_image_shape_[1];
int imgW = this->rec_image_shape_[2];
float max_wh_ratio = imgW * 1.0 / imgH;
for (int ino = beg_img_no; ino < end_img_no; ino++) {
int h = img_list[indices[ino]].rows;
int w = img_list[indices[ino]].cols;
float wh_ratio = w * 1.0 / h;
max_wh_ratio = std::max(max_wh_ratio, wh_ratio);
}
std::vector<cv::Mat> img_batch;
std::vector<ov::Tensor> batch_tensors;
int batch_width = imgW;
std::vector<cv::Mat> norm_img_batch;
for (int ino = beg_img_no; ino < end_img_no; ino++) {
cv::Mat srcimg;
img_list[indices[ino]].copyTo(srcimg);
cv::Mat resize_img;
this->resize_op_.Run(srcimg, resize_img, max_wh_ratio, this->rec_image_shape_);
double e = 1.0;
e /= 255.0;
resize_img.convertTo(resize_img, CV_32FC3, e);
norm_img_batch.push_back(resize_img);
// auto input_tensor = ov::Tensor(this->model->input().get_element_type(), {1, imgH, resize_img.cols, 3});
// auto input_data = input_tensor.data<float>();
// input_data = (float*)resize_img.data;
// batch_tensors.push_back(input_tensor);
batch_width = max(resize_img.cols, batch_width);
}
// for (int batch = 0; batch < batch_num; batch++)
// {
// for (int h = 0; h < imgH; h++)
// {
// for (int w = 0; w < batch_width; w++)
// {
// for (int c = 0; c < 3; c++)
// {
// int index = c + 3*w + 3*batch_width*h + 3*batch_width*imgH*batch;
// data[index] = float(norm_img_batch[batch].at<Vec3b>(h, w)[c]);
// }
// }
// }
// }
this->model->reshape({batch_num, imgH, batch_width,3});
// float data[batch_num * 3 * imgH * batch_width];
this->rec_model = this->core.compile_model(this->model, "CPU");
this->infer_request = this->rec_model.create_infer_request();
auto input_port = this->rec_model.input();
ov::Tensor input_tensor = this->infer_request.get_input_tensor();
const size_t batch_size = norm_img_batch.size();
for (size_t image_id = 0; image_id < norm_img_batch.size(); ++image_id) {
const size_t image_size = ov::shape_size(this->model->input().get_shape()) / batch_size;
std::memcpy(input_tensor.data<float>() + image_id * image_size, (float*)norm_img_batch[image_id].data, image_size*sizeof(float));
}
// ov::Tensor input_tensor(input_port.get_element_type(), input_port.get_shape(), data);
// this->infer_request.set_input_tensor(input_tensor);
// -------- Step 7. Start inference --------
this->infer_request.infer();
auto output = this->infer_request.get_output_tensor();
const float *out_data = output.data<const float>();
auto predict_shape = output.get_shape();
// predict_batch is the result of Last FC with softmax
for (int m = 0; m < predict_shape[0]; m++) {
std::string str_res;
int argmax_idx;
int last_index = 0;
float score = 0.f;
int count = 0;
float max_value = 0.0f;
for (int n = 0; n < predict_shape[1]; n++) {
// get idx
argmax_idx = int(Utility::argmax(
&out_data[(m * predict_shape[1] + n) * predict_shape[2]],
&out_data[(m * predict_shape[1] + n + 1) * predict_shape[2]]));
// get score
max_value = float(*std::max_element(
&out_data[(m * predict_shape[1] + n) * predict_shape[2]],
&out_data[(m * predict_shape[1] + n + 1) * predict_shape[2]]));
if (argmax_idx > 0 && (!(n > 0 && argmax_idx == last_index))) {
score += max_value;
count += 1;
str_res += this->label_list_[argmax_idx];
}
last_index = argmax_idx;
}
score /= count;
if (std::isnan(score)) {
continue;
}
rec_texts[indices[beg_img_no + m]] = str_res;
rec_text_scores[indices[beg_img_no + m]] = score;
}
}
// sort boex from top to bottom, from left to right
for (int i = 0; i < rec_texts.size(); i++) {
ocr_results[i].text = rec_texts[i];
ocr_results[i].score = rec_text_scores[i];
}
return true;
}
}

View File

@@ -0,0 +1,69 @@
#include "include/structure_layout.h"
namespace PaddleOCR {
Layout::Layout(std::string model_path, std::string layout_dict_path) {
ov::Core core;
this->model_path = model_path;
this->model = core.read_model(this->model_path);
this->model->reshape({ 1, 3, this->layout_img_h_, this->layout_img_w_ });
// preprocessing API
ov::preprocess::PrePostProcessor prep(this->model);
// declare section of desired application's input format
prep.input().tensor().set_layout("NHWC").set_color_format(ov::preprocess::ColorFormat::BGR);
// specify actual model layout
prep.input().model().set_layout("NCHW");
prep.input().preprocess().mean(this->mean_).scale(this->scale_);
// dump preprocessor
std::cout << "Preprocessor: " << prep << std::endl;
this->model = prep.build();
this->compiled_model = core.compile_model(this->model, "CPU");
this->infer_request = this->compiled_model.create_infer_request();
this->post_processor_.init(layout_dict_path, this->layout_score_threshold,
this->layout_nms_threshold);
}
void Layout::Run(cv::Mat& src_img, std::vector<StructurePredictResult>& structure_result) {
this->src_img = src_img;
this->resize_op_.Run(this->src_img, this->resize_img, this->layout_img_h_, this->layout_img_w_);
std::vector<std::vector<std::vector<int>>> boxes;
auto input_port = this->compiled_model.input();
// -------- set input --------
this->resize_img.convertTo(this->resize_img, CV_32FC3, e);
ov::Tensor input_tensor(input_port.get_element_type(), input_port.get_shape(), (float*)this->resize_img.data);
this->infer_request.set_input_tensor(input_tensor);
// -------- start inference --------
this->infer_request.infer();
std::vector<std::vector<float>> out_tensor_list;
std::vector<ov::Shape> output_shape_list;
for (int j = 0; j < (this->model->outputs()).size(); j++) {
auto output = this->infer_request.get_output_tensor(j);
auto output_shape = output.get_shape();
int out_num = std::accumulate(output_shape.begin(), output_shape.end(), 1,
std::multiplies<int>());
output_shape_list.push_back(output_shape);
const float* out_data = output.data<const float>();
std::vector<float> out_tensor(out_data, out_data + out_num);
out_tensor_list.push_back(out_tensor);
}
std::vector<int> bbox_num;
int reg_max = 0;
for (int i = 0; i < out_tensor_list.size(); i++) {
if (i == this->post_processor_.fpn_stride_.size()) {
reg_max = output_shape_list[i][2] / 4;
break;
}
}
std::vector<int> ori_shape = { this->src_img.rows, this->src_img.cols };
std::vector<int> resize_shape = { this->resize_img.rows, this->resize_img.cols };
this->post_processor_.Run(structure_result, out_tensor_list, ori_shape, resize_shape,
reg_max);
bbox_num.push_back(structure_result.size());
}
}

View File

@@ -0,0 +1,96 @@
#include "include/structure_table.h"
namespace PaddleOCR {
Table::Table(std::string model_path, const std::string table_char_dict_path) {
ov::Core core;
this->model_path = model_path;
this->model = core.read_model(this->model_path);
// reshape the model for dynamic batch size and sentence width
this->model->reshape({ {ov::Dimension(1, this->table_batch_num_), 3, this->table_max_len_, this->table_max_len_} });
this->compiled_model = core.compile_model(this->model, "CPU");
this->infer_request = this->compiled_model.create_infer_request();
this->post_processor_.init(table_char_dict_path, false);
}
void Table::Run(std::vector<cv::Mat> img_list,
std::vector<std::vector<std::string>>& structure_html_tags,
std::vector<float>& structure_scores,
std::vector<std::vector<std::vector<int>>>& structure_boxes) {
int img_num = img_list.size();
for (int beg_img_no = 0; beg_img_no < img_num;
beg_img_no += this->table_batch_num_) {
// preprocess
auto preprocess_start = std::chrono::steady_clock::now();
int end_img_no = std::min(img_num, beg_img_no + this->table_batch_num_);
size_t batch_num = end_img_no - beg_img_no;
std::vector<cv::Mat> norm_img_batch;
std::vector<int> width_list;
std::vector<int> height_list;
for (int ino = beg_img_no; ino < end_img_no; ino++) {
cv::Mat srcimg;
img_list[ino].copyTo(srcimg);
cv::Mat resize_img;
cv::Mat pad_img;
this->resize_op_.Run(srcimg, resize_img, this->table_max_len_);
this->normalize_op_.Run(&resize_img, this->mean_, this->scale_,
this->is_scale_);
this->pad_op_.Run(resize_img, pad_img, this->table_max_len_);
norm_img_batch.push_back(pad_img);
width_list.push_back(srcimg.cols);
height_list.push_back(srcimg.rows);
}
size_t tableMaxLen = this->table_max_len_;
std::vector<float> input(batch_num * 3 * this->table_max_len_ * this->table_max_len_, 0.0f);
ov::Shape intput_shape = { batch_num, 3, tableMaxLen, tableMaxLen };
this->permute_op_.Run(norm_img_batch, input.data());
// inference.
auto input_port = this->compiled_model.input();
ov::Tensor input_tensor(input_port.get_element_type(), intput_shape, input.data());
this->infer_request.set_input_tensor(input_tensor);
// start inference
this->infer_request.infer();
auto output0 = this->infer_request.get_output_tensor(0);
const float* out_data0 = output0.data<const float>();
auto predict_shape0 = output0.get_shape();
auto output1 = this->infer_request.get_output_tensor(1);
const float* out_data1 = output1.data<const float>();
auto predict_shape1 = output1.get_shape();
int out_num0 = std::accumulate(predict_shape0.begin(), predict_shape0.end(),
1, std::multiplies<int>());
int out_num1 = std::accumulate(predict_shape1.begin(), predict_shape1.end(),
1, std::multiplies<int>());
std::vector<float> loc_preds(out_data0, out_data0 + out_num0);
std::vector<float> structure_probs(out_data1, out_data1 + out_num1);
// postprocess
std::vector<std::vector<std::string>> structure_html_tag_batch;
std::vector<float> structure_score_batch;
std::vector<std::vector<std::vector<int>>> structure_boxes_batch;
this->post_processor_.Run(loc_preds, structure_probs, structure_score_batch,
predict_shape0, predict_shape1,
structure_html_tag_batch, structure_boxes_batch,
width_list, height_list);
for (int m = 0; m < predict_shape0[0]; m++) {
structure_html_tag_batch[m].insert(structure_html_tag_batch[m].begin(),
"<table>");
structure_html_tag_batch[m].insert(structure_html_tag_batch[m].begin(),
"<body>");
structure_html_tag_batch[m].insert(structure_html_tag_batch[m].begin(),
"<html>");
structure_html_tag_batch[m].push_back("</table>");
structure_html_tag_batch[m].push_back("</body>");
structure_html_tag_batch[m].push_back("</html>");
structure_html_tags.push_back(structure_html_tag_batch[m]);
structure_scores.push_back(structure_score_batch[m]);
structure_boxes.push_back(structure_boxes_batch[m]);
}
}
}
}