一组rgb转换函数,支持rgb565/rgb888/xrgb8888之间的数据转换

本文介绍了一种位图颜色空间转换的方法,包括不同颜色模式之间的转换实现,如RGB565到RGB888、RGB565到RGBx8888等,并提供了具体的C语言实现代码。

mybmp.h

/******************************************************************** created: 2012/04/07 filename: mybmp.h author: purpose: *********************************************************************/ #ifndef _mybmp_h__ #define _mybmp_h__ //------------------------------------------------------------------- #ifdef __cplusplus extern "C" { #endif #define UpAlign4(n) (((n) + 3) & ~3) #define UpAlign8(n) (((n) + 7) & ~7) //拷贝数据 void rgb_copy(const void * psrc, void * pdst, int sw, int sh, int dw, int dh, int bpp); void rbg565_copy(const void * psrc, void * pdst, int sw, int sh, int dw, int dh); void rbg888_copy(const void * psrc, void * pdst, int sw, int sh, int dw, int dh); void rbgx8888_copy(const void * psrc, void * pdst, int sw, int sh, int dw, int dh); //行数据翻转 void line_reversal(void * pdata, int w, int h, int bpp); void rgb565_line_reversal(void * p565, int w, int h); void rgb888_line_reversal(void * p888, int w, int h); void rgbx8888_line_reversal(void * p8888, int w, int h); //转换 typedef void * (* RGB_CONVERT_FUN)(const void * psrc, int w, int h); void * rgb565_to_rgb888_buffer(const void * psrc, int w, int h); void * rgb888_to_rgb565_buffer(const void * psrc, int w, int h); void * rgb565_to_rgbx8888_buffer(const void * psrc, int w, int h); void * rgbx8888_to_rgb565_buffer(const void * psrc, int w, int h); void * rgb888_to_rgbx8888_buffer(const void * psrc, int w, int h); void * rgbx8888_to_rgb888_buffer(const void * psrc, int w, int h); RGB_CONVERT_FUN get_convert_func(int frombpp, int tobpp); #ifdef __cplusplus }; #endif //------------------------------------------------------------------- #endif // #ifndef _mybmp_h__
rgb_convert.c

/******************************************************************** created: 2012/05/19 filename: rgb_convert.c author: purpose: *********************************************************************/ //------------------------------------------------------------------- #include <stdlib.h> #include <stdio.h> #include <memory.h> #include "mybmp.h" //------------------------------------------------------------------- //拷贝 void rgb_copy(const void * psrc, void * pdst, int sw, int sh, int dw, int dh, int bpp) { int bytes = UpAlign8(bpp) >> 3; // bpp / 8 int srclinesize = UpAlign4(sw * bytes); int dstlinesize = UpAlign4(dw * bytes); int copylinesize = srclinesize < dstlinesize ? srclinesize : dstlinesize; int copylines = sh < dh ? sh : dh; const unsigned char * psrcline = (const unsigned char *)psrc; const unsigned char * pend = psrcline + copylines * srclinesize; unsigned char * pdstline = (unsigned char *)pdst; while (psrcline < pend) { memcpy(pdstline, psrcline, copylinesize); psrcline += srclinesize; pdstline += dstlinesize; } } void rbg565_copy(const void * psrc, void * pdst, int sw, int sh, int dw, int dh) { rgb_copy(psrc, pdst, sw, sh, dw, dh, 16); } void rbg888_copy(const void * psrc, void * pdst, int sw, int sh, int dw, int dh) { rgb_copy(psrc, pdst, sw, sh, dw, dh, 24); } void rbgx8888_copy(const void * psrc, void * pdst, int sw, int sh, int dw, int dh) { rgb_copy(psrc, pdst, sw, sh, dw, dh, 32); } //------------------------------------------------------------------- //行数据翻转 void line_reversal(void * pdata, int w, int h, int bpp) { int bytes = UpAlign8(bpp) >> 3; // bpp / 8 int linesize = UpAlign4(w * bytes);//4的整数倍 int copylines = h >> 1; int i; unsigned char * pline = NULL; unsigned char * pline1 = NULL; unsigned char * linebuffer = NULL; if (pdata && w > 0 && h > 1) {//至少两行才需要翻转 linebuffer = (unsigned char *)malloc(linesize); if (linebuffer) { pline = (unsigned char *)pdata; pline1 = (unsigned char *)pdata + linesize * (h - 1); for (i = 0; i < copylines; i++) { memcpy(linebuffer, pline, linesize); memcpy(pline, pline1, linesize); memcpy(pline1, linebuffer, linesize); pline += linesize; pline1 -= linesize; } free(linebuffer); } } } void rgb565_line_reversal(void * p565, int w, int h) { line_reversal(p565, w, h, 16); } void rgb888_line_reversal(void * p888, int w, int h) { line_reversal(p888, w, h, 24); } void rgbx8888_line_reversal(void * p8888, int w, int h) { line_reversal(p8888, w, h, 32); } //------------------------------------------------------------------- //转换 static int rgb565_to_rgb888(const void * psrc, int w, int h, void * pdst) { int srclinesize = UpAlign4(w * 2); int dstlinesize = UpAlign4(w * 3); const unsigned char * psrcline; const unsigned short * psrcdot; unsigned char * pdstline; unsigned char * pdstdot; int i,j; if (!psrc || !pdst || w <= 0 || h <= 0) { printf("rgb565_to_rgb888 : parameter error\n"); return -1; } psrcline = (const unsigned char *)psrc; pdstline = (unsigned char *)pdst; for (i=0; i<h; i++) { psrcdot = (const unsigned short *)psrcline; pdstdot = pdstline; for (j=0; j<w; j++) { //565 b|g|r -> 888 r|g|b *pdstdot++ = (unsigned char)(((*psrcdot) >> 0 ) << 3); *pdstdot++ = (unsigned char)(((*psrcdot) >> 5 ) << 2); *pdstdot++ = (unsigned char)(((*psrcdot) >> 11) << 3); psrcdot++; } psrcline += srclinesize; pdstline += dstlinesize; } return 0; } static int rgb888_to_rgb565(const void * psrc, int w, int h, void * pdst) { int srclinesize = UpAlign4(w * 3); int dstlinesize = UpAlign4(w * 2); const unsigned char * psrcline; const unsigned char * psrcdot; unsigned char * pdstline; unsigned short * pdstdot; int i,j; if (!psrc || !pdst || w <= 0 || h <= 0) { printf("rgb888_to_rgb565 : parameter error\n"); return -1; } psrcline = (const unsigned char *)psrc; pdstline = (unsigned char *)pdst; for (i=0; i<h; i++) { psrcdot = psrcline; pdstdot = (unsigned short *)pdstline; for (j=0; j<w; j++) { //888 r|g|b -> 565 b|g|r *pdstdot = (((psrcdot[0] >> 3) & 0x1F) << 0)//r |(((psrcdot[1] >> 2) & 0x3F) << 5)//g |(((psrcdot[2] >> 3) & 0x1F) << 11);//b psrcdot += 3; pdstdot++; } psrcline += srclinesize; pdstline += dstlinesize; } return 0; } static int rgb565_to_rgbx8888(const void * psrc, int w, int h, void * pdst) { int srclinesize = UpAlign4(w * 2); int dstlinesize = UpAlign4(w * 4); const unsigned char * psrcline; const unsigned short * psrcdot; unsigned char * pdstline; unsigned char * pdstdot; int i,j; if (!psrc || !pdst || w <= 0 || h <= 0) { printf("rgb565_to_rgbx8888 : parameter error\n"); return -1; } psrcline = (const unsigned char *)psrc; pdstline = (unsigned char *)pdst; for (i=0; i<h; i++) { psrcdot = (const unsigned short *)psrcline; pdstdot = pdstline; for (j=0; j<w; j++) { pdstdot++; *pdstdot++ = (unsigned char)(((*psrcdot) >> 0 ) << 3); *pdstdot++ = (unsigned char)(((*psrcdot) >> 5 ) << 2); *pdstdot++ = (unsigned char)(((*psrcdot) >> 11) << 3); psrcdot++; } psrcline += srclinesize; pdstline += dstlinesize; } return 0; } static int rgbx8888_to_rgb565(const void * psrc, int w, int h, void * pdst) { int srclinesize = UpAlign4(w * 4); int dstlinesize = UpAlign4(w * 2); const unsigned char * psrcline; const unsigned char * psrcdot; unsigned char * pdstline; unsigned short * pdstdot; int i,j; if (!psrc || !pdst || w <= 0 || h <= 0) { printf("rgbx8888_to_rgb565 : parameter error\n"); return -1; } psrcline = (const unsigned char *)psrc; pdstline = (unsigned char *)pdst; for (i=0; i<h; i++) { psrcdot = psrcline; pdstdot = (unsigned short *)pdstline; for (j=0; j<w; j++) { //888 r|g|b -> 565 b|g|r *pdstdot = (((psrcdot[1] >> 3) & 0x1F) << 0)//r |(((psrcdot[2] >> 2) & 0x3F) << 5)//g |(((psrcdot[3] >> 3) & 0x1F) << 11);//b psrcdot += 4; pdstdot++; } psrcline += srclinesize; pdstline += dstlinesize; } return 0; } static int rgb888_to_rgbx8888(const void * psrc, int w, int h, void * pdst) { int srclinesize = UpAlign4(w * 3); int dstlinesize = UpAlign4(w * 4); const unsigned char * psrcline; const unsigned char * psrcdot; unsigned char * pdstline; unsigned char * pdstdot; int i,j; if (!psrc || !pdst || w <= 0 || h <= 0) { printf("rgb888_to_rgbx8888 : parameter error\n"); return -1; } psrcline = (const unsigned char *)psrc; pdstline = (unsigned char *)pdst; for (i=0; i<h; i++) { psrcdot = psrcline; pdstdot = pdstline; for (j=0; j<w; j++) { *pdstdot++ = 0; *pdstdot++ = *psrcdot++; *pdstdot++ = *psrcdot++; *pdstdot++ = *psrcdot++; } psrcline += srclinesize; pdstline += dstlinesize; } return 0; } static int rgbx8888_to_rgb888(const void * psrc, int w, int h, void * pdst) { int srclinesize = UpAlign4(w * 4); int dstlinesize = UpAlign4(w * 3); const unsigned char * psrcline; const unsigned char * psrcdot; unsigned char * pdstline; unsigned char * pdstdot; int i,j; if (!psrc || !pdst || w <= 0 || h <= 0) { printf("rgbx8888_to_rgb888 : parameter error\n"); return -1; } psrcline = (const unsigned char *)psrc; pdstline = (unsigned char *)pdst; for (i=0; i<h; i++) { psrcdot = psrcline; pdstdot = pdstline; for (j=0; j<w; j++) { psrcdot++; *pdstdot++ = *psrcdot++; *pdstdot++ = *psrcdot++; *pdstdot++ = *psrcdot++; } psrcline += srclinesize; pdstline += dstlinesize; } return 0; } void * rgb565_to_rgb888_buffer(const void * psrc, int w, int h) { int size = h * UpAlign4(w * 3); void * pdst = NULL; if (psrc && w > 0 && h > 0) { pdst = malloc(size); if (pdst) { if (rgb565_to_rgb888(psrc, w, h, pdst)) { free(pdst); pdst = NULL; } } } return pdst; } void * rgb888_to_rgb565_buffer(const void * psrc, int w, int h) { int size = h * UpAlign4(w * 2); void * pdst = NULL; if (psrc && w > 0 && h > 0) { pdst = malloc(size); if (pdst) { if (rgb888_to_rgb565(psrc, w, h, pdst)) { free(pdst); pdst = NULL; } } } return pdst; } void * rgb565_to_rgbx8888_buffer(const void * psrc, int w, int h) { int size = h * UpAlign4(w * 4); void * pdst = NULL; if (psrc && w > 0 && h > 0) { pdst = malloc(size); if (pdst) { if (rgb565_to_rgbx8888(psrc, w, h, pdst)) { free(pdst); pdst = NULL; } } } return pdst; } void * rgbx8888_to_rgb565_buffer(const void * psrc, int w, int h) { int size = h * UpAlign4(w * 2); void * pdst = NULL; if (psrc && w > 0 && h > 0) { pdst = malloc(size); if (pdst) { if (rgbx8888_to_rgb565(psrc, w, h, pdst)) { free(pdst); pdst = NULL; } } } return pdst; } void * rgb888_to_rgbx8888_buffer(const void * psrc, int w, int h) { int size = h * UpAlign4(w * 4); void * pdst = NULL; if (psrc && w > 0 && h > 0) { pdst = malloc(size); if (pdst) { if (rgb888_to_rgbx8888(psrc, w, h, pdst)) { free(pdst); pdst = NULL; } } } return pdst; } void * rgbx8888_to_rgb888_buffer(const void * psrc, int w, int h) { int size = h * UpAlign4(w * 3); void * pdst = NULL; if (psrc && w > 0 && h > 0) { pdst = malloc(size); if (pdst) { if (rgbx8888_to_rgb888(psrc, w, h, pdst)) { free(pdst); pdst = NULL; } } } return pdst; } static const RGB_CONVERT_FUN g_convert_func[3][3] = { {NULL, rgb565_to_rgb888_buffer, rgb565_to_rgbx8888_buffer}, {rgb888_to_rgb565_buffer, NULL, rgb888_to_rgbx8888_buffer}, {rgbx8888_to_rgb565_buffer, rgbx8888_to_rgb888_buffer, NULL} }; RGB_CONVERT_FUN get_convert_func(int frombpp, int tobpp) { RGB_CONVERT_FUN func_ptr = NULL; frombpp = UpAlign8(frombpp) / 8 - 2; tobpp = UpAlign8(tobpp) / 8 - 2; if ((frombpp >= 0 && frombpp <= 2) && (tobpp >= 0 && tobpp <= 2)) { func_ptr = g_convert_func[frombpp][tobpp]; } return func_ptr; } //-------------------------------------------------------------------




#include <iostream> #include <fcntl.h> #include <sys/mman.h> #include <unistd.h> #include <cstring> #include <vector> #include <iomanip> #include <fstream> #include "stb_image_write.h" // 需要提前下载 stb_image_write.h 到项目目录 #include <libcamera/libcamera.h> #include <libcamera/framebuffer.h> #include <libcamera/request.h> using namespace libcamera; //using namespace std::chrono_literals; static int frame_count = 0; static std::shared_ptr<Camera> camera; #include <vector> void xrgb8888_to_rgb(const uint8_t *src, int width, int height, std::vector<uint8_t> &rgb) { rgb.resize(width * height * 3); for (int i = 0; i < width * height; ++i) { // src[i * 4 + 0] = X (ignored) uint8_t r = src[i * 4 + 1]; uint8_t g = src[i * 4 + 2]; uint8_t b = src[i * 4 + 3]; rgb[i * 3 + 0] = r; rgb[i * 3 + 1] = g; rgb[i * 3 + 2] = b; // uint8_t b = src[i * 4 + 1]; // uint8_t g = src[i * 4 + 2]; // uint8_t r = src[i * 4 + 3]; // rgb[i * 3] = r; // rgb[i * 3 + 1] = g; // rgb[i * 3 + 2] = b; } } void nv12_to_rgb(const uint8_t *y_plane, const uint8_t *uv_plane, int width, int height, std::vector<uint8_t> &rgb) { rgb.resize(width * height * 3); for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { int yp = y * width + x; int up = (y / 2) * width + (x & ~1); int u = uv_plane[up]; int v = uv_plane[up + 1]; int16_t y_val = y_plane[yp] - 16; int16_t u_val = u - 128; int16_t v_val = v - 128; int r = (298 * y_val + 409 * v_val + 128) >> 8; int g = (298 * y_val - 100 * u_val - 208 * v_val + 128) >> 8; int b = (298 * y_val + 516 * u_val + 128) >> 8; r = std::min(std::max(r, 0), 255); g = std::min(std::max(g, 0), 255); b = std::min(std::max(b, 0), 255); rgb[(y * width + x) * 3 + 0] = r; rgb[(y * width + x) * 3 + 1] = g; rgb[(y * width + x) * 3 + 2] = b; } } } static void requestComplete(Request *request) { if (request->status() == Request::RequestCancelled)//检测请求 return; const std::map<const Stream *, FrameBuffer *> &buffers = request->buffers(); for (auto bufferPair : buffers) { FrameBuffer *buffer = bufferPair.second;//当前数据流关联的帧缓冲区指针 // 获取分辨率 int width = 800;//metadata.size.width; int height = 600;//metadata.size.height; // // 获取 DMA-BUF 文件描述符(XRGB8888 是单平面格式) int fd = buffer->planes()[0].fd.get();//获取 DMA 缓冲区的文件描述符(File Descriptor) size_t length = buffer->planes()[0].length;//获取该平面的数据长度(字节数) // // mmap 映射内存 // uint8_t *data = static_cast<uint8_t *>(mmap(nullptr, length, PROT_READ, MAP_SHARED, fd, 0)); // //nullptr系统选择 length平面的数据长度 PROT_READ表示只读 MAP_SHARED标志位 // if (data == MAP_FAILED) { // std::cerr << "Failed to mmap frame data." << std::endl; // continue; // } // stbi__write_context s; // // 转换并保存图像 // std::vector<uint8_t> rgb_data;//存储转换后的RGB格式图像数据 // xrgb8888_to_rgb(data, width, height, rgb_data);//开始转换 // int success = stbi__start_write_file(&s, "output.jpg"); // if (!success) { // // 错误处理(文件无法打开) // perror("无法打开文件"); // return; // } // char filename[64]; // snprintf(filename, sizeof(filename), "frame_%04d.jpg", frame_count++); // stbi_write_jpg(filename, width, height, 3, rgb_data.data(), 95); // 文件名 宽 高 通道数3(RGB) 质量 95 // std::cout << "Saved image: " << filename << std::endl; //打印 // 保存图像到文件 try { int fd = buffer->planes()[0].fd.get(); size_t len = buffer->planes()[0].length; void* data = mmap(nullptr, len, PROT_READ, MAP_SHARED, fd, 0); if (data == MAP_FAILED) { throw std::runtime_error("Failed to mmap frame data"); } printf("1111111111111\n"); std::ofstream file("mydata.yuv", std::ios::out | std::ios::binary); // 使用 std::ofstream printf("2222222222222\n"); if (!file) { throw std::runtime_error("Failed to open file for writing"); } file.write(static_cast<char*>(data), len); file.close(); if (munmap(data, len) != 0) { throw std::runtime_error("Failed to unmap memory"); } // 解除映射 munmap(data, length);//平面长度和内存大小接触映射 } catch (const std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; } // const FrameMetadata &metadata = buffer->metadata(); // std::cout << " seq: " << std::setw(6) << std::setfill('0') << metadata.sequence << " planes: " << metadata.planes().size() << std::endl; // // 检查是否是 NV12 格式 // if (metadata.planes().size() < 2) { // std::cerr << "Expected at least 2 planes for NV12 format." << std::endl; // continue; // } // // 获取两个 plane 的 DMA-BUF 文件描述符 // int fd_y = buffer->planes()[0].fd.get(); // Y 平面 // int fd_uv = buffer->planes()[1].fd.get(); // UV 平面 // // 获取每个平面的大小 // size_t length_y = buffer->planes()[0].length; // size_t length_uv = buffer->planes()[1].length; // // mmap 映射内存 // uint8_t *y_data = static_cast<uint8_t *>(mmap(nullptr, length_y, PROT_READ, MAP_SHARED, fd_y, 0)); // uint8_t *uv_data = static_cast<uint8_t *>(mmap(nullptr, length_uv, PROT_READ, MAP_SHARED, fd_uv, 0)); // if (y_data == MAP_FAILED || uv_data == MAP_FAILED) { // std::cerr << "Failed to mmap frame data." << std::endl; // continue; // } // // 假设已知分辨率为 640x480(也可以从 metadata 或 streamConfig 获取) // int width = 800;//metadata.size.width; // int height = 600;//metadata.size.height; // // 转换并保存图像 // std::vector<uint8_t> rgb_data; // nv12_to_rgb(y_data, uv_data, width, height, rgb_data); // char filename[64]; // snprintf(filename, sizeof(filename), "frame_%04d.jpg", frame_count++); // stbi_write_jpg(filename, width, height, 3, rgb_data.data(), 95); // 95 表示质量 // std::cout << "Saved image: " << filename << std::endl; // // 解除映射 // munmap(y_data, length_y); // munmap(uv_data, length_uv); } request->reuse(Request::ReuseBuffers); camera->queueRequest(request); //std::this_thread::sleep_for(3000ms); } // static void requestComplete(Request *request) // { // if (request->status() == Request::RequestCancelled) // return; // const std::map<const Stream *, FrameBuffer *> &buffers = request->buffers(); // for (auto bufferPair : buffers) { // FrameBuffer *buffer = bufferPair.second; // const FrameMetadata &metadata = buffer->metadata(); // std::cout << " seq: " << std::setw(6) << std::setfill('0') << metadata.sequence << " bytesused: "; // unsigned int nplane = 0; // for (const FrameMetadata::Plane &plane : metadata.planes()) // { // std::cout << plane.bytesused; // if (++nplane < metadata.planes().size()) std::cout << "/"; // } // std::cout << std::endl; // } // request->reuse(Request::ReuseBuffers); // camera->queueRequest(request); // //std::this_thread::sleep_for(3000ms); // } int main() { //初始化摄像头 std::unique_ptr<CameraManager> cm = std::make_unique<CameraManager>(); cm->start(); for (auto const &camera : cm->cameras()) {//获取所有的摄像头 std::cout << camera->id() << std::endl;//输出摄像头设备 } //获取可用设备 auto cameras = cm->cameras();//获取所有的摄像头 if (cameras.empty()) {//检查列表中是否有摄像头 std::cout << "No cameras were identified on the system." << std::endl; cm->stop();//停止 return EXIT_FAILURE; } //选择摄像头 std::string cameraId = cameras[0]->id();//从摄像头设备列表中选取首个可用设备 camera = cm->get(cameraId);//通过预先获取的摄像头 ID 从管理器中提取对应设备对象 /* * Note that `camera` may not compare equal to `cameras[0]`. * In fact, it might simply be a `nullptr`, as the particular * device might have disappeared (and reappeared) in the meantime. */ camera->acquire();//锁定当前设备,不让其他进程或线程使用,避免造成干扰 std::unique_ptr<CameraConfiguration> config = camera->generateConfiguration( { StreamRole::Viewfinder } );//unique_ptr系统自动管理生命周期 StreamRole(流角色)::Viewfinder(低延迟)camera->generateConfiguration生成摄像头设备流配置的核心函数 StreamConfiguration &streamConfig = config->at(0);//访问摄像头配置对象中的第一个数据流配置 std::cout << "Default viewfinder configuration is: " << streamConfig.toString() << std::endl; //streamConfig.pixelFormat = formats::NV12; int width = streamConfig.size.width; int height = streamConfig.size.height; config->validate();//验证摄像头配置参数有效性的关键函数 std::cout << "Validated viewfinder configuration is: " << streamConfig.toString() << std::endl; std::cout << "Current pixel format: " << streamConfig.pixelFormat.toString() << std::endl; // 打印支持的格式 // std::cout << "Supported pixel formats:" << std::endl; // for (const PixelFormat &fmt : streamConfig.supportedPixelFormats()) { // std::cout << " - " << fmt.toString() << std::endl; // } camera->configure(config.get());//配置器件保持config存活??????? FrameBufferAllocator *allocator = new FrameBufferAllocator(camera);//帧缓冲区 for (StreamConfiguration &cfg : *config) { int ret = allocator->allocate(cfg.stream()); if (ret < 0) { std::cerr << "Can't allocate buffers" << std::endl; return -ENOMEM; } size_t allocated = allocator->buffers(cfg.stream()).size(); std::cout << "Allocated " << allocated << " buffers for stream" << std::endl; } Stream *stream = streamConfig.stream();//从流配置对象获取实际数据流 const std::vector<std::unique_ptr<FrameBuffer>> &buffers = allocator->buffers(stream); std::vector<std::unique_ptr<Request>> requests; for (unsigned int i = 0; i < buffers.size(); ++i) { std::unique_ptr<Request> request = camera->createRequest(); if (!request) { std::cerr << "Can't create request" << std::endl; return -ENOMEM; } const std::unique_ptr<FrameBuffer> &buffer = buffers[i]; int ret = request->addBuffer(stream, buffer.get()); if (ret < 0) { std::cerr << "Can't set buffer for request" << std::endl; return ret; } requests.push_back(std::move(request)); } camera->requestCompleted.connect(requestComplete); camera->start(); for (std::unique_ptr<Request> &request : requests) { camera->queueRequest(request.get()); } getchar(); camera->stop(); allocator->free(stream); delete allocator; camera->release(); camera.reset(); cm->stop(); return 0; }将xrgb8888转换rgb保存到文件中,并且能够使用ffmplay播放
最新发布
06-12
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值