小编想学数字图像处理,就是机器视觉类的,本人电气工程师一枚,项目上机器人配合视觉的应用比较多,初来乍到,跟着小编一块学习吧!
实际做项目都用成熟的东西,OPEN CV等,学习时也需要知道一些底层的东西,给一幅图片,首先需要访问图片的信息,open cv中的Mat类已经封装好了对各类图片格式的访问方法,这里自己做了一个访问位图的小程序 ,使用VS2013,本来想使用C++中的文件流读取bmp格式的图片,但调试一直没成功,所以使用C语言的文件访问方式。
下面是的程序只访问24位真彩色图的信息,其他位数的转换起来很麻烦,这里也就不再去研究了,现在用open cv中的Mat类访问很方便。
程序中新建了一个自定义的类,能读入24位真彩色图的信息并格式化进行输出。下面是测试的图片和结果(用像素多的位图显示非常慢)
程序源码:
#include
#include
#include
#include
#include
using namespace std;
typedef struct{ //定义一个像素,存储颜色值
UCHAR ucBlue; //蓝色分量
UCHAR ucGreen; //绿色分量
UCHAR ucRed; //红色分量
}PIXEL; //自定义像素结构体
class BMP //定义位图类
{
public :
int m_rows; //行数(图像高度)
int m_cols; //列数(图像宽度)
int m_nBitCount; //每个颜色占的位数
PIXEL *m_pDate; //数据区域指针
RGBQUAD *m_pRGBQUAD; //颜色表指针
public:
//读入位图,并将信息存放到BMP类对象中
bool imread(char* cBitmapName);
//将BMP类对象中的信息写入位图文件
bool imwrite(char* cBitmapName);
//重载输出运算符,方便显示
friend ostream &operator<<(ostream &os,const BMP &bmp);
};
bool BMP::imread(char* cBitmapName) //读入位图文件
{
FILE* fp = fopen(cBitmapName, "rb"); //打开位图文件
if (NULL == fp)
{
cout << "bitmap file open failed! pleace retry" << endl; //文件打开失败提示
return false; //失败则返回false
}
LPBITMAPFILEHEADER lpBmpFileHeader = new BITMAPFILEHEADER; //定义位图文件头指针,申请内存区域
LPBITMAPINFOHEADER lpBmpInfoHeader = new BITMAPINFOHEADER; //定义位图信息头指针
fread(lpBmpFileHeader, sizeof(BITMAPFILEHEADER), 1, fp); //读出文件头
fread(lpBmpInfoHeader, sizeof(BITMAPINFOHEADER), 1, fp); //读出信息头
m_rows = lpBmpInfoHeader->biHeight; //行数
m_cols = lpBmpInfoHeader->biWidth; //列数
m_nBitCount = lpBmpInfoHeader->biBitCount; //每种颜色所占的位数
if (24 == m_nBitCount) //如果是24位真彩色图
{
fseek(fp, lpBmpFileHeader->bfOffBits, 0); //文件指针指向图片数据的起始位置
//windows位图存储的宽度字节大小必须是4的倍数
int lineByte = (lpBmpInfoHeader->biWidth * lpBmpInfoHeader->biBitCount / 8 + 3) / 4 * 4;
UCHAR* pDateTemp = new UCHAR[lineByte*lpBmpInfoHeader->biHeight]; //申请临时区域存放读出图像数据
fread(pDateTemp, sizeof(UCHAR), lineByte*lpBmpInfoHeader->biHeight, fp);
m_pDate = new PIXEL[lpBmpInfoHeader->biWidth*lpBmpInfoHeader->biHeight]; //申请区域存放所有像素的颜色
for (long row = 0; row < lpBmpInfoHeader->biHeight; ++row) //转化,将读出的像素存入BMP类中
{
for (long col = 0; col < lpBmpInfoHeader->biWidth; ++col)
{ //每3个字节存储一个像素
m_pDate[row*lpBmpInfoHeader->biWidth + col].ucBlue = pDateTemp[row*lineByte + col * 3 + 0];
m_pDate[row*lpBmpInfoHeader->biWidth + col].ucGreen = pDateTemp[row*lineByte + col * 3 + 1];
m_pDate[row*lpBmpInfoHeader->biWidth + col].ucRed = pDateTemp[row*lineByte + col * 3 + 2];
}
}
}
delete lpBmpFileHeader; //释放内存
delete lpBmpInfoHeader;
fclose(fp); //关闭文件
return true;
}
ostream &operator<<(ostream &os, const BMP &bmp) //重载<<操作符
{
if (bmp.m_rows != 0 && bmp.m_cols != 0)
{
os << endl;
os << "从位图左下角开始,从左到右,从下到上列出各像素颜色(R,G,B)";
for (long row = 0; row < bmp.m_rows; ++row)
{
os << endl;
for (long col = 0; col < bmp.m_cols; ++col)
{
os << "(" << (int)bmp.m_pDate[row*bmp.m_cols+col].ucRed; //必须进行强制转化
os << "," << (int)bmp.m_pDate[row*bmp.m_cols + col].ucGreen;
os << "," << (int)bmp.m_pDate[row*bmp.m_cols + col].ucRed << ")" << " ";
}
}
}
return os;
}
int main() //测试程序
{
BMP bmp;
bmp.imread("test.bmp"); //读入位图文件
cout << "图像宽度:" << bmp.m_cols << endl;
cout << "图像高度:" << bmp.m_rows << endl;
cout << bmp << endl; //输出位图像素信息
cin.get(); //等待输入任意字符,防止退出
return 0;
}
这里粘贴一下BMP图像文件的结构,由下面四个部分组成:位图文件头(Bitmap File Header)、位图信息头(Bitmap Information Header)、颜色表(Color Map)和位图数据(即图像数据),实际上需要的是图片每个像素的信息,我们试着读取到图片每个像素的信息(颜色等)。
windows对位图文件头、信息头、RGBQUAD结构体的定义如下(位于wingdi.h头文件下):
typedef struct tagBITMAPFILEHEADER {
WORD bfType; //位图文件的类型,必须为0x424d,即字符串“BM”
DWORD bfSize; //文件大小
WORD bfReserved1; //备用
WORD bfReserved2; //备用
DWORD bfOffBits; //从文件头到实际位图数据的偏移字节数,即上图中前三部分大小之和
} BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;
typedef struct tagBITMAPINFOHEADER{
DWORD biSize; //本结构体的大小,为40字节
LONG biWidth; //位图的宽度,以像素为单位
LONG biHeight; //位图的高度,以像素为单位
WORD biPlanes; //目标设备的级别,必须为1(本人也不知道)
WORD biBitCount; //每个像素所占的位数,值必须为1(黑白图像),4(16色图像),8(256色图),24(真彩色图),32位色的位图
DWORD biCompression; //位图压缩类型,取值为BI_RGB(未经压缩),BI_RLE8,BI_RLE4,BI_BITFILEDS(均为windows定义的常量)
DWORD biSizeImage; //实际的位图数据占用的字节数
LONG biXPelsPerMeter; //指定目标设备的水平分辨率,单位是像素/m
LONG biYPelsPerMeter; //指定目标设备的垂直分辨率,单位是像素/m
DWORD biClrUsed; //位图实际用到的颜色数,如果该值为0,则用到的颜色数为2的biBitCount次幂
DWORD biClrImportant; //位图显示过程中重要的颜色数,如果该值为0,则认为所有的颜色都是重要的
} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;
typedef struct tagRGBQUAD {
BYTE rgbBlue; //该颜色的蓝色分量
BYTE rgbGreen; //该颜色的绿色分量
BYTE rgbRed; //该颜色的红色分量
BYTE rgbReserved; //备用
} RGBQUAD;(占用4字节)
有些位图需要颜色表,有些不需要(如真彩色图)。颜色表的大小和颜色数量由位图信息头中的biBitCount决定。
对于biBitCount=1的二值图像,每像素占1位,图像中只有两种颜色(如黑白),颜色表就只有21=2个表项,整个颜色表大小为2*sizeof(RGBQUAD)=8字节;
对于biBitCount=8的灰度图像,每像素占8位,图像中有28=256种颜色,颜色表就有256个表项,整个颜色表大小为8*sizeof(RGBQUAD)=256字节;
对于biBitCount=24的真彩色图像,由于每个像素3个字节中分别代表了R、G、B三分量的值(每个字节表示一个颜色分量),此时不需要颜色表。