图像处理知识点1:Canny算子C++实现(纯C++)

       Canny算子的原理已经有很多博文在叙述了,在这里就不做过多介绍。以下是我将opencv的程序转为c++的结果。代码写的有点拉跨。发着玩。

第一部分bmp图像读写(如果是24位图进行灰度化)。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>  
#include <cmath>
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
#include <math.h>
#include<time.h>
#include <iomanip>
#pragma warning(disable:4996)
//#define PI 3.1415926535
using namespace std;
BITMAPFILEHEADER bmpFileHeader; //位图文件头  
BITMAPINFOHEADER bmpInfoHeader; //位图信息头  
RGBQUAD* pColorTable;           //颜色表,注:24位真彩色图无颜色表  
unsigned char* pBmpData;        //位图数据  
unsigned char* pGrayData;       //灰度图像数据

unsigned char* dp;     //定义写入位图数据的指针
int height;            //定义图像高度
int width;             //定义图像宽度
int biBitCount;        //图像类型,每像素所占的位数
int lineBytes;         //定义变量,即图像每行像素所占的字节数(必须为4的倍数)


//********************定义读取位图函数**************************//
bool readBmp(char* fileName) {
	FILE* fp = fopen(fileName, "rb");    //以二进制读方式打开  
	if (NULL == fp) {
		cout << "File is opened failure!" << endl;
		return FALSE;
	}
	//读取数据  
	fread(&bmpFileHeader, sizeof(BITMAPFILEHEADER), 1, fp);
	fread(&bmpInfoHeader, sizeof(BITMAPINFOHEADER), 1, fp);
	height = bmpInfoHeader.biHeight;
	width = bmpInfoHeader.biWidth;
	//如果是8位灰度图像
	if (bmpInfoHeader.biBitCount == 8) {
		lineBytes = ((bmpInfoHeader.biWidth + 3) / 4) * 4;
		bmpInfoHeader.biSizeImage = lineBytes * bmpInfoHeader.biHeight;
		pGrayData = new unsigned char[bmpInfoHeader.biSizeImage];   //申请空间,大小为位图数据大小 
		//dp = new unsigned char[bmpInfoHeader.biSizeImage];
		pColorTable = new RGBQUAD[256];//定义颜色表数据空间
		fread(pColorTable, sizeof(RGBQUAD), 256, fp);//将fp数据读入颜色表指针空间
		fread(pGrayData, 1, bmpInfoHeader.biSizeImage, fp);
	}
	//若是24位彩色图,则进行灰度化处理
	if (bmpInfoHeader.biBitCount == 24) {
		bmpFileHeader.bfOffBits += (sizeof(RGBQUAD) * 256);       //原先的54字节加上颜色表
		bmpInfoHeader.biBitCount = 8;
		lineBytes = (bmpInfoHeader.biWidth + 3) / 4 * 4;
		int oldLineBytes = (bmpInfoHeader.biWidth * 3 + 3) / 4 * 4;
		int oldSize = bmpInfoHeader.biSizeImage;        //原图数据大小  
		bmpInfoHeader.biSizeImage = lineBytes * bmpInfoHeader.biHeight;
		//定义灰度图像的颜色表  
		pColorTable = new RGBQUAD[256];
		for (int i = 0; i < 256; i++) {
			(*(pColorTable + i)).rgbBlue = i;
			(*(pColorTable + i)).rgbGreen = i;
			(*(pColorTable + i)).rgbRed = i;
			(*(pColorTable + i)).rgbReserved = 0;
		}
		//将RGB转换为灰度值  
		int red, green, blue;
		pBmpData = new unsigned char[oldLineBytes * bmpInfoHeader.biHeight];   //申请空间,大小为位图数据大小 
		fread(pBmpData, 1, oldSize, fp);
		pGrayData = new unsigned char[bmpInfoHeader.biSizeImage];
		//dp = new unsigned char[bmpInfoHeader.biSizeImage];
		memset(pGrayData, 0, bmpInfoHeader.biSizeImage);  //为pGrayData赋初值
		for (int i = 0; i < bmpInfoHeader.biHeight; i++) {
			for (int j = 0; j < bmpInfoHeader.biWidth; j++) {
				blue = *(pBmpData + i * oldLineBytes + 3 * j);
				green = *(pBmpData + i * oldLineBytes + 3 * j + 1);
				red = *(pBmpData + i * oldLineBytes + 3 * j + 2);
				*(pGrayData + i * lineBytes + j) = (red * 0.299 + green * 0.587 + blue * 0.114); 
			}
		}
		delete[]pBmpData;
	}
	fclose(fp);         //关闭文件  
	return TRUE;
}

//**********************写入位图****************************//
bool writeBmp(char* fileName, unsigned char* dp )
{
	FILE* fp = fopen(fileName, "wb");   //以二进制写方式打开  
	if (NULL == fp) {
		cout << "File is opened failure!" << endl;
		return FALSE;
	}
	//写入数据  
	fwrite(&bmpFileHeader, sizeof(BITMAPFILEHEADER), 1, fp);
	fwrite(&bmpInfoHeader, sizeof(BITMAPINFOHEADER), 1, fp);
	fwrite(pColorTable, sizeof(RGBQUAD), 256, fp);
	fwrite(dp, bmpInfoHeader.biSizeImage, 1, fp);
	fclose(fp);
	//释放内存空间  
	delete[]pColorTable;
	delete[]dp;
	return TRUE;
}

 第2部分高斯滤波

void MakeGauss(double sigma, double** gaus, int size)
{
	int center;
	double PI = 3.14159;
	double dSum;
	dSum = 0;
	center = size / 2;

	for (int i = 0; i < size; i++)
	{
		for (int j = 0; j < size; j++)
		{
			gaus[i][j] = (1 / (2 * PI * sigma * sigma)) * exp(-((i - center) * (i - center) + (j - center) * (j - center)) / (2 * sigma * sigma));  //二维高斯分布公式
			dSum += gaus[i][j];
		}
	}

	//归一化
	for (int i = 0; i < size; i++)
	{
		for (int j = 0; j < size; j++)
		{
			gaus[i][j] /= dSum;
			cout << gaus[i][j] << "  ";
		}
		cout << endl << endl;
	}
}

//******************高斯滤波*************************
//第一个参数pUnchImg是待滤波原始图像;
//第二个参数pUnchSmthdImg是滤波后输出图像;
//第三个参数gaus是一个指向含有N个double类型数组的指针;
//第四个参数size是滤波核的尺寸
//***********************************************************
///*******************************************二维高斯卷积*********************************************/
void GaussianSmooth(unsigned char* pUnchImg, int width, int height, double** gaus, int size, unsigned char* pUnchSmthdImg)
{
	
	double *gausArray=new double[size * size];
	for (int i = 0; i < size * size; i++)
	{
		gausArray[i] = 0;                             //赋初值,空间分配
	}
	int array = 0;
	for (int i = 0; i < size; i++)
	{
		for (int j = 0; j < size; j++)
		{
			gausArray[array] = gaus[i][j];           //二维数组到一维 方便计算
			array++;
		}
	}
	//滤波
	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			int k = 0;
			for (int l = -size / 2; l <= size / 2; l++)
			{
				for (int g = -size / 2; g <= size / 2; g++)
				{
					//以下处理针对滤波后图像边界处理,为超出边界的值赋值为边界值
					int row = i + l;
					int col = j + g;
					row = row < 0 ? 0 : row;
					row = row >= height ? height - 1 : row;
					col = col < 0 ? 0 : col;
					col = col >= width ? width - 1 : col;
					//卷积和
					*(pUnchSmthdImg + i * lineBytes + j) += gausArray[k] * *(pUnchImg + row * lineBytes + col);					
					k++;
				}
			}
		}
	}

 delete[]gausArray;
 gausArray = NULL;
}

第3部分sobel求梯度

//******************Sobel卷积因子计算X、Y方向梯度和梯度方向角********************
//第一个参数imageSourc原始灰度图像;
//第二个参数imageSobelX是X方向梯度图像;
//第三个参数imageSobelY是Y方向梯度图像;
//第四个参数pointDrection是梯度方向角数组指针(一维数据指针)
//*************************************************************
void SobelGradDirction(unsigned char* pUnchImg1, unsigned char* imageSobelX, unsigned char* imageSobelY, int width, int height, double*& pointDrection)
{
	pointDrection = new double[(height - 1) * (width - 1)];
	for (int i = 0; i < (height- 1) * (width - 1); i++)
	{
		pointDrection[i] = 0;
	}



	int* SobelX = new int[height * width];
	int* SobelY = new int[height * width];
	memset(SobelX, 0, height * width * sizeof(int));      //new和delete搭配      malloc和free搭配   分配内存与释放内存
	memset(SobelY, 0, height * width * sizeof(int));


	int k = 0;
	int step = width;
	for (int i = 1; i < (height - 1); i++)
	{
		for (int j = 1; j < (width - 1); j++)
		{
			//通过指针遍历图像上每一个像素 
			float gradY = pUnchImg1[(i - 1) * step + j + 1] + pUnchImg1[i * step + j + 1] * 2 + pUnchImg1[(i + 1) * step + j + 1] - pUnchImg1[(i - 1) * step + j - 1] - pUnchImg1[i * step + j - 1] * 2 - pUnchImg1[(i + 1) * step + j - 1];
			
			SobelY[i * width + j] = (unsigned char)abs(gradY);

			float gradX = pUnchImg1[(i + 1) * step + j - 1] + pUnchImg1[(i + 1) * step + j] * 2 + pUnchImg1[(i + 1) * step + j + 1] - pUnchImg1[(i - 1) * step + j - 1] - pUnchImg1[(i - 1) * step + j] * 2 - pUnchImg1[(i - 1) * step + j + 1];
			
			SobelX[i * width + j] = (unsigned char)abs(gradX);

			if (gradX == 0)
			{
				gradX = 0.00000000000000001;  //防止除数为0异常
			}
			pointDrection[k] = atan(gradY / gradX) * 57.3;//弧度转换为度
			pointDrection[k] += 90;
			k++;
		}
	}
	for (int i = 0; i < height * width; i++)
	{
		imageSobelX[i] = SobelX[i];
		imageSobelY[i] = SobelY[i];
	}
	
	delete[]SobelX;
	delete[]SobelY;
}

第4部分计算梯度幅值

void SobelAmplitude(unsigned char* imageGradX, unsigned char* imageGradY, int width, int wight, unsigned char* SobelAmpXY)
{
	
	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			SobelAmpXY[i*width+j] = (unsigned char)sqrt(imageGradX[i*width+j] * imageGradX[i*width+j]+ imageGradY[i*width+j] * imageGradY[i*width+j]);
		}
	}
}

第5部分非极大值抑制

//******************局部极大值抑制*************************
//第一个参数imageInput输入的Sobel梯度图像;
//第二个参数imageOutPut是输出的局部极大值抑制图像;
//第三个参数pointDrection是图像上每个点的梯度方向数组指针
//*************************************************************
void LocalMaxValue(unsigned char* imageInput,  double* pointDrection)
{
	
	int k = 0;
	for (int i = 1; i < height-1; i++)
	{
		for (int j = 1; j < width-1; j++)
		{
			int value00 = imageInput[(i - 1) * width + j - 1];
			int value01 = imageInput[(i - 1) * width + j]; 
			int value02 = imageInput[(i - 1) * width + j + 1]; 
			int value10 = imageInput[(i) * width + j - 1];
			int value11 = imageInput[(i) * width + j];                               //当前像素点
			int value12 = imageInput[(i) * width + j + 1];
			int value20 = imageInput[(i + 1) * width + j - 1];
			int value21 = imageInput[(i + 1) * width + j ];
			int value22 = imageInput[(i + 1) * width + j + 1];

			if (pointDrection[k] > 0 && pointDrection[k] <= 45)
			{
				if (value11 <= (value12 + (value02 - value12) * tan(pointDrection[i * width+ j])) || (value11 <= (value10 + (value20 - value10) * tan(pointDrection[i * width + j]))))
				{
					imageInput[i*width+j] = 0;
				}
			}
			if (pointDrection[k] > 45 && pointDrection[k] <= 90)

			{
				if (value11 <= (value01 + (value02 - value01) / tan(pointDrection[i *height + j])) || value11 <= (value21 + (value20 - value21) / tan(pointDrection[i * height + j])))
				{
					imageInput[i*width+j] = 0;

				}
			}
			if (pointDrection[k] > 90 && pointDrection[k] <= 135)
			{
				if (value11 <= (value01 + (value00 - value01) / tan(180 - pointDrection[i * height + j])) || value11 <= (value21 + (value22 - value21) / tan(180 - pointDrection[i * height + j])))
				{
					imageInput[i * width + j] = 0;
				}
			}
			if (pointDrection[k] > 135 && pointDrection[k] <= 180)
			{
				if (value11 <= (value10 + (value00 - value10) * tan(180 - pointDrection[i * width + j])) || value11 <= (value12 + (value22 - value11) * tan(180 - pointDrection[i * width + j])))
				{
					imageInput[i * width + j] = 0;
				}
			}
			k++;
		}
	}
}

第6部分双阈值迭代处理

void DoubleThresholdLink(unsigned char* imageInput, double lowThreshold, double highThreshold,int n)
{
	for (int i = 1; i < height - 1; i++)
	{
		for (int j = 1; j < width - 1; j++)
		{
			if (imageInput[i * width + j] > lowThreshold && imageInput[i * width + j] < 255)
			{
				if (imageInput[(i-1) * width + (j-1)] == 255 || imageInput[(i - 1) * width + (j)] == 255 || imageInput[(i - 1) * width + (j +1)] == 255 ||
					imageInput[(i) * width + (j - 1)] == 255 ||imageInput[(i) * width + (j)] || imageInput[(i) * width + (j +1)] == 255 ||
					imageInput[(i + 1) * width + (j - 1)] == 255 || imageInput[(i + 1) * width + (j)] == 255 || imageInput[(i + 1) * width + (j + 1)] == 255)
				{
					imageInput[i * width + j] = 255;
					if (n == 1)
					{
						break;
					}

					DoubleThresholdLink(imageInput, lowThreshold, highThreshold,n-1);     //递归调用
				}
				else
				{
					imageInput[i * width + j] = 0;
				}
			}
		}
	}
}

第7部分主函数

int main()
{
	//int width = 0;
	//int height=0;
	clock_t startTime, endTime;
    startTime = clock();//计时开始
	
	
	//读入指定BMP文件进内存
	char readPath[] = "D:\\机器视觉学习工程\\lianxi\\3.bmp";
	readBmp(readPath);
	unsigned char* imgBuf = new unsigned char[lineBytes * height];

	

	memset(imgBuf, 0, lineBytes * height);
	int size = 5; //定义卷积核大小
	double** gaus = new double* [size];  //卷积核数组
	for (int i = 0; i < size; i++)
	{
		gaus[i] = new double[size];  //动态生成矩阵
	}

	MakeGauss(0.8, gaus, 5);

	GaussianSmooth(pGrayData, width, height, gaus, 5, imgBuf);
	unsigned char* imageSobelX = new unsigned char[lineBytes * height];
	memset(imageSobelX, 0, lineBytes * height);
	unsigned char* imageSobelY = new unsigned char[lineBytes * height];
	memset(imageSobelY, 0, lineBytes * height);
	unsigned char* SobelAmpXY = new unsigned char[lineBytes * height];
	memset(SobelAmpXY, 0, lineBytes * height);
	unsigned char* imageOutput = new unsigned char[lineBytes * height];
	memset(imageOutput, 0, lineBytes * height);

	double*pointDrection = new double[(height - 1) * (width - 1)];
	for (int i = 0; i < (height - 1) * (width - 1); i++)
	{
		pointDrection[i] = 0;
	}
	SobelGradDirction(imgBuf, imageSobelX, imageSobelY, width, height, pointDrection);       //梯度

	SobelAmplitude(imageSobelX, imageSobelY, width, height, SobelAmpXY);                  //梯度幅值

	LocalMaxValue(SobelAmpXY,  pointDrection);

	//高低阈值
	DoubleThreshold(SobelAmpXY, 18, 31);

	DoubleThresholdLink(SobelAmpXY, 18, 31,5);

	//输出图像的信息
	char writePath[] = "D:\\机器视觉学习工程\\lianxi\\3tidu3.bmp";
	writeBmp(writePath, SobelAmpXY);
	cout << "图像成功写入内存 " << endl;
	ShellExecuteA(nullptr, "open", writePath, "", "", SW_SHOW);     //打开写入的位图结果
	endTime = clock();//计时结束
    cout << "The run time is: " << (float)(endTime - startTime) / CLOCKS_PER_SEC << "s" << endl;
	
	delete[]imgBuf;
	//delete[]imageSobelX;
	//delete[]imageSobelY;
	//delete[]pBmpBuf;
	if (biBitCount == 8)
		delete[]pColorTable;

	system("pause");
	return 0;
}

二、实现结果

测试原图如下:

 Canny边缘检测的结果如下:

文章来源原文:https://blog.csdn.net/dcrmg/article/details/52344902

 最近整理了不少传统图像的算法及优化都是底层C++实现的。大部分都是照着opencv改的,看懂了改一改就行了,以后有时间更新吧。完事。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值