平台:STM32F1+FATFS+SD卡

基于STM32的BMP图片处理

  • 处理结果
    • 原图 test.bmp
    • 灰度图 gray.bmp
    • 缩放 zoom.bmp
  • 代码实现
  • 调试过程遇到的几个问题
    • 双线性插值
    • 字节对齐
    • 图片读写
  • 参考文档

处理结果

原图 test.bmp



灰度图 gray.bmp



缩放 zoom.bmp

失真不明显哈,估计是我缩放的比例太小 795 * 457 => 1280 * 720
插值采用的是双线性插值法


代码实现


FATFS myfs;                     // Work area (file system object) for logical driveBITMAPFILEHEADER bitHead;        //原图头信息
BITMAPINFOHEADER bitInfoHead;
BITMAPFILEHEADER grayHead;      //灰度化文件头信息
BITMAPINFOHEADER grayInfoHead;
BITMAPFILEHEADER zoomHead;      //缩放图头信息
BITMAPINFOHEADER zoomInfoHead;
RGBQUAD pColorTable[256];       //调色板u8  bmpfile[5000];         //读取数据缓存
u8  newfile[5000];          //待写入数据缓存/* 打印BMP文件的头信息,用于调试 */
static void showBmpHead(BITMAPFILEHEADER* pBmpHead)
{printf("位图文件头:\r\n");printf("文件大小:%d\r\n",(*pBmpHead).bfSize);printf("保留字:%d\r\n",(*pBmpHead).bfReserved1);printf("保留字:%d\r\n",(*pBmpHead).bfReserved2);printf("实际位图数据的偏移字节数:%d\r\n",(*pBmpHead).bfOffBits);
}/* 打印BMP文件的头信息,用于调试 */
static void showBmpInforHead(tagBITMAPINFOHEADER* pBmpInforHead)
{printf("位图信息头:\r\n");printf("结构体的长度:%d\r\n",(*pBmpInforHead).biSize);printf("位图宽:%d\r\n",(*pBmpInforHead).biWidth);printf("位图高:%d\r\n",(*pBmpInforHead).biHeight);printf("biPlanes平面数:%d\r\n",(*pBmpInforHead).biPlanes);printf("biBitCount采用颜色位数:%d\r\n",(*pBmpInforHead).biBitCount);printf("压缩方式:%d\r\n",(*pBmpInforHead).biCompression);printf("biSizeImage实际位图数据占用的字节数:%d\r\n",(*pBmpInforHead).biSizeImage);printf("X方向分辨率:%d\r\n",(*pBmpInforHead).biXPelsPerMeter);printf("Y方向分辨率:%d\r\n",(*pBmpInforHead).biYPelsPerMeter);printf("使用的颜色数:%d\r\n",(*pBmpInforHead).biClrUsed);printf("重要颜色数:%d\r\n",(*pBmpInforHead).biClrImportant);
}
int main(void)
{FIL file;UINT bw;FRESULT fres;WORD fileType;       //文件类型int bmpWidth;     //原图宽度int bmpHeight;        //原图高度int bmpBitCount;  //颜色位数 24色位图即为24int bmpLinelen;     //原图单行数据长度int grayLinelen;  //灰度图单行数据长度int zoomWidth;       //目标宽度int zoomHeight;       //目标高度int zoomLinelen;  int tmp;unsigned char *pb1; //数据读取指针double r1,r2;       //宽和高的缩放率int x1, x2, y1, y2;    //原图点坐标unsigned char Fq11, Fq12, Fq21, Fq22;//原图点的灰度值double x, y, Fr1, Fr2, Fp;//目标点坐标及目标点灰度值SystemInit();   USART1_Init(); while(1){ f_mount(&myfs,"0:",0);//将原图test.bmp灰度化,保存为gray.bmpfres = f_open(&file , (char *)"test.bmp", FA_OPEN_EXISTING | FA_READ);if ( fres == FR_OK ){printf("Open file success\r\n");//读取位图文件头信息        f_read(&file,&fileType,sizeof(WORD),&bw);     if( fileType != 0x4d42){printf("file is not .bmp file!\r\n"); }else{printf("Ok this is .bmp file\r\n");    }        f_read(&file,&bitHead,sizeof(tagBITMAPFILEHEADER),&bw); showBmpHead(&bitHead); //显示头文件printf("\r\n");//读取位图信息头信息f_read(&file,&bitInfoHead,sizeof(BITMAPINFOHEADER),&bw);        showBmpInforHead(&bitInfoHead);//打印头信息printf("\r\n");          bmpWidth = bitInfoHead.biWidth;bmpHeight = bitInfoHead.biHeight;bmpBitCount = bitInfoHead.biBitCount;bmpLinelen = (bmpWidth * bmpBitCount / 8 + 3) / 4 * 4;//每行字节数printf("bmpLinelen %d\r\n",bmpLinelen);f_close(&file); //关闭原图//==========================================//==================生成灰度图===================//==========================================//现将24位真彩图灰度化fres = f_open(&file , (char *)"gray.bmp", FA_OPEN_ALWAYS | FA_WRITE);    //新建灰度图文件grayLinelen = (bmpWidth * 8 / 8 + 3) / 4 * 4;//由于灰度化后每像素位数变为8,所以每行字节数发生改变,但仍要求为4的整数倍fileType = 0x4D42;f_write(&file,(char *)&fileType, 2, &bw);    //将修改后的文件头存入gray.bmp;   grayHead.bfSize = 14 + 40 + 256 * sizeof(RGBQUAD)+grayLinelen*bmpHeight;//修改文件大小grayHead.bfReserved1 = 0;grayHead.bfReserved2 = 0;grayHead.bfOffBits = 14 + 40 + 256 * sizeof(RGBQUAD);//修改偏移字节数,24位bmp无调色板,原图的便宜字节数为(14+40)f_write(&file,(char *)&grayHead, 12, &bw);    //将修改后的文件头存入gray.bmp;                //修改信息头,其中有两项需要修改,1个位biBitCount:真彩图为24 ,应改成8;另一个是biSizeImage:由于每像素所占位数的变化grayInfoHead.biBitCount = 8;    //将每像素的位数改为8grayInfoHead.biClrImportant = 0;grayInfoHead.biCompression = 0;grayInfoHead.biClrUsed = 0;grayInfoHead.biHeight = bmpHeight;grayInfoHead.biWidth = bmpWidth;grayInfoHead.biPlanes = 1;grayInfoHead.biSize = 40;grayInfoHead.biSizeImage = grayLinelen*bmpHeight;//修改位图数据的大小grayInfoHead.biXPelsPerMeter = 0;grayInfoHead.biYPelsPerMeter = 0;f_write(&file,(char *)&grayInfoHead, 40,&bw);  //将修改后的信息头存入fp1;//调色板for (int i = 0; i < 256; i++){pColorTable[i].rgbRed = i;pColorTable[i].rgbGreen = i;pColorTable[i].rgbBlue = i; //是颜色表里的B、G、R分量都相等,且等于索引值}f_write(&file,(char *)pColorTable, sizeof(RGBQUAD)*256,&bw);  //将修改后的信息头存入gray.bmp;//由于RAM不足,不能直接读取全部原图RGB数据,只能读一行,写一行。f_close(&file); //关闭灰度图for (int i = 0; i < bmpHeight; i++){//读取第i行原图RNG数据f_open(&file , (char *)"test.bmp", FA_OPEN_EXISTING | FA_READ);f_lseek(&file, 14 + 40 + i*bmpLinelen);f_read(&file,&bmpfile,bmpLinelen,&bw);  f_close(&file); //将RGB数据转化为灰度值for (int j = 0; j<bmpWidth; j++){           pb1 = bmpfile + j * 3;tmp = *(pb1)*0.299 + *(pb1 + 1)*0.587 + *(pb1 + 2)*0.114; //将每一个像素都按公式y=B*0.299+G*0.587+R*0.114进行转化*(newfile + j) = tmp;}//写入第i行灰度值f_open(&file , (char *)"gray.bmp", FA_OPEN_ALWAYS | FA_WRITE);f_lseek(&file, 14 + 40  + 256 * sizeof(RGBQUAD) + i*grayLinelen);f_write(&file,(char *)newfile, grayLinelen,&bw);  //将修改后的信息头存入fp1;f_close(&file); }        }//==========================================//==================生成缩放图===================//==========================================//将图片规格缩放为1280*720zoomWidth = 1280;zoomHeight = 720;fres = f_open(&file , (char *)"zoom.bmp", FA_OPEN_ALWAYS | FA_WRITE);zoomLinelen = (zoomWidth * 8 / 8 + 3) / 4 * 4;fileType = 0x4D42;f_write(&file,(char *)&fileType, 2, &bw);    //将修改后的文件头存入fp1; zoomHead.bfSize = 14 + 40 + 256 * sizeof(RGBQUAD)+zoomLinelen*zoomHeight;//修改文件大小zoomHead.bfReserved1 = 0;zoomHead.bfReserved2 = 0;zoomHead.bfOffBits = 14 + 40 + 256 * sizeof(RGBQUAD);//修改偏移字节数f_write(&file,(char *)&zoomHead, 12, &bw);    //将修改后的文件头存入fp1;    //修改信息头,其中有两项需要修改,1个位biBitCount:真彩图为24 ,应改成8;//另一个是biSizeImage:由于每像素所占位数的变化,所以位图数据的大小发生变化zoomInfoHead.biBitCount = 8;    //将每像素的位数改为8zoomInfoHead.biClrImportant = 0;zoomInfoHead.biCompression = 0;zoomInfoHead.biClrUsed = 0;zoomInfoHead.biHeight = zoomHeight;zoomInfoHead.biWidth = zoomWidth;zoomInfoHead.biPlanes = 1;zoomInfoHead.biSize = 40;zoomInfoHead.biSizeImage = zoomLinelen*zoomHeight;//修改位图数据的大小zoomInfoHead.biXPelsPerMeter = 0;zoomInfoHead.biYPelsPerMeter = 0;f_write(&file,(char *)&zoomInfoHead, 40,&bw);  //将修改后的信息头存入fp1;for (int i = 0; i < 256; i++){pColorTable[i].rgbRed = i;pColorTable[i].rgbGreen = i;pColorTable[i].rgbBlue = i; //是颜色表里的B、G、R分量都相等,且等于索引值}f_write(&file,(char *)pColorTable, sizeof(RGBQUAD)*256,&bw);  //将修改后的信息头存入fp1;f_close(&file); r1 = (double)bmpHeight / zoomHeight;r2 = (double)bmpWidth / zoomWidth;for (int i = 0; i < zoomHeight; ++i){y = (double)r1*i;y1 = (int)(y);  if(y1 == bmpHeight-1) y1 = bmpHeight-2;y2 = y1 + 1;f_open(&file , (char *)"gray.bmp", FA_OPEN_EXISTING | FA_READ);f_lseek(&file, 14 + 40 + 256 * sizeof(RGBQUAD)+ y1*grayLinelen);f_read(&file,&bmpfile,grayLinelen*2,&bw);  f_close(&file); for (int j = 0; j < zoomWidth; ++j){x = (double)r2*j; // 原图像坐标// 四个坐标值x1 = (int)(x);  if(x1 == zoomWidth-1)x1 = zoomWidth-2;x2 = x1 + 1;// 四个坐标对应的灰度值Fq11 = *(bmpfile  + x1);Fq12 = *(bmpfile + grayLinelen + x1);Fq21 = *(bmpfile  + x2);Fq22 = *(bmpfile + grayLinelen + x2);// x方向插值和y方向插值Fr1 = 0; Fr2 = 0;Fr1 = (double)(x2 - x)*Fq11/ (x2 - x1) + (double)(x - x1)*Fq21/ (x2 - x1);Fr2 = (double)(x2 - x)*Fq12 / (x2 - x1) + (double)(x - x1)*Fq22/ (x2 - x1);Fp = (double)(y2 - y)*Fr1 / (y2 - y1) + (double)(y - y1)*Fr2/ (y2 - y1);// 新图像灰度值赋值//if (Fp >= 0 && Fp <= 255){pb1 = (unsigned char *)(newfile + j); // 新图像(*pb1 ) = (char)(Fp);//}}printf("%d\r\n",i+1);f_open(&file , (char *)"zoom.bmp", FA_OPEN_ALWAYS | FA_WRITE);f_lseek(&file, 14 + 40  + 256 * sizeof(RGBQUAD) + i*zoomLinelen);f_write(&file,(char *)newfile, zoomLinelen,&bw);  //将修改后的信息头存入fp1;f_close(&file);                    }}}

调试过程遇到的几个问题

双线性插值


  • x、y是图片的坐标参数
  • f(Q11)、f(Q12)、f(R1)、f( P)、··· 分别是Q11、Q12、R1、P、···点的灰度值

字节对齐

typedef struct tagBITMAPFILEHEADER {
//WORD    bfType;
DWORD   bfSize;
WORD    bfReserved1;
WORD    bfReserved2;
DWORD   bfOffBits;
} BITMAPFILEHEADER
BITMAPFILEHEADER bitHead;
f_read(&file,&fileType,sizeof(WORD),&bw);
if( fileType != 0x4d42)
{printf("file is not .bmp file!\r\n");
}
else
{printf("Ok this is .bmp file\r\n");
}
f_read(&file,&bitHead,sizeof(tagBITMAPFILEHEADER),&bw);
showBmpHead(&bitHead); //显示头文件
printf("\r\n");

需要将 BITMAPFILEHEADER 结构体中的 参数bfType 屏蔽,文件类型不放在结构体中,单独读写。

typedef unsinged short WORD
typedef unsinged long DWORD
  • WORD为2个字节
  • DWORD为4个字节

所以 BITMAPFILEHEADER 是4字节对齐

链接: C语言struct字节对齐问题

不屏蔽参数bfType时
- sizeof(BITMAPFILEHEADER) = 16;
- &(bitHead.bfSize) = &bitHead + 4;
屏蔽参数bfType时
- sizeof(BITMAPFILEHEADER) = 12;
- &(bitHead.bfSize) = &bitHead;

图片读写

  • 跟参考链接的平台不同,MCU资源有限,不能一次全部读出,需要读一行、处理一行、写一行。
  • 双线性插值时,y值最大为bmpHeight-1,注意读取y2 = y1 + 1坐标灰度时,内存溢出。
    y1 = (int)(y);
    if(y1 == bmpHeight-1) y1 = bmpHeight-2;
    y2 = y1 + 1;
  • 注意强制类型转换。我没仔细区分,为了省事除法都强制转换为(double)。
  • 还有就是单片机进行浮点型运算是真的慢。。。

参考文档

  • 链接: C语言实现BMP图像处理(彩色图转灰度图)

  • 链接: C语言实现BMP图像处理(缩放)

基于STM32的BMP图片解码灰度化以及缩放相关推荐

  1. 用C#编写一个图片浏览器,实现鼠标控制图片的平移缩放与图片的灰度化

    1. 界面设计 如图1 所示:一个名为ImView 的Form 只中包含有一个名为picturebox 的Picturebox.图2 是它的运行结果.该程序的界面设计较为简单,主要根据鼠标行为及键盘按 ...

  2. Java_最不重要位替换(LSB)基于24位BMP图片

    隐写术的一个简单示例 向BMP图片中隐藏一段文字并保存,从保存的图片中提取文字. 原理:把需要隐藏的文本信息转换成二进制字符流,再将其拆分成一个个的0和1,隐藏在像素数据(RGB字节)中,因对RGB的 ...

  3. MATLAB基础图形处理实现图形通道转换灰度化旋转缩放镜像拼接

    %practice,还是以彩色荷花图片lotus为例 %读入图片数据 Image1=imread('carphone.jpg');%图片1是原图,汽车与手机JPG格式的原图 %红绿通道互换 Image ...

  4. stm32显示bmp图片

    目录 1.介绍 2.代码 2.1 定义 2.2 图片显示 2.3 格式转换 3.实现效果 4.源码地址 1.介绍 使用stm32单片机将bmp格式的图片显示在屏幕上,如果图片尺寸大于屏幕就将其缩放,图 ...

  5. Linux图片的灰度化,iOS图像灰度解决方案--架构设计

    ZUNL7OS33q.gif 这是一个类似于QQ头像的处理方法,据我所知QQ也是用这种方式处理的,当然我们有两种方案可以选择 第一种方案 使用第三方工具 ---OpenCV(官网内可下载包文件) Op ...

  6. 基于STM32的多功能MP3设计 毕业设计(论文)开题报告

    中国计量学院 毕业设计(论文)开题报告 学生姓名:卢杰学 号:XXXXXXXXX 专    业:电子科学与技术 班    级:10电子1 设计(论文)题目: 基于STM32的多功能MP3设计 指导教师 ...

  7. 图片识别为什么大部分都将彩色图像灰度化

    对于图片识别灰度化的原因这里根据自己的理解和网上看到的一些自己觉得合理的解释这里做个大概总结,如有错误欢迎大神们打脸指正 最直接的原因:减少计算量 包含色彩的图片,特征量,计算量会成指数倍数增加 比如 ...

  8. 从零开始用C语言实现图片解码播放器(有源码)

    1.项目描述 1.1.项目硬件平台介绍 (1)硬件平台:九鼎公司的X210开发板,S5PV210(Cortex-A8内核): (2)软件平台:linux2.6.35.7内核,直接基于linux API ...

  9. 图像的色彩类别,灰度化,二值化

    灰度化:在RGB模型中,如果R=G=B时,则彩色表示一种灰度颜色,其中R=G=B的值叫灰度值,因此,灰度图像每个像素只需一个字节存放灰度值(又称强度值.亮度值),灰度范围为0-255.一般常用的是加权 ...

最新文章

  1. 21. Merge Two Sorted Lists
  2. AOP:【动态代理】||@Pointcut
  3. Err Welcoe to emergency mode
  4. Debian10降级安装php,如何在Debian 10 Linux上安装PHP
  5. sap 新建事务_SAP GUI里的收藏夹事务码管理工具
  6. 力扣——206.反转链表
  7. 小程序 自适应rpx
  8. (二)docker常用命令
  9. svn管理ad元件库_AD元器件库服务器管理指南
  10. PCIE知识点-003:PCIE协议中的upstream概念
  11. 2.aop原理:@EnableAspectJAutoProxy
  12. CodeSmith(1):使用和语法简介
  13. 一个2022本科生的秋招总结 (大疆、Arm、小米、荣耀、美团、联发科等)
  14. 二进制和 四,八,十,十六,三十二进制的转换
  15. web前端开发是什么?
  16. QQ欢乐斗地主心得体会 (三):高倍场攻略
  17. Pycharm 注册码
  18. Asymptotic Analysis——渐近分析
  19. 在线客服系统源码,多商户在线客服系统可开机器人自动聊天多商户在线客服源码
  20. “印度管理”会成为超越中国的秘密武器吗?[高度关注]

热门文章

  1. qstring 字符串查找_QString总结
  2. Jmeter插件duang duang duang 学会模拟各种场景
  3. 2022年教师资格考试(高中历史学科知识)经典试题及答案
  4. Windows系统创建虚拟环境操作
  5. 报错:JSON parse error: Unexpected character (‘ ‘ (code 160)): was expecting double-quote to start fiel
  6. cannot set up a python SDK
  7. yolov3的GUI界面(简易,图片检测)
  8. 龙讯LT6911系列C/UXC/UXB/GXC/GXB芯片功能区别阐述
  9. 男人必吃的12种健康食物,程序员更得看看!
  10. 淘宝架构框架《上篇》