1、背景
现有如下测试图片,希望消除图片背景中的模糊字迹。
2、实现
白色是由红绿蓝混合而成,其像素值是(255,255,255)。黑色即什么颜色也没有,其像素值(0,0,0)。实际想要显示的内容为黑色,与模糊字迹区分较明显。
方法1:
增加图片对比度,设置一个阈值g_threshold略低于模糊字迹的像素值,使超过g_threshold的模糊字迹变得更白以至于消失,使低于g_threshold的内容减少像素值变得更黑。
方法2:
对整张图片增加亮度,使模糊字迹率先消失。
#include <opencv2/opencv.hpp>
#include <iostream>
#include <string>
cv::Mat g_image, g_image1;
int g_contrast = 10; // 对比度
int g_threshold = 215; // 阈值
int g_brightness = 0; // 亮度
std::string g_name = "Trackbar";
void onTrackbar(int pos, void *)
{
for (size_t row = 0; row < g_image.rows; ++row) // 行号,相当于y
{
for (size_t col = 0; col < g_image.cols; ++col) // 列号,相当于x
{
for (size_t rgb = 0; rgb < 3; ++rgb) // 红绿蓝三通道
{
size_t n;
if (g_image.at<cv::Vec3b>(row, col)[rgb] > g_threshold) // 高于g_threshold增加像素值
n = g_image.at<cv::Vec3b>(row, col)[rgb] * (1 + g_contrast * 0.01) + g_brightness;
else // 低于g_threshold减少像素值
n = g_image.at<cv::Vec3b>(row, col)[rgb] * (1 - g_contrast * 0.01) + g_brightness;
n = cv::saturate_cast<uchar>(n); //矫正像素值,确保在[0,255]之间
g_image1.at<cv::Vec3b>(row, col)[rgb] = n;
}
}
}
cv::imshow(g_name, g_image1);
}
int main()
{
g_image = cv::imread("1.jpg");
if (g_image.empty())
{
std::cout << "can not open image!" << std::endl;
return -1;
}
g_image.copyTo(g_image1);
cv::namedWindow(g_name, cv::WINDOW_AUTOSIZE);
// 进度条
cv::createTrackbar("contrast", g_name, &g_contrast, 100, onTrackbar);
cv::createTrackbar("threshold", g_name, &g_threshold, 255, onTrackbar);
cv::createTrackbar("brightness", g_name, &g_brightness, 255, onTrackbar);
while (true)
{
onTrackbar(0, 0);
auto c = cv::waitKey(10);
if (c == 's') // 按s键保存图片
cv::imwrite("2.jpg", g_image1);
else if (c >= 0 && c != 's') // 按s之外的其它键退出
break;
}
cv::destroyAllWindows();
return 0;
}
3、拓展
读取文件夹中所有图片并消除其背景中的模糊字迹。
#include <opencv2/opencv.hpp>
#include <iostream>
#include <io.h>
#include <string>
#include <set>
using namespace std;
// 查找路径path下,文件类型为type的所有文件
void find_files(const string &path, const string &type, set<string> &files)
{
_finddata_t data;
auto handle = _findfirst((path + "/*.*").c_str(), &data); // 读取第1文件或文件夹
if (handle == -1) // 判断是否可以读取文件
{
cout << "can not read file!";
return;
}
do
{
string s = data.name; // 文件名
if (data.attrib & _A_SUBDIR) // 目录
{
// if (s != "." && s != "..") // 排除文件夹.和文件夹..
// cout << "dir: " << s << endl;
}
else // 文件
{
string s1 = "." + type;
if (s.rfind(s1) == s.size() - s1.size()) // 判断后缀是否为.type
{
// files.insert(path + "/" + s);
files.insert(s);
// cout << "file: " << s << endl;
}
}
} while (_findnext(handle, &data) == 0); // 读取下一个文件或文件夹
_findclose(handle); // 关闭搜索句柄
}
void image_process(const string &img_path)
{
int contrast = 10; // 对比度
int threshold = 215; // 阈值
int brightness = 0; // 亮度
cv::Mat img = cv::imread(img_path);
for (size_t row = 0; row < img.rows; ++row) // 行号,相当于y
{
for (size_t col = 0; col < img.cols; ++col) // 列号,相当于x
{
for (size_t rgb = 0; rgb < 3; ++rgb) // 红绿蓝三通道
{
size_t n;
if (img.at<cv::Vec3b>(row, col)[rgb] > threshold) // 高于threshold增加像素值
n = img.at<cv::Vec3b>(row, col)[rgb] * (1 + contrast * 0.01) + brightness;
else // 低于threshold减少像素值
n = img.at<cv::Vec3b>(row, col)[rgb] * (1 - contrast * 0.01) + brightness;
n = cv::saturate_cast<uchar>(n); // 矫正像素值,确保在[0,255]之间
img.at<cv::Vec3b>(row, col)[rgb] = n;
}
}
}
string path = img_path, s = "_1";
path.insert(path.end() - 4, s.begin(), s.end());
cv::imwrite(path, img); // 保存图片
}
int main()
{
string path = "C:/Users/Administrator/Desktop/program/1"; // 路径名
string type = "PNG"; // 文件类型
set<string> files; // 存放找到的文件
find_files(path, type, files);
cout << endl;
if (files.size() == 0)
cout << "目录下不存在该类型文件!" << endl;
for (auto file : files) // 浏览找到的文件
{
image_process(path + "/" + file);
cout << file << " 处理完成。" << endl;
}
return 0;
}
处理前的文件夹:
运行结果:
处理后的文件夹: