滤波与卷积(一)


预备知识

本文中有两个重要的概念会经常出现,第一个是滤波器(也称核)以及在OpenCV中具体的运用,第二个是OpenCV在图像的边缘以及图像边界之外的区域如何调用滤波器或其他的方法。


边界外推和边界处理

OpenCV中的滤波操作(如cv::blur(),cv::erode(),cv::dilate()等)得到的输出图像与源图像的形状是一样的。

自定义边框

copyMakeBorder()就是为图像创建边框的

CV_EXPORTS_W void copyMakeBorder(
InputArray src,     //Source image.
OutputArray dst,    //Result image.
int top,            //the top pixels
int bottom,         //the bottom pixels
int left,           //the left pixels
int right,			//right side padding
int borderType,     //Border type,像素的填充方法
const Scalar& value = Scalar() 
);
enum BorderTypes {
	
    BORDER_CONSTANT    = 0, //!< `iiiiii|abcdefgh|iiiiiii`  with some specified `i`,复制指定的常量扩展边界
    BORDER_REPLICATE   = 1, //!< `aaaaaa|abcdefgh|hhhhhhh`
    BORDER_REFLECT     = 2, //!< `fedcba|abcdefgh|hgfedcb`
    BORDER_WRAP        = 3, //!< `cdefgh|abcdefgh|abcdefg`,复制对边的像素扩展边界
    BORDER_REFLECT_101 = 4, //!< `gfedcb|abcdefgh|gfedcba`
    BORDER_TRANSPARENT = 5, //!< `uvwxyz|abcdefgh|ijklmno`

    BORDER_REFLECT101  = BORDER_REFLECT_101, //!< same as BORDER_REFLECT_101
    BORDER_DEFAULT     = BORDER_REFLECT_101, //!< same as BORDER_REFLECT_101
    BORDER_ISOLATED    = 16 //!< do not look outside of ROI
};

copyMakeBorder Demo

在这里插入图片描述
在这里插入图片描述
代码:

#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
using namespace cv;
//![variables]
// Declare the variables
Mat src, dst;
int top, bottom, left, right;
int borderType = BORDER_CONSTANT;
const char* window_name = "copyMakeBorder Demo";
RNG rng(12345);
//![variables]
int main(int argc, char** argv)
{
	//![load]
	const char* imageName = argc >= 2 ? argv[1] : "lena.jpg";
	// Loads an image
	src = imread(samples::findFile(imageName), IMREAD_COLOR); // Load an image
	// Check if image is loaded fine
	if (src.empty()) {
		printf(" Error opening image\n");
		printf(" Program Arguments: [image_name -- default lena.jpg] \n");
		return -1;
	}
	//![load]
	// Brief how-to for this program
	printf("\n \t copyMakeBorder Demo: \n");
	printf("\t -------------------- \n");
	printf(" ** Press 'c' to set the border to a random constant value \n");
	printf(" ** Press 'r' to set the border to be replicated \n");
	printf(" ** Press 'ESC' to exit the program \n");
	//![create_window]
	namedWindow(window_name, WINDOW_AUTOSIZE);
	//![create_window]
	//![init_arguments]
	// Initialize arguments for the filter
	top = (int)(0.05*src.rows); bottom = top;
	left = (int)(0.05*src.cols); right = left;
	//![init_arguments]
	for (;;)
	{
		//![update_value]
		Scalar value(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		//![update_value]
		//![copymakeborder]
		copyMakeBorder(src, dst, top, bottom, left, right, borderType, value);
		//![copymakeborder]
		//![display]
		imshow(window_name, dst);
		//![display]
		//![check_keypress]
		char c = (char)waitKey(500);
		if (c == 27)
			break;
		else if (c == 'c')
			borderType = BORDER_CONSTANT;
		else if (c == 'r')
			borderType = BORDER_REPLICATE;
		//![check_keypress]
	}
	return 0;
}

自定义外推

CV_EXPORTS_W int borderInterpolate(
int p,    //0-based coordinate of the extrapolated pixel along one of the axes
int len,  // Length of the array along the corresponding axis
int borderType  //Border type
);

阈值化操作

cv::threshold()

CV_EXPORTS_W double threshold( 
InputArray src, 
OutputArray dst,
double thresh, 
double maxval, 
int type    // thresholding type (see #ThresholdTypes)
);

enum ThresholdTypes {
    THRESH_BINARY     = 0, 
    THRESH_BINARY_INV = 1, 
    THRESH_TRUNC      = 2, 
    THRESH_TOZERO     = 3, 
    THRESH_TOZERO_INV = 4, 
    THRESH_MASK       = 7,
    THRESH_OTSU       = 8, 
    THRESH_TRIANGLE   = 16 
};

在这里插入图片描述
案例结果
在这里插入图片描述
在这里插入图片描述
案例代码

#include<opencv2/opencv.hpp>
#include<iostream>
#include<string>
using namespace std;

static void test() {
	cv::Mat image, binary, binary_inv,trunc,tozero, tozero_inv;
	const std::string path = "Image.jpg";
	image = cv::imread(path);
	if (!image.data) {
		cout << "load image is failed..." << endl;
		return;
	}

	string title[] = { "Original","BINARY","BINARY_INV","TRUNC","TOZERO","TOZERO_INV" };
	
	cv::threshold(image, binary, 127, 255, cv::THRESH_BINARY);
	cv::threshold(image, binary_inv, 127, 255, cv::THRESH_BINARY_INV);
	cv::threshold(image, trunc, 127, 255, cv::THRESH_TRUNC);
	cv::threshold(image, tozero, 127, 255, cv::THRESH_TOZERO);
	cv::threshold(image, tozero_inv, 127, 255, cv::THRESH_TOZERO_INV);
	cv::namedWindow(title[0], cv::WINDOW_AUTOSIZE);
	cv::imshow(title[0], image);
	cv::namedWindow(title[1], cv::WINDOW_AUTOSIZE);
	cv::imshow(title[1], binary);
	cv::namedWindow(title[2], cv::WINDOW_AUTOSIZE);
	cv::imshow(title[2], binary_inv);
	cv::namedWindow(title[3], cv::WINDOW_AUTOSIZE);
	cv::imshow(title[3], trunc);
	cv::namedWindow(title[4], cv::WINDOW_AUTOSIZE);
	cv::imshow(title[4], tozero);
	cv::namedWindow(title[5], cv::WINDOW_AUTOSIZE);
	cv::imshow(title[5], tozero_inv);
	cv::waitKey(0);

}

int main()
{
	test();
	system("pause");
	return 0;
}

Otsu算法

函数cv::threshold()也可以自动决定最优的阈值,你只需对参数thresh传递cv::THRESH_OTSU即可。
简言之,Otsu算法就是遍历所有可能的阈值,然后对每个阈值结果的两类像素计算方差。

算法结果:
在这里插入图片描述
代码:

#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <string>
using namespace std;
using namespace cv;
#define Gamma 3
//OTSU 函数实现
static int OTSU(const Mat& srcImage) {
	int nCols = srcImage.cols;
	int nRows = srcImage.rows;
	int threshold = 0;
	//init the parameters
	int nSumPix[256];
	float nProDis[256];
	for (int i = 0; i < 256; i++)
	{
		nSumPix[i] = 0;
		nProDis[i] = 0;
	}

	//统计灰度集中每个像素在整幅图像中的个数
	for (int i = 0; i < nRows; i++)
	{
		for (int j = 0; j < nCols; j++)
		{
			nSumPix[(int)srcImage.at<uchar>(i, j)]++;
		}
	}

	//计算每个灰度级占图像中的概率分布
	for (int i = 0; i < 256; i++)
	{
		nProDis[i] = (float)nSumPix[i] / (nCols*nRows);
	}

	//遍历灰度级【0,255】,计算出最大类间方差下的阈值
	float w0, w1, u0_temp, u1_temp, u0, u1, delta_temp;
	double delta_max = 0.0;
	for (int i = 0; i < 256; i++)
	{
		//初始化相关参数
		w0 = w1 = u0 = u1 = u0_temp = u1_temp = delta_temp = 0;
		for (int j = 0; j < 256; j++)
		{
			//背景部分
			if (j <= i)
			{
				w0 += nProDis[j];
				u0_temp += j * nProDis[j];
			}
			//前景部分
			else
			{
				w1 += nProDis[j];
				u1_temp += j * nProDis[j];
			}
		}
		//计算两个分类的平均灰度
		u0 = u0_temp / w0;
		u1 = u1_temp / w1;
		//依次找到最大类间方差下的阈值
		delta_temp = (float)(w0*w1*pow((u0 - u1), 2)); //前景与背景之间的方差(类间方差)
		if (delta_temp > delta_max)
		{
			delta_max = delta_temp;
			threshold = i;
		}
	}
	return threshold;
}

static void test() {
	namedWindow("srcGray", WINDOW_AUTOSIZE);
	namedWindow("otsuResultImage", WINDOW_AUTOSIZE);
	namedWindow("dst", WINDOW_AUTOSIZE);
	//图像读取及判断
	Mat srcImage;
	srcImage = imread("renwu1.jpg");
	imshow("dst", srcImage);
	if (!srcImage.data)
	{
		return ;
	}
	Mat srcGray;
	cvtColor(srcImage, srcGray, COLOR_RGB2GRAY);
	imshow("srcGray", srcGray);

	//调用otsu算法得到图像
	int otsuThreshold = OTSU(srcGray);
	cout << otsuThreshold << endl;
	//定义输出结果图像
	Mat otsuResultImage = Mat::zeros(srcGray.rows, srcGray.cols, CV_8UC1);

	//利用得到的阈值进行二值化操作
	for (int i = 0; i < srcGray.rows; i++)
	{
		for (int j = 0; j < srcGray.cols; j++)
		{
			//高像素阈值判断
			if (srcGray.at<uchar>(i, j) > otsuThreshold)
			{
				otsuResultImage.at<uchar>(i, j) = 255;
			}
			else
			{
				otsuResultImage.at<uchar>(i, j) = 0;
			}
		}
	}
	imshow("otsuResultImage", otsuResultImage);
	waitKey(0);
}

int  main()
{
	test();
	system("pause");
	return 0;
}

自适应阈值

有一种与之前不同的阈值化方法,这种方法中阈值在整个过程中自动产生变化。在OpenCV中,函数cv::adaptiveThreshold()实现了这种方法:

CV_EXPORTS_W void adaptiveThreshold(
InputArray src, 
OutputArray dst,
double maxValue, 
int adaptiveMethod,  //mean or Gaussian
int thresholdType, 
int blockSize, 
double C 
);

案例结果:

在这里插入图片描述
在这里插入图片描述
代码:

#include<opencv2/opencv.hpp>
#include<iostream>
#include<string>
using namespace std;
static int test() {
	cv::Mat image,gray_image, binary, media_blur, adaptive_mean_image,adaptive_gaussian_image;
	const std::string path = "Image.jpg";
	image = cv::imread(path);
	if (!image.data) {
		cout << "load image is failed..." << endl;
		return -1;
	}
	if (image.channels() > 1)
		cv::cvtColor(image, gray_image, cv::COLOR_RGB2GRAY);
	string title[] = { "Original","BINARY","MEDIANBLUR","ADAPTIVE_THRESH_MEAN_C","ADAPTIVE_THRESH_GAUSSIAN_C" };
	cv::medianBlur(gray_image, media_blur, 5);
	cv::threshold(gray_image, binary, 127, 255, cv::THRESH_BINARY);
	cv::adaptiveThreshold(media_blur, adaptive_mean_image, 255, cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY, 11, 2);
	cv::adaptiveThreshold(media_blur, adaptive_gaussian_image, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY, 11, 2);
	cv::namedWindow(title[0], cv::WINDOW_AUTOSIZE);
	cv::imshow(title[0], image);
	cv::namedWindow(title[1], cv::WINDOW_AUTOSIZE);
	cv::imshow(title[1], binary);
	cv::namedWindow(title[2], cv::WINDOW_AUTOSIZE);
	cv::imshow(title[2], media_blur);
	cv::namedWindow(title[3], cv::WINDOW_AUTOSIZE);
	cv::imshow(title[3], adaptive_mean_image);
	cv::namedWindow(title[4], cv::WINDOW_AUTOSIZE);
	cv::imshow(title[4], adaptive_gaussian_image);
	cv::waitKey(0);
}

int main()
{
	test();
	system("pause");
	return 0;
}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值