2024年彩色BMP转换成灰度图的原理_bmp灰度图,2024年最新连续四年百度物联网嵌入式开发岗必问面试题

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

易就查得到,这里主要介绍其中的调色板,和图象数据部分。

对于非真彩的位图,都有一个调色板,调色板的格式如下

typedef struct tagRGBQUAD{

BYTE rgbBlue;//蓝色的分量
 BYTE rgbGreen;//绿色的分量
 BYTE rgbRed;//红色的分量
 BYTE rgbReserved;//保留值不用管它为0就好

}RGBQUAD;
一般的调色版是一个,由上面的结构体组成的结构体数组,存储具体的颜色信息,而位图中,图象数据部分存储的只是调色板的下标

。这样做就可以大大的节省空间。

例如:
RGBQUAD rgb[2];
rgb[0].rgbBlue = 0;
rgb[0].rgbGreen = 0;
rgb[0].rgbRed = 0;
rgb[0].rgbReserved = 0;
rgb[1].rgbBlue = 255;
rgb[1].rgbGreen = 255;
rgb[1].rgbRed = 255;
rgb[1].rgbReserved = 255;

这个长度为2的RGBQUAD数组就是一个1位2色黑白图的调色板,
在位图数据部分只需要用1位的长度存储0表示黑,1表示白就可以了,1字节可以表示8个像素的信息,比用3字节直接表示R,G,B节

省了24倍的存储空间

而真彩图则不然,比如24位图,那么他就需要一个数组大小为2的24次方的调色板,而调色板的下标也需要3个字节才储存,这样还

不如直接就R,G,B这三个分量来直接表示每一个像素的色值。使用调色板技术还浪费了一个256*256*256*3字节大的调色板空间.

而这里要用4位表示一个灰度图,那么它的调色板只有16项,每一项的RGB值同通常由256色构成的灰度图的调色板一样的道理

这里这样建立这个调色板

RGBQUAD pa[16];
 BYTE c;
 for(int i=0;i<16;i++)
 {
  c= i * 17;
  pa[i].rgbRed = c;
  pa[i].rgbGreen = c;
  pa[i].rgbBlue = c;
  pa[i].rgbReserved = 0;
 }

2.转换算法

现在的图象是24位真彩的,表示它的数据部分,3字节表示一个像素,这三个字节分别表示RGB。
我们现在要做的是求每一像素点的RGB值的平均值,然后用16色调色板中最接近这个颜色亮度的值来表示它。
而4位的图象是1个字节表示2个像素,在这里需要特殊注意

具体算法实现代码如下,pBuffer是储存图象数据的数组

USHORT R,G,B;

// 第一个像素
  R = pBuffer[dwIndex++];
  G = pBuffer[dwIndex++];
  B = pBuffer[dwIndex++];

int maxcolor = (R+G+B)/3;

maxcolor /= 17;//计算在16色调色板中的下标

//第二个像素
  R = pBuffer[dwIndex++];
  G = pBuffer[dwIndex++];
  B = pBuffer[dwIndex++];

int maxcolor2 = (R+G+B)/3;

maxcolor2 /= 17;
  
  pNew[dwOldIndex++] = ( maxcolor<<4 )| maxcolor2;//合成一个字节表示两个像素

3.实现代码

完整的实现代码如下
BOOL Convert24To4(LPCTSTR lpszSrcFile, LPCTSTR lpszDestFile)//24->4灰度图
{
 BITMAPFILEHEADER bmHdr;  // BMP文件头
 BITMAPINFOHEADER bmInfo; // BMP文件信息

HANDLE hFile, hNewFile;
 DWORD dwByteWritten = 0;

// 打开源文件句柄
 hFile = CreateFile(lpszSrcFile, 
        GENERIC_READ,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL);

if (hFile == INVALID_HANDLE_VALUE)
  return FALSE;

// 创建新文件
 hNewFile = CreateFile(lpszDestFile,
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL);
 if (hNewFile == INVALID_HANDLE_VALUE)
 {
  CloseHandle(hFile);
  return FALSE;
 }

// 读取源文件BMP头和文件信息
 ReadFile(hFile, &bmHdr, sizeof(bmHdr), &dwByteWritten, NULL); 
 ReadFile(hFile, &bmInfo, sizeof(bmInfo), &dwByteWritten, NULL);

TRACE("biSize: %d , biWidth: %d , biHeight: %d , biBitCount: %d , biSizeImage: %d

/n",bmInfo.biSize,bmInfo.biWidth,bmInfo.biHeight,bmInfo.biBitCount,bmInfo.biSizeImage);
 TRACE("biX: %d , biY: %d , biClrUsed: %d , biClrImportant: %d

/n",bmInfo.biXPelsPerMeter,bmInfo.biYPelsPerMeter,bmInfo.biClrUsed,bmInfo.biClrImportant);

// 只处理24位未压缩的图像
 if (bmInfo.biBitCount != 24 || bmInfo.biCompression!=0)
 {
  CloseHandle(hNewFile);
  CloseHandle(hFile);
  DeleteFile(lpszDestFile);
  return FALSE;
 }

// 计算图像数据大小
 DWORD dwOldSize = bmInfo.biSizeImage;
 if(dwOldSize == 0) // 重新计算
 {
  dwOldSize = bmHdr.bfSize - sizeof(bmHdr) - sizeof(bmInfo);
 }

TRACE(“Old Width: %d , Old Height: %d ,Old Size: %d bytes/n”,bmInfo.biWidth,bmInfo.biHeight,dwOldSize);

long wid = bmInfo.biWidth % 4;

if(wid>0)
 {
  wid = 4 - wid;
 }

wid += bmInfo.biWidth;

DWORD dwNewSize;

dwNewSize = wid * bmInfo.biHeight / 2; //计算转换后新图象大小

TRACE(“New Size: %d bytes/n”, dwNewSize);

// 读取原始数据
 UCHAR *pBuffer = NULL;
 pBuffer = new UCHAR[dwOldSize]; // 申请原始数据空间
 if(pBuffer == NULL)
 {
  CloseHandle(hNewFile);
  CloseHandle(hFile);
  DeleteFile(lpszDestFile);
  return FALSE;
 }
 // 读取数据
 ReadFile(hFile, pBuffer, dwOldSize, &dwByteWritten, NULL);

UCHAR *pNew = new UCHAR[dwNewSize];

UCHAR  color = 0;
 DWORD dwIndex = 0, dwOldIndex = 0;
 while( dwIndex < dwOldSize )//一字节表示两个像素
 {
  USHORT R,G,B;

// 第一个像素
  R = pBuffer[dwIndex++];
  G = pBuffer[dwIndex++];
  B = pBuffer[dwIndex++];

int maxcolor = (R+G+B)/3;

maxcolor /= 17;

//第二个像素
  R = pBuffer[dwIndex++];
  G = pBuffer[dwIndex++];
  B = pBuffer[dwIndex++];

int maxcolor2 = (R+G+B)/3;

maxcolor2 /= 17;
  
  pNew[dwOldIndex++] = ( maxcolor<<4 )| maxcolor2;//合成一个字节表示两个像素

}

// 完工, 把结果保存到新文件中

// 修改属性
 bmHdr.bfSize = sizeof(bmHdr)+sizeof(bmInfo)+sizeof(RGBQUAD)*16+dwNewSize;
 bmHdr.bfOffBits = bmHdr.bfSize - dwNewSize;
 bmInfo.biBitCount = 4;
 bmInfo.biSizeImage = dwNewSize;

// 创建调色板
 RGBQUAD pa[16];
 UCHAR c;
 for(int i=0;i<16;i++)
 {
  c= i * 17;
  pa[i].rgbRed = c;
  pa[i].rgbGreen = c;
  pa[i].rgbBlue = c;
  pa[i].rgbReserved = 0;
 }

// BMP头
 WriteFile(hNewFile, &bmHdr, sizeof(bmHdr), &dwByteWritten, NULL);
 // 文件信息头
 WriteFile(hNewFile, &bmInfo, sizeof(bmInfo), &dwByteWritten, NULL);
 // 调色板
 WriteFile(hNewFile, pa, sizeof(RGBQUAD)*16, &dwByteWritten, NULL);
 // 文件数据
 WriteFile(hNewFile, pNew, dwNewSize, &dwByteWritten, NULL);

delete []pBuffer;
 delete []pNew;

// 关闭文件句柄
 CloseHandle(hNewFile);
 CloseHandle(hFile);

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

CloseHandle(hFile);

[外链图片转存中…(img-TZwdtxY2-1715629790451)]
[外链图片转存中…(img-sg76YcRg-1715629790453)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

  • 15
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值