从零开始写一个图像处理程序之五(手撕边缘检测终章)

前四篇的铺垫: 

从零开始写一个图像处理程序之一(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;
}         
 

原来的灰度图:

水平检测后的图片:

 改变算子为Gy:

	int CK[9]={-1,0,1,-1,0,1,-1,0,1};

垂直检测后的图片:

 综合处理:

在同一个像素点取 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;
}         
 

最后的边缘检测得到的图片:

 替换卷积核:

将卷积核替换为不同的内容可以对程序进行不同的处理

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值