Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

我已经实现了python的表格识别,我现在需要移植到paddle lite上,已经用opt将表格识别模型转换了,也写了c++demo来进行预测推理,但是与paddleocr上python推理的结果不一样,输入图片的初始化已经和python的操作一样,先将图片的最大边为488,然后进行标准化,然后进行填充为488x488大小,然后进行预测,c++图片处理后的数据和python处理后的数据几乎一样,但推理后的结果还是有很大差别,会不会是opt转换之后会不会对推理结果造成影响 #10605

Open
manongxiaobaigbc opened this issue Dec 2, 2024 · 27 comments
Assignees

Comments

@manongxiaobaigbc
Copy link

为使您的问题得到快速解决,在建立 Issue 前,请您先通过如下方式搜索是否有相似问题: 历史 issue, FAQ 文档, 官方文档

建立 issue 时,为快速解决问题,请您根据使用情况给出如下信息:

  • 标题:简洁、精准描述您的问题,例如“ssd 模型转换报错”
  • 版本、环境信息:
       1)Paddle Lite 版本:请提供您的 Paddle Lite 版本号(如v2.10)或 CommitID
       2)Host 环境:请描述 Host 系统类型、OS 版本,如 Mac OS 10.14、Ubuntu 18.04
  • 模型信息
       1)模型名称
    2)模型链接
  • 复现信息:提供 OPT 使用命令或方法,用于复现问题
  • 问题描述:请详细描述您的问题,同步贴出报错信息、日志/代码关键片段
@manongxiaobaigbc manongxiaobaigbc changed the title 我已经实现了python的表格识别,我现在需要移植到paddle lite上,已经用opt将表格识别模型转换了,也写了c++demo来进行预测推理,但是与paddleocr上python推理的结果不一样 我已经实现了python的表格识别,我现在需要移植到paddle lite上,已经用opt将表格识别模型转换了,也写了c++demo来进行预测推理,但是与paddleocr上python推理的结果不一样,输入图片的初始化已经和python的操作一样,先将图片的最大边为488,然后进行标准化,然后进行填充为488x488大小,然后进行预测,c++图片处理后的数据和python处理后的数据几乎一样,但推理后的结果还是有很大差别,会不会是opt转换之后会不会对推理结果造成影响 Dec 2, 2024
@manongxiaobaigbc
Copy link
Author

paddle lite c++推理结果
table
paddle ocr python推理结果
image

@MuShangCC
Copy link
Collaborator

请描述清楚所用的 Paddle Lite 版本和运行的硬件环境,以及是否能提供模型和demo来复现。

@Whisper-jack
Copy link

Whisper-jack commented Dec 2, 2024

paddlepaddle:2.6.2
paddle-lite:v2.13-rc
paddleOcr:release/2.7
硬件:ARMv8 cpu:hisi mix210
模型:
ch_PP-OCRv3_det_opt.nb
ch_PP-OCRv3_rec_opt.nb
表格模型使用paddleOCR中表格demo的模型ch_ppstructure_mobile_v2.0_SLANet_infer
image

利用opt转换的nb格式,opt版本为v2.13-rc
image

@Whisper-jack
Copy link

【超级会员V6】通过百度网盘分享的文件:Paddle_L...
链接:https://pan.baidu.com/s/1IAkuWrmpBV2lrxSF_-5uaw?pwd=8D43
提取码:8D43
复制这段内容打开「百度网盘APP 即可获取」

@TrioTea
Copy link

TrioTea commented Dec 3, 2024

应该是你的后处理有问题,我的是正常的

@manongxiaobaigbc
Copy link
Author

manongxiaobaigbc commented Dec 3, 2024

你是用c++来识别吗,能给一下demo吗,如何对处理后的数据进行处理 @TrioTea

@TrioTea
Copy link

TrioTea commented Dec 3, 2024

你是用c++来识别吗,能给一下demo吗,如何对处理后的数据进行处理 @TrioTea

//获取输出结果loc_preds和structure_probs
        const float *loc_preds = results.at(0).get_float_data(); //位置预测
        const float *structure_probs = results.at(1).get_float_data(); //结构概率
        // 获取输出结果的维度
        const std::vector<int64_t> loc_preds_shape = results.at(0).get_shape();
        const std::vector<int64_t> structure_probs_shape = results.at(1).get_shape();


        float score = 0.f;      // 总分数
        int count = 0;          // 有效标签计数
        float char_score;   // 当前字符的分数
        int char_idx;       // 当前字符的索引

        for (int step_idx = 0; step_idx < structure_probs_shape[1]; step_idx++) {
            std::vector<int> rec_box; // 当前边界框

            int64_t step_start_idx = step_idx * structure_probs_shape[2];

            char_idx = int(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]]));

            for (int point_idx = 0; point_idx < loc_preds_shape[2]; point_idx++) {
                step_start_idx = step_idx * loc_preds_shape[2] + point_idx;
                float point = loc_preds[step_start_idx];
                // 根据宽度和高度调整坐标
                if (point_idx % 2 == 0) {
                    point = point * float(width);
                } else {
                    point = point * float(height);
                }
                rec_box.push_back((int) point);
            }
            rec_boxes.push_back(rec_box);
            // 累加有效标签计数和分数
            count += 1;
            score += char_score;
            rec_html_tags_idx.push_back(char_idx);
        }

你可以参考一下,另外可以交流一下

@manongxiaobaigbc
Copy link
Author

你是用c++来识别吗,能给一下demo吗,如何对处理后的数据进行处理 @TrioTea

//获取输出结果loc_preds和structure_probs
        const float *loc_preds = results.at(0).get_float_data(); //位置预测
        const float *structure_probs = results.at(1).get_float_data(); //结构概率
        // 获取输出结果的维度
        const std::vector<int64_t> loc_preds_shape = results.at(0).get_shape();
        const std::vector<int64_t> structure_probs_shape = results.at(1).get_shape();


        float score = 0.f;      // 总分数
        int count = 0;          // 有效标签计数
        float char_score;   // 当前字符的分数
        int char_idx;       // 当前字符的索引

        for (int step_idx = 0; step_idx < structure_probs_shape[1]; step_idx++) {
            std::vector<int> rec_box; // 当前边界框

            int64_t step_start_idx = step_idx * structure_probs_shape[2];

            char_idx = int(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]]));

            for (int point_idx = 0; point_idx < loc_preds_shape[2]; point_idx++) {
                step_start_idx = step_idx * loc_preds_shape[2] + point_idx;
                float point = loc_preds[step_start_idx];
                // 根据宽度和高度调整坐标
                if (point_idx % 2 == 0) {
                    point = point * float(width);
                } else {
                    point = point * float(height);
                }
                rec_box.push_back((int) point);
            }
            rec_boxes.push_back(rec_box);
            // 累加有效标签计数和分数
            count += 1;
            score += char_score;
            rec_html_tags_idx.push_back(char_idx);
        }

你可以参考一下,另外可以交流一下
你前面处理图像的操作是怎么样的,是和我一样吗,我的代码在上面百度网盘里,result的定义是std::unique_ptr result(
std::move(predictor_table->GetOutput(0)));吗,

@TrioTea
Copy link

TrioTea commented Dec 3, 2024

我基于安卓demo去写的,参考paddleOCR中的C++代码完成的前处理和后处理

@TrioTea
Copy link

TrioTea commented Dec 3, 2024

我更好奇你有微调表格模型吗,使用你的环境可以正常进行opt量化吗?
paddlepaddle:2.6.2
paddle-lite:v2.13-rc
paddleOcr:release/2.7

@manongxiaobaigbc
Copy link
Author

我更好奇你有微调表格模型吗,使用你的环境可以正常进行opt量化吗? paddlepaddle:2.6.2 paddle-lite:v2.13-rc paddleOcr:release/2.7

我们这可以正常量化,opt 的版本要和paddle lite一样,现在在测试量化完会不会有影响,还差推理后的数据进行处理这一步,能把你完整demo发一下吗,我可以测一下

@manongxiaobaigbc
Copy link
Author

std::vector<std::vector> RunTableModel(
std::shared_ptr predictor_table,
std::map<std::string, double> Config,
cv::Mat img
)
{
// 调整图像大小488
int max_side_len = 488;
cv::Mat img1 = resizeImageToMaxSide(img, max_side_len);

// 图像归一化参数
std::vector<float> mean = {0.485, 0.456, 0.406};
std::vector<float> std = {0.229, 0.224, 0.225};
float scale = 1.f / 255.f;

std::vector<float> scale1 = {1.f / 255.f, 1.f / 255.f, 1.f / 255.f};
// 对图像进行归一化
cv::Mat img2 = normalizeImage(img1, mean, std, scale);
std::cout << "Processed image shape (CHW): " << img2.size() << std::endl;

// 填充488x488
int w = img2.cols;
int h = img2.rows;
float ratio = 1.0f;
int max_wh = std::max(w, h);
if (max_wh > max_side_len) {
    ratio = static_cast<float>(max_side_len) / max_wh;
}
int resize_w = static_cast<int>(w * ratio);
int resize_h = static_cast<int>(h * ratio);
int top = (max_side_len - resize_h) / 2;
int bottom = max_side_len - resize_h - top;
int left = (max_side_len - resize_w) / 2;
int right = max_side_len - resize_w - left;
cv::Mat img3;
cv::copyMakeBorder(img2, img3, 0, bottom+top, 0, right+left, cv::BORDER_CONSTANT, cv::Scalar(255, 255, 255));


// 从 hwc 格式转换为 chw 格式
//cv::Mat img4 = img3.reshape(1, {3, max_side_len, max_side_len});


std::unique_ptr<Tensor> input_tensor0(std::move(predictor_table->GetInput(0)));  // 获取模型输入
input_tensor0->Resize({1, 3, img3.rows, img3.cols});  // 设置输入尺寸,1是batch size, 3是通道数
auto *data0 = input_tensor0->mutable_data<float>();  // 获取可修改的输入数据指针

// 将标准化后的填充图像数据拷贝到模型输入
const float* dimg_padded = reinterpret_cast<const float*>(img3.data);
NeonMeanScale(dimg_padded, data0, img3.rows * img3.cols, mean, std);  // 标准化后传入模型

predictor_table->Run();  // 运行预测器,进行推理

// 获取模型的输出
std::unique_ptr<const Tensor> output_tensor(std::move(predictor_table->GetOutput(0)));

// --------------------------------------------------------------------------------------------
std::unique_ptr<const Tensor> results(std::move(predictor_table->GetOutput(0)));
//获取输出结果loc_preds和structure_probs
const float *loc_preds = results->data<float>();  // 位置预测
const float *structure_probs = results->data<float>();  // 结构概率
// 获取输出结果的维度
const std::vector<int64_t>& loc_preds_shape = results->shape();  // 获取 loc_preds 的维度
const std::vector<int64_t>& structure_probs_shape = results->shape();  // 获取 structure_probs 的维度
std::cout << "loc_preds shape: ";
for (int64_t dim : loc_preds_shape) {
    std::cout << dim << " ";
}
std::cout << std::endl;

std::cout << "structure_probs shape: ";
for (int64_t dim : structure_probs_shape) {
    std::cout << dim << " ";
}
std::cout << std::endl;
float score = 0.f;      // 总分数
int count = 0;          // 有效标签计数
float char_score;   // 当前字符的分数
int char_idx;       // 当前字符的索引
std::vector<int> rec_html_tags_idx;  // 存储标签索引
std::vector<std::vector<int>> rec_boxes;  // 存储边界框坐标
for (int step_idx = 0; step_idx < structure_probs_shape[1]; step_idx++) {
    std::vector<int> rec_box; // 当前边界框

    int64_t step_start_idx = step_idx * structure_probs_shape[2];

    char_idx = int(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]]));

    for (int point_idx = 0; point_idx < loc_preds_shape[2]; point_idx++) {
        step_start_idx = step_idx * loc_preds_shape[2] + point_idx;
        float point = loc_preds[step_start_idx];
        // 根据宽度和高度调整坐标
        if (point_idx % 2 == 0) {
            point = point * float(img.cols);
        } else {
            point = point * float(img.rows);
        }
        rec_box.push_back((int) point);
    }
    rec_boxes.push_back(rec_box);
    // 累加有效标签计数和分数
    count += 1;
    score += char_score;
    rec_html_tags_idx.push_back(char_idx);
}
// 输出最终的结果
std::cout << "Final score: " << score << std::endl;
std::cout << "Number of valid tags: " << count << std::endl;
// 输出 rec_html_tags_idx 和 rec_boxes
std::cout << "rec_html_tags_idx: ";
for (int idx : rec_html_tags_idx) {
    std::cout << idx << " ";
}
std::cout << std::endl;

std::cout << "rec_boxes: " << std::endl;
for (const auto& box : rec_boxes) {
    std::cout << "[ ";
    for (int coordinate : box) {
        std::cout << coordinate << " ";
    }
    std::cout << "]" << std::endl;
}
    // 保存框选后的图像
string output_image_path = "/opt/vw/bin/ctrlb/table.jpg";
cv::imwrite(output_image_path, img);
std::cout << "Saved image with bounding boxes: " << output_image_path << std::endl;


return table_boxes;

}
@TrioTea
改了推测后的结果后推理的图片还是不敬人意,
table

@TrioTea
Copy link

TrioTea commented Dec 4, 2024

@manongxiaobaigbc 预处理所有代码都可参见PaddleOCR项目的C++部分去进行处理,例如图片大小调整、图像填充等都有现成的函数
https://github.com/PaddlePaddle/PaddleOCR/tree/main/deploy/cpp_infer

@manongxiaobaigbc
Copy link
Author

manongxiaobaigbc commented Dec 5, 2024

@TrioTea @MuShangCC
endtable
仿照官方demo然后写paddle lite的demo,最后运行出来的结果还是有很大差距,模型是opt转换后的nb模型,应该是转换后的模型应该不支持那么高精度的,话有没有什么方法可以优化模型

@TrioTea
Copy link

TrioTea commented Dec 5, 2024

@TrioTea @MuShangCC
endtable
仿照官方demo然后写paddle lite的demo,最后运行出来的结果还是有很大差距,模型是opt转换后的nb模型,应该是转换后的模型应该不支持那么高精度的,话有没有什么方法可以优化模型

不过我转化后的精度很高,我不知道你具体是哪里出现了问题

@manongxiaobaigbc
Copy link
Author

manongxiaobaigbc commented Dec 5, 2024

@TrioTea 你读出来的图片有吗,我的后处理和你的代码差不多一样,应该是不会有啥区别,框框是代码逻辑还是运行环境有影响,你的模型是nb的还是转成了什么,可以提供一下,测测
tabledemo.txt
这是我识别table的demo,paddle lite

@TrioTea
Copy link

TrioTea commented Dec 5, 2024

@manongxiaobaigbc
image

@manongxiaobaigbc
Copy link
Author

@manongxiaobaigbc image

可以发一下模型吗

@TrioTea
Copy link

TrioTea commented Dec 5, 2024

@manongxiaobaigbc
Copy link
Author

infer_table.zip
这是我d的模型,你试试,我试了你的结果还是一样,有可能是我的前处理有问题了,能给一下你的完整demo吗,大佬
@TrioTea

@TrioTea
Copy link

TrioTea commented Dec 5, 2024

你的模型没问题,可能主要还是前后处理的问题,你仔细去看看这个里的代码以及相关的前后处理代码,我都是从这里参考的
https://github.com/PaddlePaddle/PaddleOCR/blob/main/deploy/cpp_infer/src/structure_table.cpp
前处理你尽量仿照官方的去写,看你的代码和官方的操作有一些区别

@manongxiaobaigbc
Copy link
Author

@TrioTea 有什么可以加快文字识别速度的方法吗

@TrioTea
Copy link

TrioTea commented Dec 9, 2024

@TrioTea 有什么可以加快文字识别速度的方法吗

模型搞定了?文字识别只能考虑官方说的那几种方案吧

@manongxiaobaigbc
Copy link
Author

可以识别对应了,但文字识别慢,把图片压缩从488改成244,速度没多大提升,现在在想办法提升速度,文字识别要十五秒

@TrioTea 有什么可以加快文字识别速度的方法吗

模型搞定了?文字识别只能考虑官方说的那几种方案吧

@TrioTea
Copy link

TrioTea commented Dec 9, 2024

什么设备啊?

@manongxiaobaigbc
Copy link
Author

manongxiaobaigbc commented Dec 10, 2024

纯cpu跑的,armv8,4线程的,跑表格识别的文字要跑15s,太夸张了
@TrioTea

@manongxiaobaigbc
Copy link
Author

qq 2494159277

要不要加个wx交流下

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants