Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
weixin_41840029
PaddleOCR
提交
dd9f48da
P
PaddleOCR
项目概览
weixin_41840029
/
PaddleOCR
与 Fork 源项目一致
Fork自
PaddlePaddle / PaddleOCR
通知
1
Star
1
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
P
PaddleOCR
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
dd9f48da
编写于
9月 20, 2022
作者:
文幕地方
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
fix bug
上级
b913f664
变更
21
显示空白变更内容
内联
并排
Showing
21 changed file
with
122 addition
and
266 deletion
+122
-266
deploy/cpp_infer/include/ocr_cls.h
deploy/cpp_infer/include/ocr_cls.h
+1
-15
deploy/cpp_infer/include/ocr_det.h
deploy/cpp_infer/include/ocr_det.h
+3
-17
deploy/cpp_infer/include/ocr_rec.h
deploy/cpp_infer/include/ocr_rec.h
+2
-17
deploy/cpp_infer/include/paddleocr.h
deploy/cpp_infer/include/paddleocr.h
+0
-19
deploy/cpp_infer/include/paddlestructure.h
deploy/cpp_infer/include/paddlestructure.h
+0
-19
deploy/cpp_infer/include/postprocess_op.h
deploy/cpp_infer/include/postprocess_op.h
+2
-39
deploy/cpp_infer/include/preprocess_op.h
deploy/cpp_infer/include/preprocess_op.h
+6
-15
deploy/cpp_infer/include/structure_layout.h
deploy/cpp_infer/include/structure_layout.h
+2
-17
deploy/cpp_infer/include/structure_table.h
deploy/cpp_infer/include/structure_table.h
+2
-17
deploy/cpp_infer/include/utility.h
deploy/cpp_infer/include/utility.h
+4
-4
deploy/cpp_infer/src/main.cpp
deploy/cpp_infer/src/main.cpp
+15
-14
deploy/cpp_infer/src/ocr_cls.cpp
deploy/cpp_infer/src/ocr_cls.cpp
+5
-5
deploy/cpp_infer/src/ocr_det.cpp
deploy/cpp_infer/src/ocr_det.cpp
+3
-4
deploy/cpp_infer/src/ocr_rec.cpp
deploy/cpp_infer/src/ocr_rec.cpp
+7
-9
deploy/cpp_infer/src/paddleocr.cpp
deploy/cpp_infer/src/paddleocr.cpp
+1
-1
deploy/cpp_infer/src/paddlestructure.cpp
deploy/cpp_infer/src/paddlestructure.cpp
+8
-10
deploy/cpp_infer/src/postprocess_op.cpp
deploy/cpp_infer/src/postprocess_op.cpp
+41
-15
deploy/cpp_infer/src/preprocess_op.cpp
deploy/cpp_infer/src/preprocess_op.cpp
+6
-21
deploy/cpp_infer/src/structure_layout.cpp
deploy/cpp_infer/src/structure_layout.cpp
+3
-3
deploy/cpp_infer/src/structure_table.cpp
deploy/cpp_infer/src/structure_table.cpp
+3
-3
deploy/cpp_infer/src/utility.cpp
deploy/cpp_infer/src/utility.cpp
+8
-2
未找到文件。
deploy/cpp_infer/include/ocr_cls.h
浏览文件 @
dd9f48da
...
...
@@ -14,26 +14,12 @@
#pragma once
#include "opencv2/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include "paddle_api.h"
#include "paddle_inference_api.h"
#include <chrono>
#include <iomanip>
#include <iostream>
#include <ostream>
#include <vector>
#include <cstring>
#include <fstream>
#include <numeric>
#include <include/preprocess_op.h>
#include <include/utility.h>
using
namespace
paddle_infer
;
namespace
PaddleOCR
{
class
Classifier
{
...
...
@@ -66,7 +52,7 @@ public:
std
::
vector
<
float
>
&
cls_scores
,
std
::
vector
<
double
>
&
times
);
private:
std
::
shared_ptr
<
Predictor
>
predictor_
;
std
::
shared_ptr
<
paddle_infer
::
Predictor
>
predictor_
;
bool
use_gpu_
=
false
;
int
gpu_id_
=
0
;
...
...
deploy/cpp_infer/include/ocr_det.h
浏览文件 @
dd9f48da
...
...
@@ -14,26 +14,12 @@
#pragma once
#include "opencv2/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include "paddle_api.h"
#include "paddle_inference_api.h"
#include <chrono>
#include <iomanip>
#include <iostream>
#include <ostream>
#include <vector>
#include <cstring>
#include <fstream>
#include <numeric>
#include <include/postprocess_op.h>
#include <include/preprocess_op.h>
using
namespace
paddle_infer
;
namespace
PaddleOCR
{
class
DBDetector
{
...
...
@@ -41,7 +27,7 @@ public:
explicit
DBDetector
(
const
std
::
string
&
model_dir
,
const
bool
&
use_gpu
,
const
int
&
gpu_id
,
const
int
&
gpu_mem
,
const
int
&
cpu_math_library_num_threads
,
const
bool
&
use_mkldnn
,
const
string
&
limit_type
,
const
bool
&
use_mkldnn
,
const
st
d
::
st
ring
&
limit_type
,
const
int
&
limit_side_len
,
const
double
&
det_db_thresh
,
const
double
&
det_db_box_thresh
,
const
double
&
det_db_unclip_ratio
,
...
...
@@ -77,7 +63,7 @@ public:
std
::
vector
<
double
>
&
times
);
private:
std
::
shared_ptr
<
Predictor
>
predictor_
;
std
::
shared_ptr
<
paddle_infer
::
Predictor
>
predictor_
;
bool
use_gpu_
=
false
;
int
gpu_id_
=
0
;
...
...
@@ -85,7 +71,7 @@ private:
int
cpu_math_library_num_threads_
=
4
;
bool
use_mkldnn_
=
false
;
string
limit_type_
=
"max"
;
st
d
::
st
ring
limit_type_
=
"max"
;
int
limit_side_len_
=
960
;
double
det_db_thresh_
=
0.3
;
...
...
deploy/cpp_infer/include/ocr_rec.h
浏览文件 @
dd9f48da
...
...
@@ -14,27 +14,12 @@
#pragma once
#include "opencv2/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include "paddle_api.h"
#include "paddle_inference_api.h"
#include <chrono>
#include <iomanip>
#include <iostream>
#include <ostream>
#include <vector>
#include <cstring>
#include <fstream>
#include <numeric>
#include <include/ocr_cls.h>
#include <include/preprocess_op.h>
#include <include/utility.h>
using
namespace
paddle_infer
;
namespace
PaddleOCR
{
class
CRNNRecognizer
{
...
...
@@ -42,7 +27,7 @@ public:
explicit
CRNNRecognizer
(
const
std
::
string
&
model_dir
,
const
bool
&
use_gpu
,
const
int
&
gpu_id
,
const
int
&
gpu_mem
,
const
int
&
cpu_math_library_num_threads
,
const
bool
&
use_mkldnn
,
const
string
&
label_path
,
const
bool
&
use_mkldnn
,
const
st
d
::
st
ring
&
label_path
,
const
bool
&
use_tensorrt
,
const
std
::
string
&
precision
,
const
int
&
rec_batch_num
,
const
int
&
rec_img_h
,
...
...
@@ -75,7 +60,7 @@ public:
std
::
vector
<
float
>
&
rec_text_scores
,
std
::
vector
<
double
>
&
times
);
private:
std
::
shared_ptr
<
Predictor
>
predictor_
;
std
::
shared_ptr
<
paddle_infer
::
Predictor
>
predictor_
;
bool
use_gpu_
=
false
;
int
gpu_id_
=
0
;
...
...
deploy/cpp_infer/include/paddleocr.h
浏览文件 @
dd9f48da
...
...
@@ -14,28 +14,9 @@
#pragma once
#include "opencv2/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include "paddle_api.h"
#include "paddle_inference_api.h"
#include <chrono>
#include <iomanip>
#include <iostream>
#include <ostream>
#include <vector>
#include <cstring>
#include <fstream>
#include <numeric>
#include <include/ocr_cls.h>
#include <include/ocr_det.h>
#include <include/ocr_rec.h>
#include <include/preprocess_op.h>
#include <include/utility.h>
using
namespace
paddle_infer
;
namespace
PaddleOCR
{
...
...
deploy/cpp_infer/include/paddlestructure.h
浏览文件 @
dd9f48da
...
...
@@ -14,28 +14,9 @@
#pragma once
#include "opencv2/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include "paddle_api.h"
#include "paddle_inference_api.h"
#include <chrono>
#include <iomanip>
#include <iostream>
#include <ostream>
#include <vector>
#include <cstring>
#include <fstream>
#include <numeric>
#include <include/paddleocr.h>
#include <include/preprocess_op.h>
#include <include/structure_layout.h>
#include <include/structure_table.h>
#include <include/utility.h>
using
namespace
paddle_infer
;
namespace
PaddleOCR
{
...
...
deploy/cpp_infer/include/postprocess_op.h
浏览文件 @
dd9f48da
...
...
@@ -14,24 +14,9 @@
#pragma once
#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/clipper.h"
#include "include/utility.h"
using
namespace
std
;
namespace
PaddleOCR
{
class
DBPostProcessor
{
...
...
@@ -92,23 +77,7 @@ private:
class
TablePostProcessor
{
public:
void
init
(
std
::
string
label_path
,
bool
merge_no_span_structure
=
true
)
{
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
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
,
std
::
vector
<
int
>
&
loc_preds_shape
,
std
::
vector
<
int
>
&
structure_probs_shape
,
...
...
@@ -126,13 +95,7 @@ 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
})
{
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
;
}
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
);
...
...
deploy/cpp_infer/include/preprocess_op.h
浏览文件 @
dd9f48da
...
...
@@ -14,21 +14,12 @@
#pragma once
#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>
using
namespace
std
;
using
namespace
paddle
;
#include "opencv2/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
namespace
PaddleOCR
{
...
...
@@ -51,9 +42,9 @@ public:
class
ResizeImgType0
{
public:
virtual
void
Run
(
const
cv
::
Mat
&
img
,
cv
::
Mat
&
resize_img
,
string
limit_type
,
int
limit_side_len
,
float
&
ratio_h
,
float
&
ratio_w
,
bool
use_tensorrt
);
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
,
bool
use_tensorrt
);
};
class
CrnnResizeImg
{
...
...
deploy/cpp_infer/include/structure_layout.h
浏览文件 @
dd9f48da
...
...
@@ -14,26 +14,11 @@
#pragma once
#include "opencv2/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include "paddle_api.h"
#include "paddle_inference_api.h"
#include <chrono>
#include <iomanip>
#include <iostream>
#include <ostream>
#include <vector>
#include <cstring>
#include <fstream>
#include <numeric>
#include <include/postprocess_op.h>
#include <include/preprocess_op.h>
#include <include/utility.h>
using
namespace
paddle_infer
;
namespace
PaddleOCR
{
...
...
@@ -42,7 +27,7 @@ public:
explicit
StructureLayoutRecognizer
(
const
std
::
string
&
model_dir
,
const
bool
&
use_gpu
,
const
int
&
gpu_id
,
const
int
&
gpu_mem
,
const
int
&
cpu_math_library_num_threads
,
const
bool
&
use_mkldnn
,
const
string
&
label_path
,
const
bool
&
use_mkldnn
,
const
st
d
::
st
ring
&
label_path
,
const
bool
&
use_tensorrt
,
const
std
::
string
&
precision
,
const
double
&
layout_score_threshold
,
const
double
&
layout_nms_threshold
)
{
...
...
@@ -66,7 +51,7 @@ public:
std
::
vector
<
double
>
&
times
);
private:
std
::
shared_ptr
<
Predictor
>
predictor_
;
std
::
shared_ptr
<
paddle_infer
::
Predictor
>
predictor_
;
bool
use_gpu_
=
false
;
int
gpu_id_
=
0
;
...
...
deploy/cpp_infer/include/structure_table.h
浏览文件 @
dd9f48da
...
...
@@ -14,26 +14,11 @@
#pragma once
#include "opencv2/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include "paddle_api.h"
#include "paddle_inference_api.h"
#include <chrono>
#include <iomanip>
#include <iostream>
#include <ostream>
#include <vector>
#include <cstring>
#include <fstream>
#include <numeric>
#include <include/postprocess_op.h>
#include <include/preprocess_op.h>
#include <include/utility.h>
using
namespace
paddle_infer
;
namespace
PaddleOCR
{
...
...
@@ -42,7 +27,7 @@ public:
explicit
StructureTableRecognizer
(
const
std
::
string
&
model_dir
,
const
bool
&
use_gpu
,
const
int
&
gpu_id
,
const
int
&
gpu_mem
,
const
int
&
cpu_math_library_num_threads
,
const
bool
&
use_mkldnn
,
const
string
&
label_path
,
const
bool
&
use_mkldnn
,
const
st
d
::
st
ring
&
label_path
,
const
bool
&
use_tensorrt
,
const
std
::
string
&
precision
,
const
int
&
table_batch_num
,
const
int
&
table_max_len
,
const
bool
&
merge_no_span_structure
)
{
...
...
@@ -70,7 +55,7 @@ public:
std
::
vector
<
double
>
&
times
);
private:
std
::
shared_ptr
<
Predictor
>
predictor_
;
std
::
shared_ptr
<
paddle_infer
::
Predictor
>
predictor_
;
bool
use_gpu_
=
false
;
int
gpu_id_
=
0
;
...
...
deploy/cpp_infer/include/utility.h
浏览文件 @
dd9f48da
...
...
@@ -41,8 +41,7 @@ struct OCRPredictResult {
};
struct
StructurePredictResult
{
std
::
vector
<
int
>
box
;
std
::
vector
<
float
>
box_float
;
std
::
vector
<
float
>
box
;
std
::
vector
<
std
::
vector
<
int
>>
cell_box
;
std
::
string
type
;
std
::
vector
<
OCRPredictResult
>
text_res
;
...
...
@@ -60,7 +59,7 @@ public:
const
std
::
string
&
save_path
);
static
void
VisualizeBboxes
(
const
cv
::
Mat
&
srcimg
,
StructurePredictResult
&
structure_result
,
const
StructurePredictResult
&
structure_result
,
const
std
::
string
&
save_path
);
template
<
class
ForwardIterator
>
...
...
@@ -84,7 +83,8 @@ public:
static
void
print_result
(
const
std
::
vector
<
OCRPredictResult
>
&
ocr_result
);
static
cv
::
Mat
crop_image
(
cv
::
Mat
&
img
,
std
::
vector
<
int
>
&
area
);
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
);
...
...
deploy/cpp_infer/src/main.cpp
浏览文件 @
dd9f48da
...
...
@@ -75,7 +75,8 @@ void check_params() {
}
if
(
FLAGS_precision
!=
"fp32"
&&
FLAGS_precision
!=
"fp16"
&&
FLAGS_precision
!=
"int8"
)
{
cout
<<
"precison should be 'fp32'(default), 'fp16' or 'int8'. "
<<
endl
;
std
::
cout
<<
"precison should be 'fp32'(default), 'fp16' or 'int8'. "
<<
std
::
endl
;
exit
(
1
);
}
}
...
...
@@ -93,7 +94,7 @@ void ocr(std::vector<cv::String> &cv_all_img_names) {
cv
::
Mat
img
=
cv
::
imread
(
cv_all_img_names
[
i
],
cv
::
IMREAD_COLOR
);
if
(
!
img
.
data
)
{
std
::
cerr
<<
"[ERROR] image read failed! image path: "
<<
cv_all_img_names
[
i
]
<<
endl
;
<<
cv_all_img_names
[
i
]
<<
std
::
endl
;
continue
;
}
img_list
.
push_back
(
img
);
...
...
@@ -104,7 +105,7 @@ void ocr(std::vector<cv::String> &cv_all_img_names) {
ocr
.
ocr
(
img_list
,
FLAGS_det
,
FLAGS_rec
,
FLAGS_cls
);
for
(
int
i
=
0
;
i
<
img_names
.
size
();
++
i
)
{
cout
<<
"predict img: "
<<
cv_all_img_names
[
i
]
<<
endl
;
std
::
cout
<<
"predict img: "
<<
cv_all_img_names
[
i
]
<<
std
::
endl
;
Utility
::
print_result
(
ocr_results
[
i
]);
if
(
FLAGS_visualize
&&
FLAGS_det
)
{
std
::
string
file_name
=
Utility
::
basename
(
img_names
[
i
]);
...
...
@@ -126,11 +127,11 @@ void structure(std::vector<cv::String> &cv_all_img_names) {
}
for
(
int
i
=
0
;
i
<
cv_all_img_names
.
size
();
i
++
)
{
cout
<<
"predict img: "
<<
cv_all_img_names
[
i
]
<<
endl
;
std
::
cout
<<
"predict img: "
<<
cv_all_img_names
[
i
]
<<
std
::
endl
;
cv
::
Mat
img
=
cv
::
imread
(
cv_all_img_names
[
i
],
cv
::
IMREAD_COLOR
);
if
(
!
img
.
data
)
{
std
::
cerr
<<
"[ERROR] image read failed! image path: "
<<
cv_all_img_names
[
i
]
<<
endl
;
<<
cv_all_img_names
[
i
]
<<
std
::
endl
;
continue
;
}
...
...
@@ -156,14 +157,14 @@ void structure(std::vector<cv::String> &cv_all_img_names) {
"_"
+
file_name
);
}
}
else
{
cout
<<
"count of ocr result is : "
<<
structure_results
[
j
].
text_res
.
size
()
<<
endl
;
std
::
cout
<<
"count of ocr result is : "
<<
structure_results
[
j
].
text_res
.
size
()
<<
std
::
endl
;
if
(
structure_results
[
j
].
text_res
.
size
()
>
0
)
{
cout
<<
"********** print ocr result "
<<
"**********"
<<
endl
;
std
::
cout
<<
"********** print ocr result "
<<
"**********"
<<
std
::
endl
;
Utility
::
print_result
(
structure_results
[
j
].
text_res
);
cout
<<
"********** end print ocr result "
<<
"**********"
<<
endl
;
std
::
cout
<<
"********** end print ocr result "
<<
"**********"
<<
std
::
endl
;
}
}
}
...
...
@@ -180,13 +181,13 @@ int main(int argc, char **argv) {
if
(
!
Utility
::
PathExists
(
FLAGS_image_dir
))
{
std
::
cerr
<<
"[ERROR] image path not exist! image_dir: "
<<
FLAGS_image_dir
<<
endl
;
<<
std
::
endl
;
exit
(
1
);
}
std
::
vector
<
cv
::
String
>
cv_all_img_names
;
cv
::
glob
(
FLAGS_image_dir
,
cv_all_img_names
);
std
::
cout
<<
"total images num: "
<<
cv_all_img_names
.
size
()
<<
endl
;
std
::
cout
<<
"total images num: "
<<
cv_all_img_names
.
size
()
<<
std
::
endl
;
if
(
!
Utility
::
PathExists
(
FLAGS_output
))
{
Utility
::
CreateDir
(
FLAGS_output
);
...
...
@@ -196,6 +197,6 @@ int main(int argc, char **argv) {
}
else
if
(
FLAGS_type
==
"structure"
)
{
structure
(
cv_all_img_names
);
}
else
{
std
::
cout
<<
"only value in ['ocr','structure'] is supported"
<<
endl
;
std
::
cout
<<
"only value in ['ocr','structure'] is supported"
<<
std
::
endl
;
}
}
deploy/cpp_infer/src/ocr_cls.cpp
浏览文件 @
dd9f48da
...
...
@@ -32,7 +32,7 @@ void Classifier::Run(std::vector<cv::Mat> img_list,
for
(
int
beg_img_no
=
0
;
beg_img_no
<
img_num
;
beg_img_no
+=
this
->
cls_batch_num_
)
{
auto
preprocess_start
=
std
::
chrono
::
steady_clock
::
now
();
int
end_img_no
=
min
(
img_num
,
beg_img_no
+
this
->
cls_batch_num_
);
int
end_img_no
=
std
::
min
(
img_num
,
beg_img_no
+
this
->
cls_batch_num_
);
int
batch_num
=
end_img_no
-
beg_img_no
;
// preprocess
std
::
vector
<
cv
::
Mat
>
norm_img_batch
;
...
...
@@ -97,7 +97,7 @@ void Classifier::Run(std::vector<cv::Mat> img_list,
}
void
Classifier
::
LoadModel
(
const
std
::
string
&
model_dir
)
{
Analysis
Config
config
;
paddle_infer
::
Config
config
;
config
.
SetModel
(
model_dir
+
"/inference.pdmodel"
,
model_dir
+
"/inference.pdiparams"
);
...
...
@@ -112,7 +112,7 @@ void Classifier::LoadModel(const std::string &model_dir) {
precision
=
paddle_infer
::
Config
::
Precision
::
kInt8
;
}
config
.
EnableTensorRtEngine
(
1
<<
20
,
10
,
3
,
precision
,
false
,
false
);
if
(
!
Utility
::
PathExists
(
"./trt_cls_shape.txt"
)){
if
(
!
Utility
::
PathExists
(
"./trt_cls_shape.txt"
))
{
config
.
CollectShapeRangeInfo
(
"./trt_cls_shape.txt"
);
}
else
{
config
.
EnableTunedTensorRtDynamicShape
(
"./trt_cls_shape.txt"
,
true
);
...
...
@@ -136,6 +136,6 @@ void Classifier::LoadModel(const std::string &model_dir) {
config
.
EnableMemoryOptim
();
config
.
DisableGlogInfo
();
this
->
predictor_
=
CreatePredictor
(
config
);
this
->
predictor_
=
paddle_infer
::
CreatePredictor
(
config
);
}
}
// namespace PaddleOCR
deploy/cpp_infer/src/ocr_det.cpp
浏览文件 @
dd9f48da
...
...
@@ -33,12 +33,11 @@ void DBDetector::LoadModel(const std::string &model_dir) {
precision
=
paddle_infer
::
Config
::
Precision
::
kInt8
;
}
config
.
EnableTensorRtEngine
(
1
<<
30
,
1
,
20
,
precision
,
false
,
false
);
if
(
!
Utility
::
PathExists
(
"./trt_det_shape.txt"
)){
if
(
!
Utility
::
PathExists
(
"./trt_det_shape.txt"
))
{
config
.
CollectShapeRangeInfo
(
"./trt_det_shape.txt"
);
}
else
{
config
.
EnableTunedTensorRtDynamicShape
(
"./trt_det_shape.txt"
,
true
);
}
}
}
else
{
config
.
DisableGpu
();
...
...
@@ -59,7 +58,7 @@ void DBDetector::LoadModel(const std::string &model_dir) {
config
.
EnableMemoryOptim
();
// config.DisableGlogInfo();
this
->
predictor_
=
CreatePredictor
(
config
);
this
->
predictor_
=
paddle_infer
::
CreatePredictor
(
config
);
}
void
DBDetector
::
Run
(
cv
::
Mat
&
img
,
...
...
deploy/cpp_infer/src/ocr_rec.cpp
浏览文件 @
dd9f48da
...
...
@@ -37,7 +37,7 @@ void CRNNRecognizer::Run(std::vector<cv::Mat> img_list,
for
(
int
beg_img_no
=
0
;
beg_img_no
<
img_num
;
beg_img_no
+=
this
->
rec_batch_num_
)
{
auto
preprocess_start
=
std
::
chrono
::
steady_clock
::
now
();
int
end_img_no
=
min
(
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
];
...
...
@@ -46,7 +46,7 @@ void CRNNRecognizer::Run(std::vector<cv::Mat> img_list,
int
h
=
img_list
[
indices
[
ino
]].
rows
;
int
w
=
img_list
[
indices
[
ino
]].
cols
;
float
wh_ratio
=
w
*
1.0
/
h
;
max_wh_ratio
=
max
(
max_wh_ratio
,
wh_ratio
);
max_wh_ratio
=
std
::
max
(
max_wh_ratio
,
wh_ratio
);
}
int
batch_width
=
imgW
;
...
...
@@ -60,7 +60,7 @@ void CRNNRecognizer::Run(std::vector<cv::Mat> img_list,
this
->
normalize_op_
.
Run
(
&
resize_img
,
this
->
mean_
,
this
->
scale_
,
this
->
is_scale_
);
norm_img_batch
.
push_back
(
resize_img
);
batch_width
=
max
(
resize_img
.
cols
,
batch_width
);
batch_width
=
std
::
max
(
resize_img
.
cols
,
batch_width
);
}
std
::
vector
<
float
>
input
(
batch_num
*
3
*
imgH
*
batch_width
,
0.0
f
);
...
...
@@ -115,7 +115,7 @@ void CRNNRecognizer::Run(std::vector<cv::Mat> img_list,
last_index
=
argmax_idx
;
}
score
/=
count
;
if
(
isnan
(
score
))
{
if
(
std
::
isnan
(
score
))
{
continue
;
}
rec_texts
[
indices
[
beg_img_no
+
m
]]
=
str_res
;
...
...
@@ -130,7 +130,6 @@ void CRNNRecognizer::Run(std::vector<cv::Mat> img_list,
}
void
CRNNRecognizer
::
LoadModel
(
const
std
::
string
&
model_dir
)
{
// AnalysisConfig config;
paddle_infer
::
Config
config
;
config
.
SetModel
(
model_dir
+
"/inference.pdmodel"
,
model_dir
+
"/inference.pdiparams"
);
...
...
@@ -147,12 +146,11 @@ void CRNNRecognizer::LoadModel(const std::string &model_dir) {
if
(
this
->
precision_
==
"int8"
)
{
precision
=
paddle_infer
::
Config
::
Precision
::
kInt8
;
}
if
(
!
Utility
::
PathExists
(
"./trt_rec_shape.txt"
)){
if
(
!
Utility
::
PathExists
(
"./trt_rec_shape.txt"
))
{
config
.
CollectShapeRangeInfo
(
"./trt_rec_shape.txt"
);
}
else
{
config
.
EnableTunedTensorRtDynamicShape
(
"./trt_rec_shape.txt"
,
true
);
}
}
}
else
{
config
.
DisableGpu
();
...
...
@@ -177,7 +175,7 @@ void CRNNRecognizer::LoadModel(const std::string &model_dir) {
config
.
EnableMemoryOptim
();
// config.DisableGlogInfo();
this
->
predictor_
=
CreatePredictor
(
config
);
this
->
predictor_
=
paddle_infer
::
CreatePredictor
(
config
);
}
}
// namespace PaddleOCR
deploy/cpp_infer/src/paddleocr.cpp
浏览文件 @
dd9f48da
...
...
@@ -16,7 +16,7 @@
#include <include/paddleocr.h>
#include "auto_log/autolog.h"
#include <numeric>
namespace
PaddleOCR
{
PPOCR
::
PPOCR
()
{
...
...
deploy/cpp_infer/src/paddlestructure.cpp
浏览文件 @
dd9f48da
...
...
@@ -16,8 +16,6 @@
#include <include/paddlestructure.h>
#include "auto_log/autolog.h"
#include <numeric>
#include <sys/stat.h>
namespace
PaddleOCR
{
...
...
@@ -50,7 +48,7 @@ PaddleStructure::structure(cv::Mat srcimg, bool layout, bool table, bool ocr) {
}
else
{
StructurePredictResult
res
;
res
.
type
=
"table"
;
res
.
box
=
std
::
vector
<
int
>
(
4
,
0
);
res
.
box
=
std
::
vector
<
float
>
(
4
,
0.
0
);
res
.
box
[
2
]
=
img
.
cols
;
res
.
box
[
3
]
=
img
.
rows
;
structure_results
.
push_back
(
res
);
...
...
@@ -108,10 +106,10 @@ void PaddleStructure::table(cv::Mat img,
std
::
vector
<
int
>
ocr_box
;
for
(
int
j
=
0
;
j
<
ocr_result
.
size
();
j
++
)
{
ocr_box
=
Utility
::
xyxyxyxy2xyxy
(
ocr_result
[
j
].
box
);
ocr_box
[
0
]
=
max
(
0
,
ocr_box
[
0
]
-
expand_pixel
);
ocr_box
[
1
]
=
max
(
0
,
ocr_box
[
1
]
-
expand_pixel
),
ocr_box
[
2
]
=
min
(
img_list
[
i
].
cols
,
ocr_box
[
2
]
+
expand_pixel
);
ocr_box
[
3
]
=
min
(
img_list
[
i
].
rows
,
ocr_box
[
3
]
+
expand_pixel
);
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
[
i
].
cols
,
ocr_box
[
2
]
+
expand_pixel
);
ocr_box
[
3
]
=
std
::
min
(
img_list
[
i
].
rows
,
ocr_box
[
3
]
+
expand_pixel
);
cv
::
Mat
crop_img
=
Utility
::
crop_image
(
img_list
[
i
],
ocr_box
);
rec_img_list
.
push_back
(
crop_img
);
...
...
@@ -132,7 +130,7 @@ 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
<
string
>>
matched
(
structure_boxes
.
size
(),
std
::
vector
<
std
::
vector
<
st
d
::
st
ring
>>
matched
(
structure_boxes
.
size
(),
std
::
vector
<
std
::
string
>
());
std
::
vector
<
int
>
ocr_box
;
...
...
@@ -233,7 +231,7 @@ float PaddleStructure::dis(std::vector<int> &box1, std::vector<int> &box2) {
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
+
min
(
dis_2
,
dis_3
);
return
dis
+
std
::
min
(
dis_2
,
dis_3
);
}
void
PaddleStructure
::
reset_timer
()
{
...
...
deploy/cpp_infer/src/postprocess_op.cpp
浏览文件 @
dd9f48da
...
...
@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <include/clipper.h>
#include <include/postprocess_op.h>
namespace
PaddleOCR
{
...
...
@@ -352,6 +351,25 @@ std::vector<std::vector<std::vector<int>>> DBPostProcessor::FilterTagDetRes(
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
,
std
::
vector
<
int
>
&
loc_preds_shape
,
...
...
@@ -412,7 +430,7 @@ void TablePostProcessor::Run(
}
}
score
/=
count
;
if
(
isnan
(
score
)
||
rec_boxes
.
size
()
==
0
)
{
if
(
std
::
isnan
(
score
)
||
rec_boxes
.
size
()
==
0
)
{
score
=
-
1
;
}
rec_scores
.
push_back
(
score
);
...
...
@@ -421,6 +439,17 @@ void TablePostProcessor::Run(
}
}
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
,
...
...
@@ -469,12 +498,10 @@ void PicodetPostProcessor::Run(std::vector<StructurePredictResult> &results,
}
this
->
nms
(
bbox_results
[
i
],
this
->
nms_threshold_
);
for
(
auto
box
:
bbox_results
[
i
])
{
box
.
box_float
[
0
]
=
box
.
box_float
[
0
]
/
scale_factor_w
;
box
.
box_float
[
2
]
=
box
.
box_float
[
2
]
/
scale_factor_w
;
box
.
box_float
[
1
]
=
box
.
box_float
[
1
]
/
scale_factor_h
;
box
.
box_float
[
3
]
=
box
.
box_float
[
3
]
/
scale_factor_h
;
box
.
box
=
{(
int
)
box
.
box_float
[
0
],
(
int
)
box
.
box_float
[
1
],
(
int
)
box
.
box_float
[
2
],
(
int
)
box
.
box_float
[
3
]};
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
);
}
}
...
...
@@ -501,13 +528,13 @@ PicodetPostProcessor::disPred2Bbox(std::vector<float> bbox_pred, int label,
dis_pred
[
i
]
=
dis
;
}
float
xmin
_float
=
(
std
::
max
)(
ct_x
-
dis_pred
[
0
],
.0
f
);
float
ymin
_float
=
(
std
::
max
)(
ct_y
-
dis_pred
[
1
],
.0
f
);
float
xmax
_float
=
(
std
::
min
)(
ct_x
+
dis_pred
[
2
],
(
float
)
im_shape
[
1
]);
float
ymax
_float
=
(
std
::
min
)(
ct_y
+
dis_pred
[
3
],
(
float
)
im_shape
[
0
]);
float
xmin
=
(
std
::
max
)(
ct_x
-
dis_pred
[
0
],
.0
f
);
float
ymin
=
(
std
::
max
)(
ct_y
-
dis_pred
[
1
],
.0
f
);
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
_float
=
{
xmin_float
,
ymin_float
,
xmax_float
,
ymax_float
};
result_item
.
box
=
{
xmin
,
ymin
,
xmax
,
ymax
};
result_item
.
type
=
this
->
label_list_
[
label
];
result_item
.
confidence
=
score
;
...
...
@@ -530,8 +557,7 @@ void PicodetPostProcessor::nms(std::vector<StructurePredictResult> &input_boxes,
if
(
picked
[
j
]
==
0
)
{
continue
;
}
float
iou
=
Utility
::
iou
(
input_boxes
[
i
].
box_float
,
input_boxes
[
j
].
box_float
);
float
iou
=
Utility
::
iou
(
input_boxes
[
i
].
box
,
input_boxes
[
j
].
box
);
if
(
iou
>
nms_threshold
)
{
picked
[
j
]
=
0
;
}
...
...
deploy/cpp_infer/src/preprocess_op.cpp
浏览文件 @
dd9f48da
...
...
@@ -12,21 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "opencv2/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include "paddle_api.h"
#include "paddle_inference_api.h"
#include <chrono>
#include <iomanip>
#include <iostream>
#include <ostream>
#include <vector>
#include <cstring>
#include <fstream>
#include <numeric>
#include <include/preprocess_op.h>
namespace
PaddleOCR
{
...
...
@@ -69,13 +54,13 @@ void Normalize::Run(cv::Mat *im, const std::vector<float> &mean,
}
void
ResizeImgType0
::
Run
(
const
cv
::
Mat
&
img
,
cv
::
Mat
&
resize_img
,
st
ring
limit_type
,
int
limit_side_len
,
float
&
ratio_h
,
float
&
ratio_w
,
bool
use_tensorrt
)
{
st
d
::
string
limit_type
,
int
limit_side_len
,
float
&
ratio_
h
,
float
&
ratio_
w
,
bool
use_tensorrt
)
{
int
w
=
img
.
cols
;
int
h
=
img
.
rows
;
float
ratio
=
1.
f
;
if
(
limit_type
==
"min"
)
{
int
min_wh
=
min
(
h
,
w
);
int
min_wh
=
std
::
min
(
h
,
w
);
if
(
min_wh
<
limit_side_len
)
{
if
(
h
<
w
)
{
ratio
=
float
(
limit_side_len
)
/
float
(
h
);
...
...
@@ -84,7 +69,7 @@ void ResizeImgType0::Run(const cv::Mat &img, cv::Mat &resize_img,
}
}
}
else
{
int
max_wh
=
max
(
h
,
w
);
int
max_wh
=
std
::
max
(
h
,
w
);
if
(
max_wh
>
limit_side_len
)
{
if
(
h
>
w
)
{
ratio
=
float
(
limit_side_len
)
/
float
(
h
);
...
...
@@ -97,8 +82,8 @@ void ResizeImgType0::Run(const cv::Mat &img, cv::Mat &resize_img,
int
resize_h
=
int
(
float
(
h
)
*
ratio
);
int
resize_w
=
int
(
float
(
w
)
*
ratio
);
resize_h
=
max
(
int
(
round
(
float
(
resize_h
)
/
32
)
*
32
),
32
);
resize_w
=
max
(
int
(
round
(
float
(
resize_w
)
/
32
)
*
32
),
32
);
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
);
...
...
deploy/cpp_infer/src/structure_layout.cpp
浏览文件 @
dd9f48da
...
...
@@ -94,7 +94,7 @@ void StructureLayoutRecognizer::Run(cv::Mat img,
}
void
StructureLayoutRecognizer
::
LoadModel
(
const
std
::
string
&
model_dir
)
{
Analysis
Config
config
;
paddle_infer
::
Config
config
;
if
(
Utility
::
PathExists
(
model_dir
+
"/inference.pdmodel"
)
&&
Utility
::
PathExists
(
model_dir
+
"/inference.pdiparams"
))
{
config
.
SetModel
(
model_dir
+
"/inference.pdmodel"
,
...
...
@@ -105,7 +105,7 @@ void StructureLayoutRecognizer::LoadModel(const std::string &model_dir) {
model_dir
+
"/model.pdiparams"
);
}
else
{
std
::
cerr
<<
"[ERROR] not find model.pdiparams or inference.pdiparams in "
<<
model_dir
<<
endl
;
<<
model_dir
<<
std
::
endl
;
exit
(
1
);
}
...
...
@@ -144,6 +144,6 @@ void StructureLayoutRecognizer::LoadModel(const std::string &model_dir) {
config
.
EnableMemoryOptim
();
config
.
DisableGlogInfo
();
this
->
predictor_
=
CreatePredictor
(
config
);
this
->
predictor_
=
paddle_infer
::
CreatePredictor
(
config
);
}
}
// namespace PaddleOCR
deploy/cpp_infer/src/structure_table.cpp
浏览文件 @
dd9f48da
...
...
@@ -34,7 +34,7 @@ void StructureTableRecognizer::Run(
beg_img_no
+=
this
->
table_batch_num_
)
{
// preprocess
auto
preprocess_start
=
std
::
chrono
::
steady_clock
::
now
();
int
end_img_no
=
min
(
img_num
,
beg_img_no
+
this
->
table_batch_num_
);
int
end_img_no
=
std
::
min
(
img_num
,
beg_img_no
+
this
->
table_batch_num_
);
int
batch_num
=
end_img_no
-
beg_img_no
;
std
::
vector
<
cv
::
Mat
>
norm_img_batch
;
std
::
vector
<
int
>
width_list
;
...
...
@@ -118,7 +118,7 @@ void StructureTableRecognizer::Run(
}
void
StructureTableRecognizer
::
LoadModel
(
const
std
::
string
&
model_dir
)
{
Analysis
Config
config
;
paddle_infer
::
Config
config
;
config
.
SetModel
(
model_dir
+
"/inference.pdmodel"
,
model_dir
+
"/inference.pdiparams"
);
...
...
@@ -157,6 +157,6 @@ void StructureTableRecognizer::LoadModel(const std::string &model_dir) {
config
.
EnableMemoryOptim
();
config
.
DisableGlogInfo
();
this
->
predictor_
=
CreatePredictor
(
config
);
this
->
predictor_
=
paddle_infer
::
CreatePredictor
(
config
);
}
}
// namespace PaddleOCR
deploy/cpp_infer/src/utility.cpp
浏览文件 @
dd9f48da
...
...
@@ -66,7 +66,7 @@ void Utility::VisualizeBboxes(const cv::Mat &srcimg,
}
void
Utility
::
VisualizeBboxes
(
const
cv
::
Mat
&
srcimg
,
StructurePredictResult
&
structure_result
,
const
StructurePredictResult
&
structure_result
,
const
std
::
string
&
save_path
)
{
cv
::
Mat
img_vis
;
srcimg
.
copyTo
(
img_vis
);
...
...
@@ -281,7 +281,7 @@ void Utility::print_result(const std::vector<OCRPredictResult> &ocr_result) {
}
}
cv
::
Mat
Utility
::
crop_image
(
cv
::
Mat
&
img
,
std
::
vector
<
int
>
&
box
)
{
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
]);
...
...
@@ -298,6 +298,12 @@ cv::Mat Utility::crop_image(cv::Mat &img, std::vector<int> &box) {
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
)
{
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录