RGB数据的处理
基本概念
分辨率为640*480的图像,其像素点的个数为 widthxheight,即为640x480 = 307200
- 二值图像
每个像素通过一位来存储即为二值图,取值只有0和1。 - 灰度图像
在二值图像中加入许多介于黑色与白色之间的颜色深度,就构成了灰度图像,就典型的就是256色图,像素取值可以是0到255之间的整数值,那么每个像素占一个字节即8位,灰度图反映的是该图像的亮度信息,灰度级为0~255。占用内存的大小为widthxheight。 - RGB24图像
每个像素占三个字节,对应于Red,Green,Blue三原色值,每个原色的取值是0到 255间的整数。这样的图也称为rgb24,24位真彩色。占用内存的大小为widthxheightx3
数字图像的表示
为了表述像素之间的相对和绝对位置,可以把图像看一个原点在左上角的二维坐标系。如下图
那么一副物理图像就被转化成了数字矩阵,成为了计算机能够处理的对象。
为M行N列的矩阵,对应的就是Height行width列的矩阵。
rgb图像在程序中的表示
- 二维矩阵在C++中映射的就是二维数组。对分辨率为w*h的图像,可以在程序中定义
unsigned char imag[Height][width]
来表示。这里的imag是表示指向元素个数为width类型为unsigned char的数组的指针,unsigned char (*img)[width]。
- 动态分配存放图像数据的二维数组的代码示例如下:
//指针的指针
unsigned char** pRGBData = NULL;
//元素个数为Height的unsigned char*数组
pRGBData = new unsigned char*[Height];
for (int i=0; i<Height; ++i)
{
pRGBData[i] = new unsigned char[width];
}
内存结构示意:
rgb内存存储顺序
不同于字面的顺序,rgb数据实际的存储顺序为 BGR,BGR
bmp格式
windows有一种bmp的图像文件格式,它的图像数据就rgb数据,将rgb封装成bmp格式后可以很方便的进行预览。
bmp格式介绍
处理bmp数据涉及到如下两个问题:
1. 在图像处理中的坐标系的原点是在左上角,而bmp数据坐标的原点在左下角。这里涉及到坐标的转换。
2. bmp的数据是需要4字节对齐的,如果不为4字节的整数倍,则是会填充数据的,所以计算图像数据大小时就不能简单的通过width*height*像素字节数来计算,可以通过如下宏计算
// 在计算图像大小时,采用公式:biSizeImage = biWidth' × biHeight。
// 是biWidth',而不是biWidth,这里的biWidth'必须是4的整倍数,表示
// 大于或等于biWidth的,离4最近的整倍数。WIDTHBYTES就是用来计算
// biWidth'
#define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4)
//这里计算的是rgb24的行说占的字节数
int nWidthBytes = WIDTHBYTES(iWidth*24);
示例程序
下面实现了一个rgb24处理类,包含如下功能:
1. 加载rgb24数据文件,这里的rgb数据文件并非指bmp文件,而指只有rgb数据的文件。
2. 获取/设置指定像素的值。
3. 获取指定像素的灰度值。
4. 将rgb24数据文件存成bmp格式文件。
5. 在rgb24图像上画线。
#include <stdio.h>
#include <string>
#include <Windows.h>
//#define RGB(r,g,b) ((unsigned long)(((unsigned char)(r)|((unsigned short)((unsigned char)(g))<<8))|(((unsigned long)(unsigned char)(b))<<16)))
#define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4)
class CProcessRGB
{
public:
CProcessRGB() :m_pRGBFile(NULL), m_iHeight(0),m_iWidth(0), m_pRGBData(NULL),m_pBmpFile(NULL)
{
}
~CProcessRGB()
{
if (NULL != m_pRGBFile)
{
fclose(m_pRGBFile);
m_pRGBFile = NULL;
}
if (NULL != m_pRGBData)
{
for (int i = 0; i < m_iHeight; ++i)
{
if (NULL != m_pRGBData[i])
{
delete[] m_pRGBData[i];
m_pRGBData[i] = NULL;
}
}
}
m_pRGBData = NULL;
}
int Init(int iWidth,int iHeight,const std::string& strRGBFile)
{
m_iWidth = iWidth;
m_iHeight = iHeight;
m_pRGBFile = fopen(strRGBFile.c_str(), "rb");
m_pRGBData = new unsigned char*[iHeight];
// 读取图像数据,WIDTHBYTES宏用于生成每行字节数
int nWidthBytes = WIDTHBYTES(iWidth*24);
for (int i = 0; i < iHeight; ++i)
{
m_pRGBData[i] = new unsigned char[nWidthBytes];
fread(m_pRGBData[i], nWidthBytes, 1, m_pRGBFile);
}
return 0;
}
//获取指定像素点的颜色值
unsigned long GetPixel(int x, int y)
{
unsigned long color = RGB(m_pRGBData[m_iHeight - y - 1][x * 3 + 2],
m_pRGBData[m_iHeight - y - 1][x * 3 + 1],
m_pRGBData[m_iHeight - y - 1][x * 3]);
return color;
}
//设置指定像素的颜色值
void SetPixel(int x, int y, unsigned long color,unsigned char** pData)
{
pData[m_iHeight - y - 1][x * 3] = color;
pData[m_iHeight - y - 1][x * 3 + 1] = color >> 8;
pData[m_iHeight - y - 1][x * 3 + 2] = color >> 16;
}
//计算指定像素点的灰度值
unsigned char GetGray(int x, int y)
{
unsigned long ref = GetPixel(x, y);
unsigned char r, g, b, byte;
r = ref;
g = ref >> 8;
b = ref >> 16;
if (r == g && r == b)
return r;
double dGray = (0.30*r + 0.59*g + 0.11*b);
// 灰度化
byte = (int)dGray;
return byte;
}
void SaveToBmpFile(const std::string& strBmpFile)
{
if (NULL != m_pBmpFile)
{
fclose(m_pBmpFile);
m_pBmpFile = NULL;
}
m_pBmpFile = fopen(strBmpFile.c_str(), "wb");
BITMAPINFOHEADER InfoHeader = { 0 };
BuildInfoHeader(m_iWidth, m_iHeight, 24, InfoHeader);
BITMAPFILEHEADER bmfHeader = { 0 };
BuildFileHeader(m_iWidth, m_iHeight, 24, bmfHeader);
int nWidthBytes = WIDTHBYTES((m_iWidth)*24);
int iSize = nWidthBytes*m_iHeight;
fwrite(&bmfHeader, sizeof(BITMAPFILEHEADER), 1, m_pBmpFile);
fwrite(&InfoHeader, sizeof(BITMAPINFOHEADER), 1, m_pBmpFile);
//坐标转换
for (int i = m_iHeight - 1; i>=0 ; --i)
{
fwrite(m_pRGBData[i], nWidthBytes, 1, m_pBmpFile);
}
}
//画线 ptStart表示起始点,nLen表示线的长度,nWide表示线的宽度,bHor是横线还是竖线
void Line(POINT ptStart, int nLen, int nWide, BOOL bHor)
{
int i, j;
DWORD dw = RGB(0, 0, 255);
if (bHor)
{
for (i = ptStart.x; i <= nLen + ptStart.x; i++)
{
for (j = 0; j<nWide; j++)
{
SetPixel(i, ptStart.y + j, dw, m_pRGBData);
}
}
}
else
{
for (j = ptStart.y; j <= nLen + ptStart.y; j++)
{
for (i = 0; i<nWide; i++)
{
SetPixel(ptStart.x + i, j, dw, m_pRGBData);
}
}
}
}
private:
void BuildInfoHeader(LONG lWidth, LONG lHeight, WORD wBitCount, BITMAPINFOHEADER &bitmapInfoHeader)
{
LONG lWidthStep = (((lWidth * wBitCount) + 31) & (~31)) / 8;
bitmapInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfoHeader.biWidth = lWidth;
bitmapInfoHeader.biHeight = lHeight;
bitmapInfoHeader.biPlanes = 0;
bitmapInfoHeader.biBitCount = wBitCount;
bitmapInfoHeader.biCompression = BI_RGB;
bitmapInfoHeader.biSizeImage = lWidthStep * lHeight;
bitmapInfoHeader.biXPelsPerMeter = 0;
bitmapInfoHeader.biYPelsPerMeter = 0;
bitmapInfoHeader.biClrUsed = 0;
bitmapInfoHeader.biClrImportant = 0;
}
void BuildFileHeader(LONG lWidth, LONG lHeight, WORD wBitCount, BITMAPFILEHEADER &bitmapFileHeader)
{
LONG lWidthStep = (((lWidth * wBitCount) + 31) & (~31)) / 8;
bitmapFileHeader.bfType = ((WORD)('M' << 8) | 'B'); //'BM'
bitmapFileHeader.bfSize = (DWORD) sizeof(BITMAPFILEHEADER) + (DWORD) sizeof(BITMAPINFOHEADER) + lWidthStep * lHeight;
bitmapFileHeader.bfReserved1 = 0;
bitmapFileHeader.bfReserved2 = 0;
bitmapFileHeader.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + (DWORD) sizeof(BITMAPINFOHEADER);
}
private:
//存储RGB内存数据的指针
unsigned char** m_pRGBData;
FILE *m_pRGBFile;
FILE *m_pBmpFile;
int m_iHeight;
int m_iWidth;
};
int main()
{
CProcessRGB RGBProcess;
//读取rgb24图像
RGBProcess.Init(1280,720,"./preview_rgb24");
//画一条横线
POINT p1;
p1.x = 0;
p1.y = 359;
RGBProcess.Line(p1, 1279, 3, true);
//画一条竖线
POINT p2;
p2.x = 639;
p2.y = 0;
RGBProcess.Line(p2, 719, 3, false);
//将rgb24数据存成bmp文件
RGBProcess.SaveToBmpFile("./preview.bmp");
}
运行结果:
rgb原始数据文件下载
https://download.csdn.net/download/mo4776/10589099