【VOFA+上位机图传-V4L2】

1..RGB565->RGB888->JPEG->VOFA--大概的流程

头文件

#ifndef _SOCKET_CLINET_H_
#define _SOCKET_CLINET_H_

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "main.h"

enum ImgFormat {
    Format_Invalid,
    Format_Mono,
    Format_MonoLSB,
    Format_Indexed8,
    Format_RGB32,
    Format_ARGB32,
    Format_ARGB32_Premultiplied,
    Format_RGB16,
    Format_ARGB8565_Premultiplied,
    Format_RGB666,
    Format_ARGB6666_Premultiplied,
    Format_RGB555,
    Format_ARGB8555_Premultiplied,
    Format_RGB888,
    Format_RGB444,
    Format_ARGB4444_Premultiplied,
    Format_RGBX8888,
    Format_RGBA8888,
    Format_RGBA8888_Premultiplied,
    Format_BGR30,
    Format_A2BGR30_Premultiplied,
    Format_RGB30,
    Format_A2RGB30_Premultiplied,
    Format_Alpha8,
    Format_Grayscale8,
    
    // 以下格式发送时,IMG_WIDTH和IMG_HEIGHT不需要强制指定,设置为-1即可
    Format_BMP,
    Format_GIF,
    Format_JPG,
    Format_PNG,
    Format_PBM,
    Format_PGM,
    Format_PPM,
    Format_XBM,
    Format_XPM,
    Format_SVG,
};
// RGB565转JPEG内存数组
int rgb565_to_jpeg(const uint16_t *rgb565_data, int width, int height, 
                  uint8_t **jpeg_buffer, size_t *jpeg_size, int quality);

 // 校验JPEG数据
int verify_jpeg_data(const uint8_t *jpeg_buffer, size_t jpeg_size);

.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <jpeglib.h>
#include "main.h"
#include "show_jpeg_image.h"
#include <setjmp.h>
// RGB565转RGB888
void rgb565_to_rgb888(uint16_t rgb565, uint8_t *r, uint8_t *g, uint8_t *b) {
    *r = ((rgb565 >> 11) & 0x1F) << 3;
    *g = ((rgb565 >> 5) & 0x3F) << 2;
    *b = (rgb565 & 0x1F) << 3;
}

// RGB888转RGB565
uint16_t rgb888_to_rgb565(uint8_t r, uint8_t g, uint8_t b) {
    return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
}

// 自定义内存目标管理器(用于编码JPEG到内存)
typedef struct {
    struct jpeg_destination_mgr pub;
    JOCTET *buffer;
    size_t bufsize;
    size_t written;
} my_destination_mgr;

// 初始化目标缓冲区
static void init_destination(j_compress_ptr cinfo) {
    my_destination_mgr *dest = (my_destination_mgr *)cinfo->dest;
    dest->bufsize = 102400; // 初始32KB
    dest->buffer = (JOCTET *) (*cinfo->mem->alloc_small)(
        (j_common_ptr)cinfo, JPOOL_IMAGE, dest->bufsize);
    dest->pub.next_output_byte = dest->buffer;
    dest->pub.free_in_buffer = dest->bufsize;
}

// 缓冲区满时扩展
static boolean empty_output_buffer(j_compress_ptr cinfo) {
    my_destination_mgr *dest = (my_destination_mgr *)cinfo->dest;
    size_t oldsize = dest->bufsize;
    dest->bufsize *= 2; // 每次扩展一倍
    
    dest->buffer = (JOCTET *) (*cinfo->mem->alloc_small)(
        (j_common_ptr)cinfo, JPOOL_IMAGE, dest->bufsize);
    
    memcpy(dest->buffer, dest->pub.next_output_byte - oldsize, oldsize);
    dest->pub.next_output_byte = dest->buffer + oldsize;
    dest->pub.free_in_buffer = dest->bufsize - oldsize;
    return TRUE;
}

// 终止目标处理
static void term_destination(j_compress_ptr cinfo) {
    my_destination_mgr *dest = (my_destination_mgr *)cinfo->dest;
    dest->written = dest->bufsize - dest->pub.free_in_buffer;
}

// 自定义内存源管理器(用于从内存解码JPEG)
typedef struct {
    struct jpeg_source_mgr pub;
    const JOCTET *buffer;
    size_t buffer_size;
    boolean start_of_file;
} my_source_mgr;

// 初始化源缓冲区
static void init_source(j_decompress_ptr cinfo) {
    my_source_mgr *src = (my_source_mgr *)cinfo->src;
    src->start_of_file = TRUE;
}

// 填充输入缓冲区(本例中数据已全部在内存中)
static boolean fill_input_buffer(j_decompress_ptr cinfo) {
    // 这种情况不会发生,因为我们一次性提供所有数据
    return TRUE;
}

// 跳过输入数据
static void skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
    my_source_mgr *src = (my_source_mgr *)cinfo->src;
    if (num_bytes > 0) {
        while (num_bytes > (long)src->pub.bytes_in_buffer) {
            num_bytes -= (long)src->pub.bytes_in_buffer;
            src->pub.next_input_byte = NULL;
            src->pub.bytes_in_buffer = 0;
            if (!fill_input_buffer(cinfo))
                return;
        }
        src->pub.next_input_byte += num_bytes;
        src->pub.bytes_in_buffer -= num_bytes;
    }
}

// 终止源处理
static void term_source(j_decompress_ptr cinfo) {
    // 无需操作,数据在内存中
}

// 自定义错误处理
struct my_error_mgr {
    struct jpeg_error_mgr pub;
    jmp_buf setjmp_buffer;
};

static void my_error_exit(j_common_ptr cinfo) {
    struct my_error_mgr *myerr = (struct my_error_mgr *)cinfo->err;
    (*cinfo->err->output_message)(cinfo);
    longjmp(myerr->setjmp_buffer, 1);
}

// 校验JPEG数据
int verify_jpeg_data(const uint8_t *data, size_t size) {
    if (size < 4) return 0;
    if (data[0] != 0xFF || data[1] != 0xD8) return 0; // SOI标记
    if (data[size-2] != 0xFF || data[size-1] != 0xD9) return 0; // EOI标记
    
    struct jpeg_decompress_struct cinfo;
    struct my_error_mgr jerr;
    
    cinfo.err = jpeg_std_error(&jerr.pub);
    jerr.pub.error_exit = my_error_exit;
    
    if (setjmp(jerr.setjmp_buffer)) {
        jpeg_destroy_decompress(&cinfo);
        return 0;
    }
    
    jpeg_create_decompress(&cinfo);
    jpeg_mem_src(&cinfo, data, size);
    
    if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK) {
        jpeg_destroy_decompress(&cinfo);
        return 0;
    }
    
    jpeg_destroy_decompress(&cinfo);
    return 1;
}

// RGB565转JPEG内存数组
int rgb565_to_jpeg(const uint16_t *rgb565_data, int width, int height, 
                  uint8_t **jpeg_buffer, size_t *jpeg_size, int quality) {
    struct jpeg_compress_struct cinfo;
    struct jpeg_error_mgr jerr;
    my_destination_mgr *dest;
    uint8_t *rgb888_data;
    int i, j;
    
    // 分配RGB888缓冲区
    rgb888_data = (uint8_t *)malloc(width * height * 3);
    if (!rgb888_data) return -1;
    
    // 转换为RGB888
    for (i = 0; i < height; i++) {
        for (j = 0; j < width; j++) {
            uint16_t rgb565 = rgb565_data[i * width + j];
            int idx = (i * width + j) * 3;
            rgb565_to_rgb888(rgb565, &rgb888_data[idx], &rgb888_data[idx+1], &rgb888_data[idx+2]);
        }
    }
    
    // 初始化JPEG压缩对象
    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_compress(&cinfo);
    
    // 设置内存目标管理器
    dest = (my_destination_mgr *)malloc(sizeof(my_destination_mgr));
    if (!dest) {
        free(rgb888_data);
        return -1;
    }
    
    cinfo.dest = (struct jpeg_destination_mgr *)dest;
    dest->pub.init_destination = init_destination;
    dest->pub.empty_output_buffer = empty_output_buffer;
    dest->pub.term_destination = term_destination;
    
    // 设置压缩参数
    cinfo.image_width = width;
    cinfo.image_height = height;
    cinfo.input_components = 3;
    cinfo.in_color_space = JCS_RGB;
    
    jpeg_set_defaults(&cinfo);
    jpeg_set_quality(&cinfo, quality, TRUE);
    
    // 执行压缩
    jpeg_start_compress(&cinfo, TRUE);
    
    JSAMPROW row_pointer[1];
    while (cinfo.next_scanline < cinfo.image_height) {
        row_pointer[0] = &rgb888_data[cinfo.next_scanline * width * 3];
        jpeg_write_scanlines(&cinfo, row_pointer, 1);
    }
    
    jpeg_finish_compress(&cinfo);
    
    // 返回结果
    *jpeg_buffer = dest->buffer;
    *jpeg_size = dest->written;
    
    // 清理资源
    free(dest);
    free(rgb888_data);
    jpeg_destroy_compress(&cinfo);
    
    return 0;
}

主函数

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include "socket_client.h"
#define SERVER_PORT        1347              //服务器的端口号
#define SERVER_IP       "192.168.8.101"    //服务器的IP地址
struct sockaddr_in server_addr = {0};
static char buf[2000];
static int sockfd;
static int ret;
static int i;

int jpegmain(void) 
{
   // 测试图像尺寸
    const int width = 1024;
    const int height = 600;
    
    // 分配测试数据内存
    uint16_t *rgb565_original = (uint16_t *)malloc(width * height * sizeof(uint16_t));
    if (!rgb565_original) {
        fprintf(stderr, "内存分配失败\n");
        return 1;
    }
    
    // 生成测试图案(渐变)
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            uint8_t r = (x * 255) / width;
            uint8_t g = (y * 255) / height;
            uint8_t b = ((x ^ y) * 255) / (width > height ? width : height);
            rgb565_original[y * width + x] = rgb888_to_rgb565(r, g, b);
        }
    }
    
    // 步骤1: RGB565转JPEG
    uint8_t *jpeg_buffer = NULL;
    size_t jpeg_size = 0;
    
    if (rgb565_to_jpeg(rgb565_original, width, height, &jpeg_buffer, &jpeg_size, 95) != 0) {
        fprintf(stderr, "RGB565转JPEG失败\n");
        free(rgb565_original);
    }
    
    printf("JPEG转换成功 - 尺寸: %dx%d, 大小: %zu 字节\n", width, height, jpeg_size);
    
    // 强制验证 JPEG 数据//...先验证我们的压缩..到这一步说面我们的压缩正确..
    if (!verify_jpeg_data(jpeg_buffer, jpeg_size)) {
        fprintf(stderr, "JPEG 数据无效!\n");
        // 保存损坏的 JPEG 用于分析
        FILE *fp = fopen("corrupted.jpg", "wb");
        if (fp) {
            fwrite(jpeg_buffer, 1, jpeg_size, fp);
            fclose(fp);
            printf("已保存损坏的 JPEG 到 corrupted.jpg\n");
        }
        free(rgb565_original);
    }
    //开始发送图片到VOFA+代码
    /* 打开套接字,得到套接字描述符 */
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (0 > sockfd) {
        perror("socket error");
    }

    /* 调用connect连接远端服务器 */
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);  //端口号
    inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);//IP地址

    ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if (0 > ret) {
        perror("connect error");
        close(sockfd);
    }
    printf("服务器连接成功...\n\n");
    uint16_t count,countbk;
    //开始打包数据发送
    sprintf(buf,"\nimage:%d,%d,%d,%d,%d\n",1,jpeg_size,-1,-1,Format_JPG);
    ret = send(sockfd,buf,strlen(buf),0);
    if(0 > ret)
    {
        perror("send error_0");
    }
    count = jpeg_size/1024;
    for (i = 0; i < count; i++)
    {
        ret = send(sockfd,&jpeg_buffer[i*1024],1024,0);
        if(0 > ret)
        {
            perror("send error_1");
        }
    }
    countbk = jpeg_size%1024;
    ret = send(sockfd,&jpeg_buffer[i*1024],countbk,0);
    if(0 > ret)
    {
        perror("send error_1");
    }
    sprintf(buf,"0");
    free(rgb565_original);
}

这个通过V4L2摄像头获取的一帧RGB565数据..无伤大雅..只要替换一下参数就可以了

posted @ 2025-06-08 23:26  孤走  阅读(54)  评论(0)    收藏  举报