Ⅰ、常见图片格式概述
常见的图片格式:bmp,jpeg,png,gif,tiff
- bmp(BitMap):可以压缩,但绝大多数情况下未经压缩存储原彩图片,是 windows 平台特有的图片格式
- jpeg、png:压缩图片格式,其中 jpeg(Joint Photographic Experts Group) 采用有损压缩,将不易被人眼察觉的图像颜色删除,从而达到较大的压缩比(可达到 2:1 甚至 40:1),因为 jpeg 格式的文件尺寸较小,下载速度快,所以是互联网上最广泛使用的格式;png 无损压缩,适合矢量图和几何图片的存储,支持图像透明,可以利用 Alpha 通道调节图像的透明度。二者相比 jpeg 在图片压缩方面有巨大优势,但采用有损压缩,图片质量有损失。一般截屏用 png 格式不但比 jpeg 质量高且文件还更小;防锯齿方面 png 非常有优势。
- gif(Graphics Interchange Format):不仅可以是一张静止的图片,也可以是动画,并且支持透明背景图像,适用于多种操作系统,“体型”很小,网上很多小动画都是 gif 格式,但是其色域不太广,只支持 256 种颜色
- tiff(Tagged Image File Format):可以支持 RGB、CMYK 等多种色彩模式,adobeRGB、proPhoto RGB 等多种色彩空间,8位、16位、32位等各种色彩深度,甚至也能保留图层和 alpha 通道。更重要的是,大部分的第三方软件、打印机,都能支持TIFF格式,适合专业领域图片存储。
Ⅱ、bmp 格式介绍
Ⅲ、bmp 图片读入 Mat 并显示
3-1. 图片准备
搜索并下载一张滑稽,用 windows 自带的画图软件打开,然后另存为某种 bmp 格式(如下图),这里代码实现了 24位 bmp 和 16色 bmp 格式图片的读取。
3-2. 头文件 BmpFormat.h 对 bmp 位图四个结构的定义:
#ifndef BMPFORMAT_H
#define BMPFORMAT_H
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned int DWORD;
typedef long LONG;
/* 1. 位图文件头 */
typedef struct tagBITMAPFILEHEADER
{
//WORD bfType; // windows中为 BM (0x4d42)
DWORD bfSize; // 文件大小
WORD bfReserved1; // 保留,设置为 0
WORD bfReserved2; // 保留,设置为 0
DWORD bfOffBits; // 实际图像数据的偏移量
}BITMAPFILEHEADER;
/* 2. 位图信息头 */
typedef struct tagBITMAPINFOHEADER
{
DWORD biSize; // 该结构所占字节数,windows下为 40
LONG biWidth; // 图像宽度
LONG biHeight; // 图像高度
WORD biPlanes; // 位面数,1
WORD biBitCount; // 单像素位数
DWORD biCompression; // 压缩说明
DWORD biSizeImage; // 图像大小,如无压缩,可设置为 0
LONG biXPelsPerMerer;// 水平分辨率
LONG biYPelsPerMerer;// 垂直分辨率
DWORD biClrUsed; // 位图使用的颜色数, 0 表示使用了全部颜色
DWORD biClrImportant; // 重要颜色数目
}BITMAPINFOHEADER;
/* 3. 调色板 */
typedef struct tagRGBQUAD
{
BYTE rgbBlue; // 指定蓝色强度
BYTE rgbGreen; // 指定绿色强度
BYTE rgbRed; // 指定红色强度
BYTE rgbReserved; // 保留,设为 0
}RGBQUAD;
/* 4. 位图数据
* 扫描行由低向上存储,第一字节为左下角像素,最后一个字节为右上角像素
*/
#endif
这里注意将第一部分图片文件头结构体中 bfType 单独拎出来,是处于结构体需要对齐的考虑,并且注意读取时是按字节读取,且 pc 采用小端头存储,所以 bfType 读取出来后是 MB,而在一个字节内部,读取出来的顺序就是存储时的顺序,即高位对应前面的数据,低位对应后面的数据,这里在读取 16色 bmp 图片时有体现。
3-3. 读入 opencv 的 Mat 中并显示
#include <iostream>
#include <fstream>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/core/core.hpp>
#include "BmpFormat.h"
using namespace std;
using namespace cv;
/* 输出 bmp 图片头部信息 */
void bmpFileInfo(ifstream &fpbmp, int &Offset, int &rows, int &cols);
/* 将 bmp24bit 图片读入 Mat 中 */
void bmp24bitToMat(ifstream& fpbmp, Mat& bmp, int Offset);
/* 将 bmp16色 图片读入 Mat 中 */
void bmp16ToMat(ifstream& fpbmp, Mat& bmp, int Offset);
int main(int argc, char* argv[]) {
/* 打开 bmp 文件*/
ifstream fpbmp(".\\pic\\ppx16.bmp", ifstream::in | ifstream::binary);
fpbmp.seekg(0, fpbmp.end);
int length = fpbmp.tellg();
fpbmp.seekg(0, fpbmp.beg);
int Offset, rows, cols;
bmpFileInfo(fpbmp, Offset, rows, cols);
Mat bmp(rows, cols, CV_8UC3);
/* 24 位 bmp 图片 */
//bmp24bitToMat(fpbmp, bmp, Offset);
/* 16 色 bmp 图片 */
bmp16ToMat(fpbmp, bmp, Offset);
namedWindow("ppx", WINDOW_AUTOSIZE);
imshow("ppx", bmp);
waitKey(0);
destroyWindow("ppx");
return 0;
}
/* 输出 bmp 图片头部信息 */
void bmpFileInfo(ifstream &fpbmp, int& Offset, int& rows, int& cols) {
BITMAPFILEHEADER fh;
WORD bfType;
fpbmp.read((char *)&bfType, sizeof(WORD));
fpbmp.read((char *)&fh, sizeof(BITMAPFILEHEADER));
if (bfType != 'MB') {
cout << "It's not a bmp file." << endl;
exit(1);
}
cout << "****** tagBITMAPFILEHEADER info *******" << endl;
cout << " bfType :" << " BM" << endl;
cout << " bfSize : " << (fh.bfSize) << endl;
cout << " bfReserved1 : " << (fh.bfReserved1) << endl;
cout << " bfReserved2 : " << (fh.bfReserved2) << endl;
cout << " bfOffBits : " << (fh.bfOffBits) << endl;
cout << " 总字节偏移 :" << fpbmp.tellg() << endl << endl;
BITMAPINFOHEADER fih;
fpbmp.read((char*)&fih, sizeof(BITMAPINFOHEADER));
cout << "****** tagBITMAPINFOHEADER info *******" << endl;
cout << " 位图信息头字节数 : " << (fih.biSize) << endl;
cout << " 图像像素宽度 : " << (fih.biWidth) << endl;
cout << " 图像像素高度 : " << (fih.biHeight) << endl;
cout << " 位面数 : " << (fih.biPlanes) << endl;
cout << " 单像素位数 : " << (fih.biBitCount) << endl;
cout << " 压缩说明 : " << (fih.biCompression) << endl;
cout << " 图像大小 : " << (fih.biSizeImage) << endl;
cout << " 水平分辨率 : " << (fih.biXPelsPerMerer) << endl;
cout << " 垂直分辨率 : " << (fih.biYPelsPerMerer) << endl;
cout << " 位图使用的颜色数 : " << (fih.biClrUsed) << endl;
cout << " 重要颜色数目 : " << (fih.biClrImportant) << endl;
cout << " 总字节偏移 :" << fpbmp.tellg() << endl << endl;
Offset = fh.bfOffBits;
rows = fih.biHeight;
cols = fih.biWidth;
if (Offset != 54) {
RGBQUAD rgbPalette[16];
fpbmp.read((char*)&rgbPalette, 16 * sizeof(RGBQUAD));
cout << "****** tagRGBQUAD info *******" << endl;
cout << " (R, G, B, Alpha)" << endl;
for (int i = 0; i < 16; i++) {
cout << " No." << i << " " << "(" \
<< (rgbPalette[i].rgbRed + 0) << ", " \
<< (rgbPalette[i].rgbGreen + 0) << ", " \
<< (rgbPalette[i].rgbBlue + 0) << ", " \
<< (rgbPalette[i].rgbReserved + 0) << ")" << endl;
//if (i != 0 && i % 4 == 0) cout << endl;
}
cout << " 总字节偏移 :" << fpbmp.tellg() << endl;
}
}
/* 将 bmp24bit 图片读入 Mat 中 */
void bmp24bitToMat(ifstream& fpbmp, Mat& bmp, int Offset) {
fpbmp.seekg(Offset, fpbmp.beg);
for (int i = (bmp.rows-1); i >=0 ; i--) {
for (int j = 0; j < bmp.cols; j++) {
fpbmp.read((char*)&bmp.at<Vec3b>(i, j)[0], 1);
fpbmp.read((char*)&bmp.at<Vec3b>(i, j)[1], 1);
fpbmp.read((char*)&bmp.at<Vec3b>(i, j)[2], 1);
}
}
}
/* 将 bmp16色 图片读入 Mat 中 */
void bmp16ToMat(ifstream& fpbmp, Mat& bmp, int Offset) {
RGBQUAD rgbPalette[16];
fpbmp.seekg(54, fpbmp.beg);
fpbmp.read((char*)&rgbPalette, 16 * sizeof(RGBQUAD));
fpbmp.seekg(Offset, fpbmp.beg);
char tmp;
for (int i = (bmp.rows - 1); i >= 0; i--) {
for (int j = 0; j < bmp.cols; j+=2) {
fpbmp.read((char*)&tmp, 1);
bmp.at<Vec3b>(i, j)[0] = rgbPalette[tmp >> 4 & 0x0f].rgbBlue;
bmp.at<Vec3b>(i, j)[1] = rgbPalette[tmp >> 4 & 0x0f].rgbGreen;
bmp.at<Vec3b>(i, j)[2] = rgbPalette[tmp >> 4 & 0x0f].rgbRed;
bmp.at<Vec3b>(i, j+1)[0] = rgbPalette[tmp & 0x0f].rgbBlue;
bmp.at<Vec3b>(i, j+1)[1] = rgbPalette[tmp & 0x0f].rgbGreen;
bmp.at<Vec3b>(i, j+1)[2] = rgbPalette[tmp & 0x0f].rgbRed;
}
}
}
注意,opencv 和 bmp 图片的通道顺序都是反序,即 BGR。
3-4. 结果分析
(1). 24 位 bmp 结果:
因为是真彩色图片,这里文件头偏移固定为 54 字节,因为没有调色板,两个保留字为 0,总大小的计算为:
,位面数固定为 1,压缩说明为 0,指未压缩,图像大小在无压缩情况下也为 0,位图使用的颜色数为 0,指所有颜色都有用到,共
种,重要颜色数为 0,表示都重要。
(2). 16 色 bmp 结果:
Alpha 是保留字节,设置为 0,总头部偏移:
,图像大小:
,这里因为只有 16 色,故一个字节刚好可以存储两个索引,如果是 256 色,则一个字节只能存储一个索引。
关于使用调色板的图片压缩,可以参考bmp图像压缩算法详细解析4。
参考文章
转载:https://blog.csdn.net/m0_46318517/article/details/104425450
查看评论