mpp icon indicating copy to clipboard operation
mpp copied to clipboard

BGR图像进行JPEG压缩失败

Open cz000 opened this issue 1 year ago • 23 comments

在3588的板子上,基于ros2系统,使用最新的mpp库,进行bgr图像的单帧压缩。运行时没有报错,但运行结果不能通过其他软件解码。 有一些疑问:

  1. bgr图像是1280*720,数据是1280 * 720 * 3个字节。但看mpi_enc_test.c的代码, 计算的hor_stride和ver_stride按16位对齐后是1280和720,计算frame的时候是按64对齐,得到的frame_size是要比 1280 * 720 * 3要小。那该如何输入bgr图像数据
  2. 哪些编码器的config是必须设置的,哪些是不必要的?
  3. 是用同一个buffer生成frame和packet,还是分别用不同的buffer

测试的代码大概如下:

#include <memory>
#include <rclcpp/rclcpp.hpp>
#include <sensor_msgs/msg/image.hpp>
#include <sensor_msgs/msg/compressed_image.hpp>
#include "rockchip/rk_mpi.h"
#include "rockchip/mpp_buffer.h"

// 对齐宏
#define MPP_ALIGN(x, a)    (((x)+(a)-1)&~((a)-1))
#define MPP_ALIGN_HOR 16   // 水平对齐要求
#define MPP_ALIGN_VER 16   // 垂直对齐要求

class ImageCompressionNode : public rclcpp::Node {
public:
    ImageCompressionNode() : Node("image_compression_node") {
        // 创建订阅者和发布者
        subscription_ = this->create_subscription<sensor_msgs::msg::Image>(
            "input_image", 10,
            std::bind(&ImageCompressionNode::image_callback, this, std::placeholders::_1));
        
        publisher_ = this->create_publisher<sensor_msgs::msg::CompressedImage>(
            "compressed_image", 10);

        // 初始化MPP
        ret = mpp_create(&ctx_, &mpi_);
        if (ret) {
            RCLCPP_ERROR(this->get_logger(), "mpp_create failed");
            return;
        }

        // 配置编码器参数
        MppCodingType codec_type = MPP_VIDEO_CodingMJPEG;
        ret = mpp_init(ctx_, MPP_CTX_ENC, codec_type);
        if (ret) {
            RCLCPP_ERROR(this->get_logger(), "mpp_init failed");
            return;
        }

        // 计算对齐的宽度和高度
        width_ = 1280;
        height_ = 720;
        hor_stride_ = MPP_ALIGN(width_, MPP_ALIGN_HOR);
        ver_stride_ = MPP_ALIGN(height_, MPP_ALIGN_VER);

        // 设置JPEG编码参数
        MppEncConfig config;
        memset(&config, 0, sizeof(config));
        config.width = width_;
        config.height = height_;
        config.hor_stride = hor_stride_;
        config.ver_stride = ver_stride_;
        config.format = MPP_FMT_BGR888;
        config.rc_mode = MPP_ENC_RC_MODE_FIXQP;
        config.quality = 80;

        ret = mpi_->control(ctx_, MPP_ENC_SET_CFG, &config);
        if (ret) {
            RCLCPP_ERROR(this->get_logger(), "Failed to set encoder config");
            return;
        }

        // 设置输入格式
        MppFrameFormat fmt = MPP_FMT_BGR888;
        ret = mpi_->control(ctx_, MPP_ENC_SET_FORMAT, &fmt);
        if (ret) {
            RCLCPP_ERROR(this->get_logger(), "Failed to set input format");
            return;
        }
    }

    ~ImageCompressionNode() {
        if (ctx_) {
            mpp_destroy(ctx_);
            ctx_ = nullptr;
            mpi_ = nullptr;
        }
    }

private:
    void image_callback(const sensor_msgs::msg::Image::SharedPtr msg) {
        if (msg->encoding != "bgr8") {
            RCLCPP_ERROR(this->get_logger(), "Unsupported image encoding: %s", msg->encoding.c_str());
            return;
        }

        // 准备MPP输入buffer
        MppFrame frame = nullptr;
        mpp_frame_init(&frame);
        mpp_frame_set_width(frame, width_);
        mpp_frame_set_height(frame, height_);
        mpp_frame_set_hor_stride(frame, hor_stride_);
        mpp_frame_set_ver_stride(frame, ver_stride_);
        mpp_frame_set_fmt(frame, MPP_FMT_BGR888);

        // 设置输入数据
        MppBuffer input_buf;
        size_t input_size = hor_stride_ * ver_stride_ * 3;  // BGR格式,每像素3字节
        ret = mpp_buffer_get(nullptr, &input_buf, input_size);
        if (ret) {
            RCLCPP_ERROR(this->get_logger(), "Failed to get input buffer");
            mpp_frame_deinit(&frame);
            return;
        }
        
        // 复制图像数据到MPP buffer,需要考虑stride
        void* buf_ptr = mpp_buffer_get_ptr(input_buf);
        uint8_t* src_ptr = msg->data.data();
        uint8_t* dst_ptr = (uint8_t*)buf_ptr;
        
        // 逐行复制,处理stride
        for (uint32_t i = 0; i < height_; i++) {
            memcpy(dst_ptr + i * hor_stride_ * 3, 
                   src_ptr + i * msg->step, 
                   width_ * 3);
        }

        mpp_frame_set_buffer(frame, input_buf);

        // 编码
        MppPacket packet = nullptr;
        ret = mpi_->encode_put_frame(ctx_, frame);
        if (ret) {
            RCLCPP_ERROR(this->get_logger(), "Failed to put frame");
            mpp_buffer_put(input_buf);
            mpp_frame_deinit(&frame);
            return;
        }

        ret = mpi_->encode_get_packet(ctx_, &packet);
        if (ret || !packet) {
            RCLCPP_ERROR(this->get_logger(), "Failed to get packet");
            mpp_buffer_put(input_buf);
            mpp_frame_deinit(&frame);
            return;
        }

        // 获取压缩后的数据
        void* compressed_data = mpp_packet_get_pos(packet);
        size_t compressed_size = mpp_packet_get_length(packet);

        // 创建压缩图像消息
        auto compressed_msg = std::make_shared<sensor_msgs::msg::CompressedImage>();
        compressed_msg->header = msg->header;
        compressed_msg->format = "jpeg";
        compressed_msg->data.resize(compressed_size);
        memcpy(compressed_msg->data.data(), compressed_data, compressed_size);

        // 发布压缩后的图像
        publisher_->publish(*compressed_msg);

        // 清理资源
        mpp_packet_deinit(&packet);
        mpp_buffer_put(input_buf);
        mpp_frame_deinit(&frame);
    }

    rclcpp::Subscription<sensor_msgs::msg::Image>::SharedPtr subscription_;
    rclcpp::Publisher<sensor_msgs::msg::CompressedImage>::SharedPtr publisher_;
    MppCtx ctx_ = nullptr;
    MppApi* mpi_ = nullptr;
    RK_S32 ret = 0;
    
    // 图像参数
    uint32_t width_;
    uint32_t height_;
    uint32_t hor_stride_;    // 水平步幅(对齐后的宽度)
    uint32_t ver_stride_;    // 垂直步幅(对齐后的高度)
};

int main(int argc, char** argv) {
    rclcpp::init(argc, argv);
    auto node = std::make_shared<ImageCompressionNode>();
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}

cz000 avatar Jan 07 '25 06:01 cz000