前四篇的铺垫:
从零开始写一个图像处理程序之一(BMP彩色图转灰度图)_星空_MAX的博客-CSDN博客
从零开始写一个图像处理程序之二(提取灰度图通道)_星空_MAX的博客-CSDN博客
从零开始写一个图像处理程序之三(卷积原理简介)_星空_MAX的博客-CSDN博客
从零开始写一个图像处理程序之四(Prewitt 算子分析)_星空_MAX的博客-CSDN博客
定义Prewitt算子(convolution kernel)简称CK:
先定义Gx:
int CK[9]={1,1,1,0,0,0,-1,-1,-1};
边缘处理:由于经过一次卷积的话,图像会缩水一个像素,所以在原本的channel上围上一圈0
std::vector<unsigned char>channel((bihead.biHeight+2)*(bihead.biWidth+2),0);
将图片读取到新位置:
for(int i=0;i<bihead.biHeight;i++)
for(int j=0;j<bihead.biWidth;j++)
{
unsigned char *pb;
pb=pBmpBuf+i*LineByte+j;
channel[(bihead.biWidth+2)*(i+1)+j+1]=*pb;
}
开始卷积操作:
for(int i=0;i<bihead.biHeight;i++)
for(int j=0;j<bihead.biWidth;j++)
{
result[bihead.biWidth*i+j]=channel[(bihead.biWidth+2)*i+j] *CK[0]+
channel[(bihead.biWidth+2)*i+j+1]*CK[1]+
channel[(bihead.biWidth+2)*i+j+2]*CK[2]+
channel[(bihead.biWidth+2)*(i+1)+j] *CK[3]+
channel[(bihead.biWidth+2)*(i+1)+j+1]*CK[4]+
channel[(bihead.biWidth+2)*(i+1)+j+2]*CK[5]+
channel[(bihead.biWidth+2)*(i+2)+j] *CK[6]+
channel[(bihead.biWidth+2)*(i+2)+j+1]*CK[7]+
channel[(bihead.biWidth+2)*(i+2)+j+2]*CK[8];
}
将新图像填入result中:
bfhead.bfSize=14+40+sizeof(RGBQUAD)*256+LineByte*bihead.biHeight;
bfhead.bfOffBits=14+40+sizeof(RGBQUAD)*256;
//将bmpfilehead写入
fwrite(&bfhead,14,1,newfp);
// 将bmpbithead写入
bihead.biBitCount=8; //更改每像素位数
bihead.biSizeImage=LineByte*bihead.biHeight;
fwrite(&bihead,40,1,newfp);
//灰度为1的rgb值分别为1,1,1,所以将256位颜色的颜色表分别置为相同的灰度值
RGBQUAD *pColorTable=new RGBQUAD[256];
for(int i=0;i<256;i++)
{
pColorTable[i].rgbRed = pColorTable[i].rgbGreen = pColorTable[i].rgbBlue = i;//使颜色表中每种颜色的R,G,B分量相等且等于索引值
}
fwrite(pColorTable,sizeof(RGBQUAD),256,newfp);
unsigned char *pNewBmpBuf=new unsigned char[LineByte*bihead.biHeight];
for(int i=0;i<bihead.biHeight;i++)
for(int j=0;j<bihead.biWidth;j++)
{
unsigned char *newpb;
newpb=pNewBmpBuf+i*LineByte+j;
*newpb=result[(bihead.biWidth+2)*i+j];
}
fwrite(pNewBmpBuf,LineByte*bihead.biHeight,1,newfp);
全部代码:
#include<stdio.h>
#include<windows.h>
#include<vector>
int main()
{
BITMAPFILEHEADER bfhead;
BITMAPINFOHEADER bihead;
FILE *fp=fopen("newcat.bmp","rb");
FILE *newfp=fopen("newnewcat.bmp","wb");
if(fp==0)
return 0;
fread(&bfhead,14,1,fp); //将文件头读入内存
fread(&bihead,40,1,fp); //将信息头读入内存
int LineByte=(bihead.biWidth*8/8+3)/4*4; //保证每行字节数为4的整数倍
unsigned char *pBmpBuf=new unsigned char[LineByte*bihead.biHeight]; //为数据区分配内存空间
fread(pBmpBuf,LineByte*bihead.biHeight,1,fp); //将bmp数据区读入内存
fclose(fp);
std::vector<unsigned char>channel((bihead.biHeight+2)*(bihead.biWidth+2),0);
std::vector<unsigned char>result(bihead.biHeight*bihead.biWidth,0);
int CK[9]={1,1,1,0,0,0,-1,-1,-1};
for(int i=0;i<bihead.biHeight;i++)
for(int j=0;j<bihead.biWidth;j++)
{
unsigned char *pb;
pb=pBmpBuf+i*LineByte+j;
channel[(bihead.biWidth+2)*(i+1)+j+1]=*pb;
}
for(int i=0;i<bihead.biHeight;i++)
for(int j=0;j<bihead.biWidth;j++)
{
result[bihead.biWidth*i+j]=channel[(bihead.biWidth+2)*i+j] *CK[0]+
channel[(bihead.biWidth+2)*i+j+1]*CK[1]+
channel[(bihead.biWidth+2)*i+j+2]*CK[2]+
channel[(bihead.biWidth+2)*(i+1)+j] *CK[3]+
channel[(bihead.biWidth+2)*(i+1)+j+1]*CK[4]+
channel[(bihead.biWidth+2)*(i+1)+j+2]*CK[5]+
channel[(bihead.biWidth+2)*(i+2)+j] *CK[6]+
channel[(bihead.biWidth+2)*(i+2)+j+1]*CK[7]+
channel[(bihead.biWidth+2)*(i+2)+j+2]*CK[8];
}
//创建新图片
bfhead.bfSize=14+40+sizeof(RGBQUAD)*256+LineByte*bihead.biHeight;
bfhead.bfOffBits=14+40+sizeof(RGBQUAD)*256;
//将bmpfilehead写入
fwrite(&bfhead,14,1,newfp);
// 将bmpbithead写入
bihead.biBitCount=8; //更改每像素位数
bihead.biSizeImage=LineByte*bihead.biHeight;
fwrite(&bihead,40,1,newfp);
//灰度为1的rgb值分别为1,1,1,所以将256位颜色的颜色表分别置为相同的灰度值
RGBQUAD *pColorTable=new RGBQUAD[256];
for(int i=0;i<256;i++)
{
pColorTable[i].rgbRed = pColorTable[i].rgbGreen = pColorTable[i].rgbBlue = i;//使颜色表中每种颜色的R,G,B分量相等且等于索引值
}
fwrite(pColorTable,sizeof(RGBQUAD),256,newfp);
unsigned char *pNewBmpBuf=new unsigned char[LineByte*bihead.biHeight];
for(int i=0;i<bihead.biHeight;i++)
for(int j=0;j<bihead.biWidth;j++)
{
unsigned char *newpb;
newpb=pNewBmpBuf+i*LineByte+j;
*newpb=result[bihead.biWidth*i+j];
}
fwrite(pNewBmpBuf,LineByte*bihead.biHeight,1,newfp);
printf("\nWidth:%d, Height:%d,biBitCount:%d\n",bihead.biWidth,bihead.biHeight,bihead.biBitCount);
fclose(fp);
fclose(newfp);
return 0;
}
原来的灰度图:![](https://i-blog.csdnimg.cn/blog_migrate/2a484580e6ed5e04f27bffeaea65ffc8.png)
水平检测后的图片:
改变算子为Gy:
int CK[9]={-1,0,1,-1,0,1,-1,0,1};
垂直检测后的图片:![](https://i-blog.csdnimg.cn/blog_migrate/7d77ea149f5ab4724a2d75b04dfb0f92.png)
综合处理:
在同一个像素点取 P(i,j)=max[G(i),G(j)]
即横向检测和纵向检测去一个最大值:
#include<stdio.h>
#include<windows.h>
#include<vector>
#define MAX(a,b) a>b?a:b
int main()
{
BITMAPFILEHEADER bfhead;
BITMAPINFOHEADER bihead;
FILE *fp=fopen("newcat.bmp","rb");
FILE *newfp=fopen("newnewcat.bmp","wb");
if(fp==0)
return 0;
fread(&bfhead,14,1,fp); //将文件头读入内存
fread(&bihead,40,1,fp); //将信息头读入内存
int LineByte=(bihead.biWidth*8/8+3)/4*4; //保证每行字节数为4的整数倍
unsigned char *pBmpBuf=new unsigned char[LineByte*bihead.biHeight]; //为数据区分配内存空间
fread(pBmpBuf,LineByte*bihead.biHeight,1,fp); //将bmp数据区读入内存
fclose(fp);
std::vector<unsigned char>channel((bihead.biHeight+2)*(bihead.biWidth+2),0);
std::vector<unsigned char>result(bihead.biHeight*bihead.biWidth,0);
int CK1[9]={1,1,1,0,0,0,-1,-1,-1};
int CK2[9]={-1,0,1,-1,0,1,-1,0,1};
for(int i=0;i<bihead.biHeight;i++)
for(int j=0;j<bihead.biWidth;j++)
{
unsigned char *pb;
pb=pBmpBuf+i*LineByte+j;
channel[(bihead.biWidth+2)*(i+1)+j+1]=*pb;
}
for(int i=0;i<bihead.biHeight;i++)
for(int j=0;j<bihead.biWidth;j++)
{
result[bihead.biWidth*i+j]=MAX(channel[(bihead.biWidth+2)*i+j] *CK1[0]+
channel[(bihead.biWidth+2)*i+j+1]*CK1[1]+
channel[(bihead.biWidth+2)*i+j+2]*CK1[2]+
channel[(bihead.biWidth+2)*(i+1)+j] *CK1[3]+
channel[(bihead.biWidth+2)*(i+1)+j+1]*CK1[4]+
channel[(bihead.biWidth+2)*(i+1)+j+2]*CK1[5]+
channel[(bihead.biWidth+2)*(i+2)+j] *CK1[6]+
channel[(bihead.biWidth+2)*(i+2)+j+1]*CK1[7]+
channel[(bihead.biWidth+2)*(i+2)+j+2]*CK1[8],
channel[(bihead.biWidth+2)*i+j] *CK2[0]+
channel[(bihead.biWidth+2)*i+j+1]*CK2[1]+
channel[(bihead.biWidth+2)*i+j+2]*CK2[2]+
channel[(bihead.biWidth+2)*(i+1)+j] *CK2[3]+
channel[(bihead.biWidth+2)*(i+1)+j+1]*CK2[4]+
channel[(bihead.biWidth+2)*(i+1)+j+2]*CK2[5]+
channel[(bihead.biWidth+2)*(i+2)+j] *CK2[6]+
channel[(bihead.biWidth+2)*(i+2)+j+1]*CK2[7]+
channel[(bihead.biWidth+2)*(i+2)+j+2]*CK2[8]) ;
}
//创建新图片
bfhead.bfSize=14+40+sizeof(RGBQUAD)*256+LineByte*bihead.biHeight;
bfhead.bfOffBits=14+40+sizeof(RGBQUAD)*256;
//将bmpfilehead写入
fwrite(&bfhead,14,1,newfp);
// 将bmpbithead写入
bihead.biBitCount=8; //更改每像素位数
bihead.biSizeImage=LineByte*bihead.biHeight;
fwrite(&bihead,40,1,newfp);
//灰度为1的rgb值分别为1,1,1,所以将256位颜色的颜色表分别置为相同的灰度值
RGBQUAD *pColorTable=new RGBQUAD[256];
for(int i=0;i<256;i++)
{
pColorTable[i].rgbRed = pColorTable[i].rgbGreen = pColorTable[i].rgbBlue = i;//使颜色表中每种颜色的R,G,B分量相等且等于索引值
}
fwrite(pColorTable,sizeof(RGBQUAD),256,newfp);
unsigned char *pNewBmpBuf=new unsigned char[LineByte*bihead.biHeight];
for(int i=0;i<bihead.biHeight;i++)
for(int j=0;j<bihead.biWidth;j++)
{
unsigned char *newpb;
newpb=pNewBmpBuf+i*LineByte+j;
*newpb=result[bihead.biWidth*i+j];
}
fwrite(pNewBmpBuf,LineByte*bihead.biHeight,1,newfp);
printf("\nWidth:%d, Height:%d,biBitCount:%d\n",bihead.biWidth,bihead.biHeight,bihead.biBitCount);
fclose(fp);
fclose(newfp);
return 0;
}
最后的边缘检测得到的图片:![](https://i-blog.csdnimg.cn/blog_migrate/96f92ecf4895feb5665df30f5fa6012c.png)
替换卷积核:
将卷积核替换为不同的内容可以对程序进行不同的处理