前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >c/c++:判断数据(stream)是否为JPEG图像快速而准确的方法

c/c++:判断数据(stream)是否为JPEG图像快速而准确的方法

作者头像
10km
发布2019-05-25 20:58:42
1.1K0
发布2019-05-25 20:58:42
举报
文章被收录于专栏:10km的专栏10km的专栏

版权声明:本文为博主原创文章,转载请注明源地址。 https://cloud.tencent.com/developer/article/1433483

JPEG是Joint Photographic Experts Group 的缩写,关于JPEG标准和格式网上有很多文章介绍这里不再多说。

JPEG标准仅仅定义了codec部分, 也就是图片如何压缩为字节流以及重新解码为图片的过程. 标准没有涉及到文件的存储格式. Exif和JFIF格式是最常见,使用最广泛的JPEG文件存储格式,但不代表JPEG只有这两种存储格式。

JPEG比较复杂,是由多段JPEG 标记(JPEG marker)构成的,有的JPEG marker并不是必须的,marker的顺序也没有严格规定,所以只是简单的检查文件头FFD8和结尾FFD9,以及判断是否有JFIFExif,来判断是否为JPEG图像是不严谨的,会造成错判和漏判。

参见我的上一篇博客 《minigui/mgncs:利用LoadBitmapFromMem函数对摄像头MJPEG格式图像解码》遇到的问题就明白只根据上面的几个标记来判断不靠谱了。

下面的check_jpg函数根据JPEG 标准,通过顺序遍历 JPEG 标记,以最终是否找到SOF0,SOF2标记来判断是否为JPEG格式(SOF0,SOF2是图像数据起始标记,一个JPEG图像至少有一个SOF0SOF2标记),就不存在错判和漏判的问题。

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
static inline uint16_t archswap16(uint16_t D) {
    return((D<<8)|(D>>8));
}
// gcc 预定义宏判断大小端
#if __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__
#define arch_swap_be16(X)        archswap16(X)
#else
#define arch_swap_be16(X)        (X)
#endif

/**
 * 从文件流中读取一个大端的16位整数,
 * 读取成功返回true,否则返回false
 */
static bool read_be16 (FILE *stream,uint16_t *value)
{
    if( sizeof(*value) != fread(value, sizeof(*value), 1,stream))
        return false;
    *value = (arch_swap_be16(*value));
    return true;
}

/*
 * Common JPEG markers(JPEG标记定义)
 * 参见 https://en.wikipedia.org/wiki/JPEG
 * */
#define JMK_SOI         0xFFD8     /* Start Of Image */
#define JMK_SOF0        0xFFC0     /* Start Of Frame (baseline DCT) */
#define JMK_SOF2        0xFFC2     /* Start Of Frame (progressive DCT) */
#define JMK_DHT         0xFFC4     /* Define Huffman Table(s) */
#define JMK_DQT         0xFFDB     /* Define Quantization Table(s) */
#define JMK_DRI         0xFFDD     /* Define Restart Interval */
#define JMK_SOS         0xFFDA     /* Start Of Scan */
#define JMK_RST_mask    0xFFD0     /* mask of Restart */
#define JMK_APP_mask    0xFFE0     /* mask of Application-specific */
#define JMK_COM         0xFFFE     /* Comment */
#define JMK_EOI         0xFFD9     /* End Of Image */

/**
* 判断文件流是否为JPEG格式图像.
* 逻辑说明:循环从文件流中读取 JPEG 标记,直到遇到SOF0,SOF2标记,就返回true,否则返回false.
*/
bool check_jpg (FILE* stream)
{
    uint16_t jpeg_marker;
    if(!stream)
        return false;
    for (;read_be16(stream,&jpeg_marker) /* 读取一个JPEG标记 */;)
    {
        /* 当前标记的数据长度(不含标记本身) */
        uint16_t payload = 1; /* 设置为0或1用于指定当前JPEG 标记是否有附加数据*/
        switch(jpeg_marker)
        {
        case JMK_SOI:
            payload = 0; /* no payload,没有附加数据 */
            break;
        case JMK_SOF0:
        case JMK_SOF2:
            return true; /* 找到SOF0,SOF2 marker,确认为JPEG 格式*/
        case JMK_DHT:
        case JMK_DQT:
        case JMK_DRI:
        case JMK_SOS:
        case JMK_COM:
            break;
        case JMK_EOI:
            return false; /* not JPEG image*/
        default:
            if((0XFFF8 & jpeg_marker) == JMK_RST_mask){
                payload = 0; /* RST0~7(FFD0~FFD7),no payload */
            }else if((0XFFF0 & jpeg_marker) == JMK_APP_mask){
                /* APP0~APP15,do nothing */
            }else
                return false; /* not JPEG image*/
        }
        if(payload){
            /*读取 JPEG 标记之后的附加数据长度字段,根据这个字段的值移动文件游标位置跳到下一个 JPEG 标记 */
            if(!read_be16(stream,&payload))
                return false; /* not JPEG image*/
            fseek (stream, payload- sizeof(payload), SEEK_CUR);
        }
    }
    return false; /* not JPEG image*/
}

参考资料

《JPEG文件格式 JFIF & Exif》

《JPEG文件格式介绍》

《JPEG wiki》

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年08月31日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 参考资料
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档