基本概念
在图像处理中,阈值分割是一种简单而有效的图像分割方法,它根据像素值将图像分割成前景和背景。自适应阈值分割是阈值分割的一种高级形式,它考虑了图像局部区域的亮度变化,从而能够更准确地分割图像。OpenCV是一个强大的计算机视觉库,它提供了多种图像处理和计算机视觉功能,包括自适应阈值分割。自适应阈值分割是图像处理中的一个重要概念,它可以根据局部区域的亮度来调整阈值,从而在光照不均匀或背景复杂的图像中获得更好的分割效果。这种方法特别适用于光照条件变化较大的场景,或者是背景与前景对比度不均匀的情况。
自适应阈值分割在 OpenCV 中可以通过 adaptiveThreshold 函数实现。这个函数允许你为图像的不同部分应用不同的阈值,这对于处理光照不均或者背景复杂变化的场景特别有用。
OpenCV中的自适应阈值分割
在OpenCV中,自适应阈值分割可以通过cv::adaptiveThreshold
函数实现。这个函数允许我们指定一个局部区域的大小,以及一个用于计算阈值的算法(通常是平均值或加权平均值)。
void cv::adaptiveThreshold(InputArray src, OutputArray dst,
double maxValue, int adaptiveMethod,
int thresholdType, int blockSize, double C)
src: 输入的8位单通道图像。
dst: 输出图像,与输入图像有相同的尺寸和类型。
maxValue: 当像素值超过(或根据阈值类型小于)计算得到的阈值时赋予的像素值。
adaptiveMethod: 使用的自适应阈值算法。cv::ADAPTIVE_THRESH_MEAN_C表示使用局部区域的平均值减去C来计算阈值;cv::ADAPTIVE_THRESH_GAUSSIAN_C表示使用局部区域的高斯加权和减去C来计算阈值。
thresholdType: 阈值类型,必须是cv::THRESH_BINARY或cv::THRESH_BINARY_INV。
blockSize: 局部区域的大小,必须是正奇数。
C: 从均值或加权均值中减去的常数。
参数说明
src: 输入图像,通常是灰度图。
dst: 输出图像,必须与输入图像有相同的尺寸。
maxValue: 当像素值超过了阈值或低于阈值(取决于类型),将被赋予的最大值。
adaptiveMethod: 指定如何计算阈值。可以是:
ADAPTIVE_THRESH_MEAN_C: 阈值是邻域均值减去常数C。
ADAPTIVE_THRESH_GAUSSIAN_C: 阈值是邻域的加权和,权重为一个高斯窗口。
thresholdType: 指定应用阈值的方式。可以是:
THRESH_BINARY: 如果像素值大于阈值,则设为最大值,否则设为0。
THRESH_BINARY_INV: 如果像素值大于阈值,则设为0,否则设为最大值。
blockSize: 决定用于计算阈值的邻域大小,即一个正方形的边长。
C: 从邻域均值或加权和中减去的常数值。
C++ 实现自适应阈值分割的基本步骤:
1. 导入必要的头文件: 首先需要包含 OpenCV 的核心模块头文件。
#include <opencv2/opencv.hpp>
using namespace cv;
2. 读取图像: 使用 imread 函数从磁盘读取图像,并将其转换为灰度图,因为自适应阈值通常应用于灰度图像。
Mat src = imread("path/to/image.jpg", IMREAD_GRAYSCALE);
if (src.empty()) {
std::cout << "Error: Image not found or unable to read the image." << std::endl;
return -1;
}
3. 设置自适应阈值参数: 在调用 adaptiveThreshold 之前,你需要决定一些参数:
•方法(ADAPTIVE_THRESH_MEAN_C 或 ADAPTIVE_THRESH_GAUSSIAN_C):前者使用邻域内的平均值作为阈值的基础,后者则使用加权平均值。
•阈值类型(如 THRESH_BINARY 或 THRESH_BINARY_INV)。
•邻域大小(blockSize):用于计算阈值的像素邻域的大小。
•常数 C:从平均值(或加权平均值)中减去的常数值。
int blockSize = 11; // 邻域大小
int C = 2; // 常数
4. 应用自适应阈值: 使用 adaptiveThreshold 函数来执行自适应阈值操作。
Mat dst;
adaptiveThreshold(src, dst, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, blockSize, C);
5. 显示结果: 可以通过 imshow 显示原始图像和处理后的图像,并等待用户按键退出。
namedWindow("Original Image", WINDOW_NORMAL);
imshow("Original Image", src);
namedWindow("Adaptive Threshold Result", WINDOW_NORMAL);
imshow("Adaptive Threshold Result", dst);
waitKey(0); // Wait for a keystroke in the window
6. 保存结果(可选): 如果需要,可以将结果保存到磁盘。
imwrite("path/to/output_image.jpg", dst);
以上就是使用 OpenCV 和 C++ 进行自适应阈值分割的基本流程。
需要注意的是,在实际应用中可能需要根据具体的图像内容调整参数以达到最佳效果。
此外,为了更好地理解如何选择合适的参数。
示例代码1
#include "pch.h"
#include<iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
Mat src = imread("809.jpeg", IMREAD_GRAYSCALE);
if (src.empty())
{
std::cout << "Error: Image not found or unable to read the image." << std::endl;
return -1;
}
int blockSize = 11; // 邻域大小
int C = 2; // 常数
Mat dst;
adaptiveThreshold(src, dst, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, blockSize, C);
namedWindow("Original Image", WINDOW_NORMAL);
imshow("Original Image", src);
namedWindow("Adaptive Threshold Result", WINDOW_NORMAL);
imshow("Adaptive Threshold Result", dst);
waitKey(0); // Wait for a keystroke in the window
imwrite("00.jpg", dst);
return 0;
}
运行结果1
示例代码2
以下是一个简单的示例,展示如何使用OpenCV进行自适应阈值分割:
#include "pch.h"
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
// 加载图像
cv::Mat img = cv::imread("789.jpeg", cv::IMREAD_GRAYSCALE);
if (img.empty())
{
std::cout << "Error: Image cannot be loaded!" << std::endl;
return -1;
}
// 定义输出图像
cv::Mat thresh;
// 应用自适应阈值
int blockSize = 11; // 可以调整这个参数来改变邻域大小
int C = 2; // 常数C
cv::adaptiveThreshold(img, thresh, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY, blockSize, C);
// 显示结果
namedWindow("Original Image", WINDOW_NORMAL);
cv::imshow("Original Image", img);
namedWindow("Adaptive Threshold", WINDOW_NORMAL);
cv::imshow("Adaptive Threshold", thresh);
// 等待按键后关闭窗口
cv::waitKey(0);
return 0;
}
注意事项
blockSize的选择很重要,如果太大可能会导致细节丢失,太小则可能导致噪声增加。
常数C用于调整阈值,通常需要通过实验来找到合适的值。
对于非常复杂的场景或者需要高度精确的阈值分割,可能需要进一步的预处理或者结合其他图像处理技术。
运行结果2
示例代码3
下面是一个使用OpenCV C++ API进行自适应阈值分割的示例代码:
#include "pch.h"
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main() {
// 加载图像
Mat src = imread("896.png", IMREAD_GRAYSCALE);
if (src.empty())
{
cout << "Could not open or find the image!\n";
return -1;
}
// 创建输出图像
Mat dst;
// 应用自适应阈值分割
adaptiveThreshold(src, dst, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 11, 2);
// 显示结果
namedWindow("Original Image", WINDOW_NORMAL);
imshow("Original Image", src);
namedWindow("Adaptive Thresholding", WINDOW_NORMAL);
imshow("Adaptive Thresholding", dst);
waitKey(0);
return 0;
}
在这个示例中,我们使用ADAPTIVE_THRESH_MEAN_C作为自适应方法,THRESH_BINARY作为阈值类型,
11作为局部区域的大小(blockSize),
2作为从均值中减去的常数(C)。
这些参数可能需要根据你的具体图像进行调整以达到最佳效
果。
注意事项
局部区域的大小(blockSize)对结果有很大影响。较小的blockSize可能会导致图像过度分割,而较大的blockSize可能会导致图像中的细节丢失。
减去的常数(C)用于调整阈值的敏感度。调整这个值可以帮助改善分割效果。
自适应阈值分割特别适用于光照不均匀的图像,因为它可以根据图像的不同区域自动调整阈值。
运行结果3
实验代码4
#include "pch.h"
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2\imgproc\types_c.h>
//#pragma comment(lib, "opencv_world450d.lib")
using namespace cv;
int main()
{
//------------【1】读取源图像并检查图像是否读取成功------------
Mat srcImage = imread("08.jpeg");
if (!srcImage.data)
{
puts("读取图片错误,请重新输入正确路径!");
system("pause");
return -1;
}
namedWindow("【源图像】", WINDOW_NORMAL);
imshow("【源图像】", srcImage);
//------------【2】灰度转换------------
Mat srcGray;
cvtColor(srcImage, srcGray, CV_RGB2GRAY);
namedWindow("【灰度图】", WINDOW_NORMAL);
imshow("【灰度图】", srcGray);
//------------【3】初始化相关变量---------------
Mat dstImage; //初始化自适应阈值参数
const int maxVal = 255;
int blockSize = 3; //取值3、5、7....等
int constValue = 10;
int adaptiveMethod = 0;
int thresholdType = 1;
//---------------【4】图像自适应阈值操作-------------------------
adaptiveThreshold(srcGray, dstImage, maxVal, adaptiveMethod, thresholdType, blockSize, constValue);
namedWindow("【自适应阈值】", WINDOW_NORMAL);
imshow("【自适应阈值】", dstImage);
waitKey(0);
return 0;
}
运行结果4
失败