本文中有两个重要的概念会经常出现,第一个是滤波器(也称核)以及在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_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
#include
#include
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;
}
函数cv::threshold()也可以自动决定最优的阈值,你只需对参数thresh传递cv::THRESH_OTSU即可。
简言之,Otsu算法就是遍历所有可能的阈值,然后对每个阈值结果的两类像素计算方差。
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include
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
#include
#include
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;
}