// 1. 获取原图
Mat src = imread("E:\\img\\3.jpg");
if (src.empty())
{
cout << "No Image!" << endl;
system("pause");
return -1;
}
imshow("原图", src);
// 2. 灰度化
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
imshow("灰度图", gray);
// 3. 图像二值化,筛选出白色区域部分
Mat binary;
// 在这里使用图像的平均值作为阈值T
Scalar T = mean(gray);
threshold(gray, binary, 220, 255, THRESH_BINARY);
imshow("二值化图", binary);
最关键在这一步,这部分的掩膜图像没生成好,就会导致生成的效果较差。因此,需要根据实际图片遍历图像元素并进行掩码。
// 4.提取图片下方的水印,制作掩模图像
Mat mask = Mat::zeros(src.size(), CV_8U);
int start_x = 0.8 * src.rows;
int end_x = src.rows;
int start_y = 0.8 * src.cols -40;
int end_y = src.cols;
//遍历图像像素,提取出水印部分像素,制作掩模图像
for (int i = start_x; i < end_x; i++)
{
uchar* data = binary.ptr<uchar>(i);
for (int j = start_y; j < end_y; j++)
{
//cout << binary.ptr<uchar>(i)[j]<<" ";
if (data[j] == 255)
{
mask.at<uchar>(i, j) = 255;
}
}
cout << endl;
}
imshow("掩膜图", mask);
直接使用提取出的二值掩模进行图像修复得到的结果,可以看出效果不是很好。原因是,提取出来的掩模未能覆盖完全待修复像素。故我们需要将掩模图像进行膨胀操作,扩大掩模范围。
// 5.将掩模进行膨胀,使其能够覆盖图像更大区域
Mat kernel = getStructuringElement(MORPH_RECT, Size(6, 6));
dilate(mask, mask, kernel);
imshow("膨胀图", mask);
getStructuringElement(int shape, Size ksize, Point anchor)
需要输入两个参数:
一个是原始图像,
一个被称为结构化元素或核,它是用来决定操作的性质的
getStructuringElement源码介绍看附录
这里使用opencv自带的图像修复函数,图像修复技术原理是利用已被破坏的边缘,即边缘的颜色和结构,繁殖和混合到损坏的图像中,已达到图像修补的目的。
// 6.使用inpaint进行图像修复
Mat result;
inpaint(src, mask, result, 8, INPAINT_NS);
imshow("image show", result);
void inpaint(
InputArray src,
InputArray inpaintMask,
OutputArray dst,
double inpaintRadius,
int flags );
第一个参数src,输入的单通道或三通道图像;
第二个参数inpaintMask,图像的掩码,单通道图像,大小跟原图像一致,inpaintMask图像上除了需要修复的部分之外其他部分的像素值全部为0;
第三个参数dst,输出的经过修复的图像;
第四个参数inpaintRadius,修复算法取的邻域半径,用于计算当前像素点的差值;
第五个参数flags,修复算法,有两种:INPAINT_NS 和I NPAINT_TELEA;
// 1. 获取原图
Mat dst = imread("E:\\img\\4.jpg");
if (dst.empty())
{
cout << "No Image!" << endl;
system("pause");
return -1;
}
void on_mouse(int event, int x, int y, int flags, void* userdata) {
Mat dst = (*(Mat*)userdata).clone();
Mat src(*(Mat*)userdata);
char temp[16];
//判断左键按下,记录起始点坐标
if (event == EVENT_LBUTTONDOWN) {
point1.x = x;
point1.y = y;
}
else if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON)) {
//计算需画的矩形
//Rect rect(startP.x, startP.y, x - startP.x, y - startP.y);
//在图像上画出矩形
point2.x = x;
point2.y = y;
sprintf_s(temp, "(%d,%d)", x, y);//坐标
putText(dst, temp, point2, FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 0, 255));//实时显示鼠标移动的坐标
rectangle(dst, point1, Point(x, y), Scalar(250, 0, 0), 2, 0);
imshow("image show", dst);
}
//左键松开,在原图像画出矩形
else if (event == EVENT_LBUTTONUP) {
point2.x = x;
point2.y = y;
//Rect rect(point1.x, point1.y, x - point1.x, y - point1.y);
rectangle(dst, point1, point2, Scalar(0, 0, 250), 2, 0);
imshow("image show", dst);
}
}
(1)按住左键进行移动,此时会显示当前坐标信息以及当前选中的区域(用蓝色标出)。
(2)释放左键,表示已经选好要去水印的区域,此时用红色矩形框标出。
选好区域后,在区域内进行像素替换,使用周围像素进行替换,偏移方向和距离可根据实际情况自定义。
//对选中区域进行像素替换,偏移3个像素,根据实际情况调节
for (int i = min(point1.y, point2.y); i < max(point2.y, point1.y); i++)
{
for (int j = min(point1.x, point2.x); j < max(point2.x, point1.x); j++)
{
src.at<Vec3b>(i, j)[0] = src.at<Vec3b>(i, j - 3)[0];
src.at<Vec3b>(i, j)[1] = src.at<Vec3b>(i, j - 3)[1];
src.at<Vec3b>(i, j)[2] = src.at<Vec3b>(i, j - 3)[2];
}
}
imshow("imagg", src);
对替换好像素的区域进行平滑处理,去除噪声
//对选中区域周围进行平滑处理
Mat imageroi = src(Range(point1.y - 3, point2.y + 3), Range(point1.x - 3, point2.x + 3));
GaussianBlur(imageroi, imageroi, Size(15, 15), 0, 0);
使用copyTo将平滑后的区域粘贴到原图像上。
Mat img1 = (src).clone();
Mat imgdst = img1(Rect(point1.x - 3,point1.y - 3, point2.x - point1.x, point2.y - point1.y));
imageroi.copyTo(imgdst);
对整体图像双边滤波(对人像有美颜效果)。
//对整体图像双边滤波(对人像有美颜效果)
bilateralFilter(img1, dst, 15, 30, 5);
getStructuringElement源码介绍
cv::Mat cv::getStructuringElement(int shape, Size ksize, Point anchor)
{
int i, j;
int r = 0, c = 0;
double inv_r2 = 0;
CV_Assert( shape == MORPH_RECT || shape == MORPH_CROSS || shape == MORPH_ELLIPSE ); //目前支持三种形状的单元创建: 矩形, 十字形, 椭圆形;
anchor = normalizeAnchor(anchor, ksize); //当默认为-1,-1时, 计算anchor;
if( ksize == Size(1,1) ) //当给定大小为1,1时,表明是一个点, 可以用矩形来表示;
shape = MORPH_RECT;
if( shape == MORPH_ELLIPSE ) //椭圆;
{
r = ksize.height/2;
c = ksize.width/2;
inv_r2 = r ? 1./((double)r*r) : 0;
}
Mat elem(ksize, CV_8U);
for( i = 0; i < ksize.height; i++ ) //对每一行,计算0,1的范围;
{
uchar* ptr = elem.ptr(i);
int j1 = 0, j2 = 0;
if( shape == MORPH_RECT || (shape == MORPH_CROSS && i == anchor.y) ) //矩形,或十字y锚点时 j2为ksize.width;
j2 = ksize.width;
else if( shape == MORPH_CROSS )
j1 = anchor.x, j2 = j1 + 1;
else //椭圆;
{
int dy = i - r;
if( std::abs(dy) <= r )
{
int dx = saturate_cast<int>(c*std::sqrt((r*r - dy*dy)*inv_r2)); //计算得到x的偏移;
j1 = std::max( c - dx, 0 );
j2 = std::min( c + dx + 1, ksize.width );
}
}
for( j = 0; j < j1; j++ ) //从这三个for可以看出, (0,j1)之间为 0, (j1, j2)之间为1, (j2, ksize.width)之间为0;
ptr[j] = 0;
for( ; j < j2; j++ )
ptr[j] = 1;
for( ; j < ksize.width; j++ )
ptr[j] = 0;
}
return elem;
}
文章浏览阅读3.2k次,点赞6次,收藏19次。在上一篇文章中,我们详细介绍了Intel IOMMU的初始化流程,并耗费大量笔墨讲述了此过程中Intel IOMMU与SWIOTLB二虎相争的故事。最终,SWIOTLB被禁用,而Intel IOMMU得以保留。现在,所有的DMA操作,都要经由Intel IOMMU了。本文将介绍Intel IOMMU在DMA Coherent Mapping过程中的作用。_linux x86-64 iommu详解
文章浏览阅读1.4w次,点赞4次,收藏40次。vue2.0实现富文本编辑及文本内容展示_vue显示富文本内容
文章浏览阅读694次。opentsDB单机版安装一、jdk安装1.下载https://www.oracle.com/technetwork/java/javase/downloads/index.html2.利用SecureCRT对服务器上传jdk,解压下载的jdk1.8.0_131tar -zxvf jdk-8u131-linux-x64.tar.gz -C /usr/local3.配置环境变量vi /..._opentsdb单机安装
文章浏览阅读835次,点赞2次,收藏2次。\documentclass{article}\usepackage{ctex}\usepackage{amsmath}\usepackage{amssymb}\usepackage{wasysym}\usepackage{booktabs}\usepackage{fancyhdr} \pagestyle{fancy} \lhead{} \chead{} \rhead{..._latex sup
文章浏览阅读370次。随着旅客的日益增加、线路的不断开辟,空中航线变得愈加繁忙。相应的,航空公司之间的竞争日趋激烈,对飞机的检修维护等工作更显得不可开交。据国际航空运输协会(IATA)的数据显...
文章浏览阅读7.8k次,点赞7次,收藏2次。windowds下编译go项目,执行如下操作时:SET CGO_ENABLED=0set GOARCH=amd64set GOOS=linuxgo build main.goset GOOS=linux这个操作的linux后面带了空格,编译器不能自动去掉空格,导致编译不过去。结束!..._go: unsupported goos/goarch pair linux /amd64
文章浏览阅读1.1k次。作者:德州仪器半导体技术(上海)有限公司 通用DSP 技术应用工程师 喻云峰1.简介TVP5150系列是一颗使用简易,超低功耗,封装极小的数字视频解码器。使用单一14.31818MHz时钟就可以实现PAL/NTSC/SECAM各种制式的解码,输出8-bit ITU-R BT.656数据,也可输出分离同步。MCU通过标准I2C接口控制TVP5150的诸多参数,比如色调,对比度,亮度,_1.8v系统电压多高会复位
文章浏览阅读2.9w次,点赞13次,收藏87次。Matlab函数函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。函数能提高应用的模块性,和代码的重复利用率。你已经知道Matlab提供了许多内建函数,比如disp()。但你也可以自己创建函数,这被叫做用户自定义函数matlabdisp(‘hello world’)hello world总的来说,自定义函数分为两步:即定义函数和调用函数。定义一个函数你可以定义一个由自己想要功能的函数,以下是简单的规则:函数代码块以 function关键词开头,后接输出变量和函数标识_matlab函数定义和调用
文章浏览阅读975次。安装JDK安装及配置安装对应版本的java环境,配置好环境变量。版本对应关系参考下表:JMeter版本JDK版本4.01.8 or 1.93.2/3.31.8+3.0/3.11.7+JDK环境变量配置:“我的电脑”属性-&amp;gt;高级-&amp;gt;环境变量-&amp;gt;在系统变量中添加以下变量及对应变量值变量名变量值_jemeter 4.0 使用jdk20
文章浏览阅读2.5w次,点赞3次,收藏21次。Python国内镜像地址:1.阿里云:https://mirrors.aliyun.com/pypi/simple/2.豆瓣:https://pypi.douban.com/simple/3.清华大学:https://pypi.tuna.tsinghua.edu.cn/simple/(推荐)4.中国科学技术大学 http://pypi.mirrors.ustc.edu.cn/simple/5.华中理工大学:http://pypi.hustunique.com/6.山东理工大学:http://py_python镜像
文章浏览阅读953次。Thin-stream属性,意味着应用程序以很低的速率发送数据,致使TCP等传输协议的重传机制不能有效的运行。一些场景(类似于在线游戏,控制系统,股票交易等)中,用户体验取决于数据的发送时延,报文丢失对于服务质量来说是灾难性的。极大的时延是由于TCP依赖于应用程序新的报文的发送,进而通过快速重传来启动丢失报文的重传,而不用等待较长时间的RTO超时。以上提到的时间敏感的交互应用,通常是会产生thi..._tcp_thin_linear_timeouts
文章浏览阅读2w次,点赞3次,收藏17次。1、linux下查看所有占用端口情况netstat -ntlp2、查看所有某个端口使用情况,如80端口。netstat -ntulp |grep 803、查看一台服务器上面哪些服务及端口。netstat -lanp4、查看一个服务有几个端口,比如要查看mysqld。ps -ef |grep mysqldnetstat命令各个参数说明如下:-a..._linux 机器如何查看大量处于tcp_wait 的端口是哪个