常见图片宽高解析

article/2025/9/21 16:51:21

于比较常用的图片格式Png、Jpg、Gif、Bmp,我们需要针对不同的图片格式使用不同的控件来显示,这里就有一个来解析图片格式的问题。我们不能单纯的用文件后缀名.png、.jpg、.jpeg、.gif、.bmp来区分图片格式,因为实际上我们可以直接修改图片后缀名,修改后缀名并不能修改图片的格式,图片还是保持它原来的格式。
  图片文件的格式结果中,在头部信息(一般都会在图片文件最开始的几个字节)中都会包含图片的格式信息。下面就列车常用的这几种格式图片的头部信息标识(十六进制)。
  1.Png图片文件包括8字节:89 50 4E 47 0D 0A 1A 0A。即为 .PNG....。 
  2.Jpg图片文件包括2字节:FF D8。
  3.Gif图片文件包括6字节:47 49 46 38 39|37 61 。即为 GIF89(7)a。
  4.Bmp图片文件包括2字节:42 4D。即为 BM。

      根据图片问题头标识信息我们可以能很方便的判断出文件的格式,首先我们需要获取图片文件的字节流信息,代码如下。

复制代码
        //获取图片文件流,根据图片是资源文件或者独立存储文件分别处理
Stream stream = null;
//如果是资源文件处理
StreamResourceInfo info = Application.GetResourceStream(new Uri(path, UriKind.Relative));
if (info != null)
{
stream = info.Stream;
}
//如果是独立存储文件处理
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
//打开文件
stream = myIsolatedStorage.OpenFile(path, FileMode.Open, FileAccess.Read);
}
复制代码

  从图片文件流stream中读取8个字节,然后再根据不同的图片格式做文件头匹配比较具能判断出文件的格式,代码如下。

复制代码
        /// <summary>
/// 定义图片格式
/// </summary>
public enum ImageType
{
Null,
Png,
Jpg,
Gif,
Bmp
}

/// <summary>
/// 获取图片格式
/// </summary>
private ImageType getImageType(Stream stream)
{
//图片格式
ImageType type = ImageType.Null;

//读取图片文件头8个字节,并根据若干个字节来确定图片格式
byte[] header = new byte[8];
stream.Read(header, 0, 8);

//确定图片格式
if (header[0] == 0x89 &&
header[1] == 0x50 && // P
header[2] == 0x4E && // N
header[3] == 0x47 && // G
header[4] == 0x0D &&
header[5] == 0x0A &&
header[6] == 0x1A &&
header[7] == 0x0A)
{
//Png图片 8字节:89 50 4E 47 0D 0A 1A 0A
type = ImageType.Png;
}
else if (header[0] == 0xFF &&
header[1] == 0xD8)
{
//Jpg图片 2字节:FF D8
type = ImageType.Jpg;
}
else if (header[0] == 0x47 && // G
header[1] == 0x49 && // I
header[2] == 0x46 && // F
header[3] == 0x38 && // 8
(header[4] == 0x39 || // 9
header[4] == 0x37) && // 7
header[5] == 0x61) // a
{
//Gif图片 6字节:47 49 46 38 39|37 61
type = ImageType.Gif;
}
else if (header[0] == 0x42 && //B
header[1] == 0x4D) //M
{
//Bmp图片 2字节:42 4D
type = ImageType.Bmp;
}

//关闭字节流
stream.Close();

return type;
}
复制代码

  解析到图片格式后,我们就可以根据图片格式选择对应的控件来显示图片了。


PNG:

要解析Png图片的宽度和高度信息,首先需要了解Png图片的数据块结构,Png图片的尺寸信息存放在文件头数据块中,所以我们需要了解文件头的数据块结构。
  文件头数据块IHDR(header chunk):它包含有PNG文件中存储的图像数据的基本信息,并要作为第一个数据块出现在PNG数据流中,而且一个PNG数据流中只能有一个文件头数据块。 文件头数据块由13字节组成,其中前8个字节即为图片的宽度和高度信息,各占4个字节。
  文件头数据块是第一个数据块,但是在数据块前,也就是PNG图片文件的最开始位置首先存储的是PNG文件署名域,占8个字节,即:89 50 4e 47 0d 0a 1a 0a ( .PNG....),通过这个可以判断图片是否为PNG格式,接下来是文件头数据块的长度和标识,各占4个字节,文件头数据块的长度为13,所以文件头数据块的长度固定为 00 00 00 0D,而文件头数据块的标识为49 48 44 52,即“IHDR”。
  通过上面的解析,我们可以分一下几步来解析PNG图片的宽度和高度。
  1.首先读取起始位的8个字节,即PNG文件署名域,判断图片是否为PNG格式,如果不是,则退出。

复制代码
        //读取图片文件头8个字节,并根据这8个字节来判断是否为PNG图片
byte[] header = new byte[8];
stream.Read(header, 0, 8);
//Png图片 8字节:89 50 4E 47 0D 0A 1A 0A
if (!(header[0] == 0x89 &&
header[1] == 0x50 && // P
header[2] == 0x4E && // N
header[3] == 0x47 && // G
header[4] == 0x0D &&
header[5] == 0x0A &&
header[6] == 0x1A &&
header[7] == 0x0A))
{
//不是PNG图片
return;
}
复制代码

  2.然后跳过8个字节,即文件头数据块的长度值 00 00 00 0D,以及文件头数据块标识 49 48 44 52(IHDR)。

        //数据域长度  4    指定数据域的长度,固定为00 00 00 0D
//数据块符号  4   49 48 44 52,是“IHDR”的 Ascii 码
stream.Seek(8, SeekOrigin.Current);

  3.接下来要读取就是图片的宽度和高度值,读取8个字节,由于是按照高低位调换存储,所以需要做高低位转换,转换后通过BitConverter类直接将字节类型转换为整数型即为图片的尺寸。

复制代码
        //读取宽度,高度 各4字节
byte[] buffer = new byte[8];
stream.Read(buffer, 0, buffer.Length);

Array.Reverse(buffer, 0, 4);
Array.Reverse(buffer, 4, 4);

width_ = BitConverter.ToInt32(buffer, 0);
height_ = BitConverter.ToInt32(buffer, 4);
复制代码

 

GIF:

GIF图片的文件格式相对比较简单,其中宽度和高度信息存放在逻辑视屏描述块的前4个字节,而逻辑视屏描述块是GIF图片的第二块区域,第一个区域为6个字节的头部,头部包括标识符和版本。下表列出到高度信息为止的各个字节的描述。

名称 字节 说明
头部    
标识符 3 GIF  47 49 46
版本 3 87a(89a) 38 39|37 61
逻辑视屏描述块    
宽度 2  
高度 2  

  根据上面的格式很容易获取图片的高度和宽度信息,具体代码如下。

复制代码
        //gif图片信息域(47 49 46 38 39|37 61) GIF89(7)a,共6字节
//根据6字节判断是否为gif图片
byte[] header = new byte[6];
stream.Read(header, 0, 6);
if (!(header[0] == 0x47 && // G
header[1] == 0x49 && // I
header[2] == 0x46 && // F
header[3] == 0x38 && // 8
(header[4] == 0x39 || // 9
header[4] == 0x37) && // 7
header[5] == 0x61)) // a
{
//不是GIF图片,退出
return;
}

//读取宽度,高度 各2字节
byte[] buffer = new byte[4];
stream.Read(buffer, 0, buffer.Length);

width_ = BitConverter.ToInt16(buffer, 0);
height_ = BitConverter.ToInt16(buffer, 2);
复制代码

 

BMP:

典型的位图文件格式通常包含下面几个数据块:
  位图头:保存位图文件的总体信息。
  位图信息:保存位图图像的详细信息。
  调色板:保存所用颜色的定义。
  位图数据:保存一个又一个像素的实际图像。

  通过解析位图头我们可以判断图片是否为bmp格式,而我们所需要的图片尺寸信息存储在位图信息数据块里。所以我们需要详细了解位图头和位图信息两个数据块的存储结构。
  位图头,这部分是识别信息,典型的应用程序会首先普通读取这部分数据以确保的确是位图文件并且没有损坏。
    字节 #0-1 保存位图文件的标识符,这两个字节的典型数据是BM
    字节 #2-5 使用一个dword保存位图文件大小。
    字节 #6-9 是保留部分,留做以后的扩展使用,对实际的解码格式没有影响。
    字节 #10-13 保存位图数据位置的地址偏移,也就是起始地址。

  位图信息,这部分告诉应用程序图像的详细信息,在屏幕上显示图像将会使用这些信息,它从文件的第15个字节开始。
    字节 #14-17 定义以下用来描述影像的区块(BitmapInfoHeader)的大小。它的值是:40 - Windows 3.2、95、NT、12 - OS/2 1.x、240 - OS/2 2.x
    字节 #18-21 保存位图宽度(以像素个数表示)。
    字节 #22-25 保存位图高度(以像素个数表示)。
    …………
  以上关于bmp文件结构的内容参考维基百科http://zh.wikipedia.org/wiki/BMP。
  通过上面对BMP图片格式的了解,可以用下面的代码来解析bmp图片的尺寸大小。

复制代码
        //Bmp 图片前2字节:0x42 4D
byte[] header = new byte[2];
stream.Read(header, 0, 2);
if ( !(header[0] == 0x42 && header[1] == 0x4D))
{
//不是BMP图片
return;
}

//跳过16个字节
stream.Seek(16, SeekOrigin.Current);

//bmp图片的宽度信息保存在第 18-21位 4字节
//bmp图片的高度度信息保存在第 22-25位 4字节
//读取宽度,高度 各4字节
byte[] buffer = new byte[8];
stream.Read(buffer, 0, buffer.Length);

width_ = BitConverter.ToInt32(buffer, 0);
height_ = BitConverter.ToInt32(buffer, 4);
复制代码

 

JPG:

由于jpg图片的格式相对于png要复杂很多,所以首先我们要先清楚的了解jpg图片的数据格式,jpg图片包括SOI和数据两个部分。
  SOI,Start of Image,图像开始,标记代码 2字节 固定值0xFFD8。
  数据部分分成很多数据段,数据段的一般结构如下。

段数据结构
名称 字节 说明
段标识 >= 1 多于一个的0xFF
段类型 1 类型编码(称作“标记码”)
段长度 2 包括段内容和段长度本身,不包括段标识和段类型
短内容 <= 65533  

  段类型有30种,但只有10种是必须被所有程序识别的,其它的类型都可以忽略。在这么多的段中,其中JPG图片的尺寸相关信息存储在SOF0(图像基本信息)段中。所以需要详细了解一下SOFO段的数据结构。

SOFO段结构
名称 字节 说明
段标识 1 0XFF
段类型 1 0XCO JFIF格式的为0XC2
段长度 2 其值=8+组件数量×3
样本精度 1 8 每个样本位数(大多数软件不支持12和16)
图片高的 2 采用Motorola格式,即:高位在前,低位在后
图片宽度 2 采用Motorola格式,即:高位在前,低位在后

  由于我们是为了解析JPG图片的宽度和高度信息,所以上表SOFO段结构只列出了到宽度为止结构信息,接下来还有其他一些图片的相关信息,这里就不再列出。
  根据上面对JPG图片格式的解析,我们可以分一下几步来解析JPG图片的尺寸信息。
  1.读取2个字节的SOI,即0xFFD8,根据这两个字节判断图片是否为JPG图片,如果不是,则退出解析过程。

复制代码
        //读取2个字节的SOI,即0xFFD8
byte[] header = new byte[2];
stream.Read(header, 0, 2);
//判断是否为JPG,不是退出解析
if (!(header[0] == 0xFF &&
header[1] == 0xD8))
{
//不是JPG图片
return;
}
复制代码

  2.接下来就需要解析图片的数据部分,由于数据部分是有很多不同的数据段构成,数据段拥有一些共同的特性,所以这里我们需要做一个循环来逐个遍历查找到SOFO数据段。

复制代码
        //段类型
int type = -1;
int ff = -1;
//记录当前读取的位置
long ps = 0;
//逐个遍历所以段,查找SOFO段
do
{
do
{
//每个新段的开始标识为oxff,查找下一个新段
ff = stream.ReadByte();
if (ff < 0) //文件结束
{
return;
}
} while (ff != 0xff);

do
{
//段与段之间有一个或多个oxff间隔,跳过这些oxff之后的字节为段标识
type = stream.ReadByte();
} while (type == 0xff);

//记录当前位置
ps = stream.Position;
switch (type)
{
case 0x00:
case 0x01:
case 0xD0:
case 0xD1:
case 0xD2:
case 0xD3:
case 0xD4:
case 0xD5:
case 0xD6:
case 0xD7:
break;
case 0xc0: //SOF0段(图像基本信息)
case 0xc2: //JFIF格式的 SOF0段
{
//找到SOFO段,解析宽度和高度信息
getJpgSize(stream);
return;
}
default: //别的段都跳过
//获取段长度,直接跳过
ps = stream.ReadByte() * 256;
ps = stream.Position + ps + stream.ReadByte() - 2;
break;
}
if (ps + 1 >= stream.Length) //文件结束
{
return;
}
stream.Position = ps; //移动指针
} while (type != 0xda); // 扫描行开始
复制代码

  3.找到SOFO数据段后,就可以解析图片的宽度和高度信息。

复制代码
        /// <summary>
/// 解析JPG图片的尺寸
/// </summary>
/// <param name="stream"></param>
private void getJpgSize(Stream stream)
{
//跳过2个自己长度信息和1个字节的精度信息
stream.Seek(3, SeekOrigin.Current);

//高度 占2字节 低位高位互换
height_ = stream.ReadByte() * 256;
height_ += stream.ReadByte();
//宽度 占2字节 低位高位互换
width_ = stream.ReadByte() * 256;
width_ += stream.ReadByte();
}
复制代码

 

出处::

 


作者:宇之乐 
出处:http://www.cnblogs.com/huizhang212/ 
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

 



http://chatgpt.dhexx.cn/article/vSl8VRpL.shtml

相关文章

图片类型解析

文章目录 PNGPNG 特点PNG 类型PNG 数据结构PNG 适用场景PNG 压缩过程 JPG/JPEGWebP PNG PNG 特点 无损压缩&#xff1a;PNG图片采取了基于LZ77派生算法对文件进行压缩&#xff0c;使得它压缩比率更高&#xff0c;生成的文件体积更小&#xff0c;并且不损失数据。体积小&#…

激活函数(5)ELU函数、PReLU函数

ELU函数 ELU函数公式和曲线如下图 ELU函数是针对ReLU函数的一个改进型&#xff0c;相比于ReLU函数&#xff0c;在输入为负数的情况下&#xff0c;是有一定的输出的&#xff0c;而且这部分输出还具有一定的抗干扰能力。这样可以消除ReLU死掉的问题&#xff0c;不过还是有梯度饱…

激活函数(relu,prelu,elu,+BN)对比on cifar10

最近做了个对比实验&#xff0c;通过修改激活函数观察对图片分类准确率的影响&#xff0c;现记录如下&#xff1a; 一&#xff0e;理论基础 1.1激活函数 1.2 elu论文&#xff08;FAST AND ACCURATE DEEP NETWORK LEARNING BY EXPONENTIAL LINEAR UNITS (ELUS)&#xff09;…

22个激活函数,ReLU、RReLU、LeakyReLU、PReLU、Sofplus、ELU、CELU、SELU、GELU、ReLU6、Sigmoid、Tanh、Softsign、Hardtanh等

转自&#xff1a;https://www.pianshen.com/article/33331174884/ 1.22.Linear常用激活函数 1.22.1.ReLU torch.nn.ReLU() 1.22.2.RReLU torch.nn.RReLU() 1.22.3.LeakyReLU torch.nn.LeakyReLU() 1.22.4.PReLU torch.nn.PReLU() 1.22.5.Sofplus torch.nn.Softplus() 1.22.6.E…

常用激活函数(Sigmiod、Tanh、Softmax、ReLU、elu、LReLU、Softplus)函数表达式、特点、图像绘制(代码)---已解决

楼主最近在研究激活函数&#xff0c;索性将常用的激活函数进行了简单的整理&#xff0c;方便以后翻看&#xff0c;也希望能帮到你。 1、sigmoid函数 函数表达式&#xff1a;f(x) 1/(1e^-x) 函数特点&#xff1a; 优点&#xff1a;1.输出[0,1]之间&#xff1b;2.连续函数&#…

Sigmoid,tanh,Relu,Leaky ReLu,ELU,GeLu 激活函数理解

目录 1 神经网络为什么需要非线性激活函数&#xff1f;2 Sigmoid2.1缺陷2.1.1 梯度消失2.2.2 Output非zero-centered 3 Tanh3.1 缺陷 4 ReLu4.1 缺陷 5 Leaky ReLu6 ELU7 GeLu7.1 基础知识回顾7.1.1 正态分布7.1.2 概率密度函数7.1.3 累积分布函数7.1.4 Φ(x)与erf(x)函数关系公…

Elu函数~小朋友

Elu激励函数的数学表达式如下&#xff1a; 图一&#xff1a;摘自 https://blog.csdn.net/zrh_CSDN/article/details/81266188 代码如下&#xff1a; #code:utf-8import numpy as np import matplotlib.pyplot as pltdef elu(x,a):y x.copy()for i in range(y.shape[0]):if y[…

【Python--torch(激活函数说明+代码讲解)】激活函数(sigmoid/softmax/ELU/ReLU/LeakyReLU/Tanh)

【Python–torch】激活函数(sigmoid/softmax/ELU/ReLU/LeakyReLU/Tanh) 文章目录 【Python--torch】激活函数(sigmoid/softmax/ELU/ReLU/LeakyReLU/Tanh)1. 介绍2. 常用激活函数说明2.1 Sigmoid2.1.1 公式2.1.2 图像2.1.3 代码解读 2.2 Softmax2.2.1 公式2.2.2 代码解读 2.3 EL…

A.深度学习基础入门篇[四]:激活函数介绍:tanh、sigmoid、ReLU、PReLU、ELU、softplus、softmax、swish等

【深度学习入门到进阶】必看系列&#xff0c;含激活函数、优化策略、损失函数、模型调优、归一化算法、卷积模型、序列模型、预训练模型、对抗神经网络等 专栏详细介绍&#xff1a;【深度学习入门到进阶】必看系列&#xff0c;含激活函数、优化策略、损失函数、模型调优、归一化…

el-descriptions

<el-descriptions title"用户信息"><el-descriptions-item label"用户名">kooriookami</el-descriptions-item><el-descriptions-item label"手机号">18100000000</el-descriptions-item><el-descriptions-ite…

功能性模块:(8)一文理解常用激活函数(Sigmoid,ReLU,ELU,GELU...)

一文理解常用激活函数 1. Sigmoid 公式&#xff1a; Sigmoid ( x ) 1 1 exp ⁡ ( − x ) \text{Sigmoid}(x) \frac{1}{1 \exp(-x)} Sigmoid(x)1exp(−x)1​ Sigmoid的函数形状如下图所示: 对应的导数形式如下图所示&#xff1a; Sigmoid函数的优点&#xff1a; 便于求…

深入理解ReLU、Leaky ReLU、 PReLU、ELU、Softplus

文章目录 ReLULeaky ReLUPReLUELUSoftplus ReLU ReLU&#xff08;Rectified Linear Unit&#xff0c;修正线性单元&#xff09;&#xff0c;也叫Rectifier 函数&#xff0c;它的定义如下&#xff1a; Relu可以实现单侧抑制&#xff08;即把一部分神经元置0&#xff09;&#x…

搞懂激活函数(Sigmoid/ReLU/LeakyReLU/PReLU/ELU)

1. 简介 在深度学习中&#xff0c;输入值和矩阵的运算是线性的&#xff0c;而多个线性函数的组合仍然是线性函数&#xff0c;对于多个隐藏层的神经网络&#xff0c;如果每一层都是线性函数&#xff0c;那么这些层在做的就只是进行线性计算&#xff0c;最终效果和一个隐藏层相当…

YOLOv5改进系列(13)——更换激活函数之SiLU,ReLU,ELU,Hardswish,Mish,Softplus,AconC系列等

【YOLOv5改进系列】前期回顾: YOLOv5改进系列(0)——重要性能指标与训练结果评价及分析 YOLOv5改进系列(1)——添加SE注意力机制

【PyTorch】教程:torch.nn.ELU

torch.nn.ELU CLASS torch.nn.ELU(alpha1.0, inplaceFalse) paper: Fast and Accurate Deep Network Learning by Exponential Linear Units (ELUs). 参数 alpha ([float]) – α \alpha α 默认为1.0inplace ([bool] ) – 内部做, 默认为 False ELU 定义 ELU ( x ) { x , …

【YOLOv7/YOLOv5系列算法改进NO.54】改进激活函数为ReLU、RReLU、Hardtanh、ReLU6、Sigmoid、Tanh、Mish、Hardswish、ELU、CELU等

文章目录 前言一、解决问题二、基本原理三、​添加方法四、总结 前言 作为当前先进的深度学习目标检测算法YOLOv7&#xff0c;已经集合了大量的trick&#xff0c;但是还是有提高和改进的空间&#xff0c;针对具体应用场景下的检测难点&#xff0c;可以不同的改进方法。此后的系…

常用激活函数:Sigmoid、Tanh、Relu、Leaky Relu、ELU优缺点总结

1、激活函数的作用 什么是激活函数&#xff1f; 在神经网络中&#xff0c;输入经过权值加权计算并求和之后&#xff0c;需要经过一个函数的作用&#xff0c;这个函数就是激活函数&#xff08;Activation Function&#xff09;。 激活函数的作用&#xff1f; 首先我们需要知道…

深度学习—激活函数详解(Sigmoid、tanh、ReLU、ReLU6及变体P-R-Leaky、ELU、SELU、Swish、Mish、Maxout、hard-sigmoid、hard-swish)

非线性激活函数详解 饱和激活函数Sigmoid函数tanh函数hard-Sigmoid函数 非饱和激活函数Relu&#xff08;修正线性单元&#xff09;&#xff1a;Relu6&#xff08;抑制其最大值&#xff09;&#xff1a;ELU&#xff08;指数线性单元&#xff09;SELULeaky-Relu / R-ReluP-Relu&a…

激活函数详解(ReLU/Leaky ReLU/ELU/SELU/Swish/Maxout/Sigmoid/tanh)

神经网络中使用激活函数来加入非线性因素&#xff0c;提高模型的表达能力。 ReLU(Rectified Linear Unit,修正线性单元) 形式如下: ReLU公式近似推导:: 下面解释上述公式中的softplus,Noisy ReLU. softplus函数与ReLU函数接近,但比较平滑, 同ReLU一样是单边抑制,有宽广的接受…

【卷积神经网络】12、激活函数 | Tanh / Sigmoid / ReLU / Leaky ReLU / ELU / SiLU / Mish

文章目录 一、Tanh二、Sigmoid三、ReLU四、Leaky ReLU五、ELU六、SiLU七、Mish 本文主要介绍卷积神经网络中常用的激活函数及其各自的优缺点 最简单的激活函数被称为线性激活&#xff0c;其中没有应用任何转换。 一个仅由线性激活函数组成的网络很容易训练&#xff0c;但不能学…