mpp
mpp copied to clipboard
BGR图像进行JPEG压缩失败
在3588的板子上,基于ros2系统,使用最新的mpp库,进行bgr图像的单帧压缩。运行时没有报错,但运行结果不能通过其他软件解码。 有一些疑问:
- 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图像数据
- 哪些编码器的config是必须设置的,哪些是不必要的?
- 是用同一个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;
}