opencv 学习
第一章:
- 载入图片,保存,旋转。
第二章:
2.1目标: 一,学会如何遍历一张图像并处理其像素 二,高效的处理方法 灰度图:像素由8位无符号数来表示; 彩色图:由三个这样的8位无符号数来表示三个颜色通道,0黑色;255白色; opencv允许创建不同像素类型的矩阵或图像:如整形(CV_8U),浮点型(CV_32F)。 它们在一些图像处理过程中,用来保存中间值这样的内容很有用。大多数矩阵可以用于任意类型的矩阵,但有些运算对数据类型或矩阵的通道数有要求。
2.2.存取像素值:高效的遍历数组。椒盐噪点,即随机设置为黑白,黑白1通道,彩色3通道。Mat类,at方法。 需要在代码中指定元素所在的行(row)和列(col),函数会返回相应的元素。单通道返回单个数值;多通道返回一组向量(Vector)。
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
void salt(Mat& image, int n) {
for (int k = 0; k < n; k++)
{
int i = rand() % image.cols;
int j = rand() % image.rows;
if (image.channels() == 1) {
image.at<uchar>(j, i) = 255;
}
else if (image.channels() == 3)
{
image.at<Vec3b>(j, i)[0] = 255;
image.at<Vec3b>(j, i)[1] = 255;
image.at<Vec3b>(j, i)[2] = 255;
}
}
}
int main() {
Mat src;
src = imread("D:/opencvcode/img/13.png");
if (!src.data) {
printf("not img!");
return -1;
}
salt(src, 3000);
namedWindow("inputimg");
imshow("inputimg", src);
waitKey(0);
return 0;
}
拓展阅读:cv::Mat_是cv::Mat的一个模板子类。
2.3 用指针遍历:rows,cows。 将要遍历图像的所有像素时,像素个数很多,所以高效的遍历很重要。以下两种方法,第一种是指针算术; 每个像素的每个通道,将其值除以N,整除。再乘以N。
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
void colorreduce(Mat& image, int div = 64) {
int n1 = image.rows;
int nc = image.cols * image.channels();
for (int j = 0; j < n1; j++) {
uchar* data = image.ptr<uchar>(j);
for (int i = 0; i < nc; i++) {
data[i] = data[i] / div * div + div / 2;
}
}
}
int main() {
Mat img;
img = imread("D:/opencvcode/img/13.png");
colorreduce(img);
namedWindow("input");
imshow("input", img);
Mat imageClone = img.clone();
colorreduce(imageClone);
void colorReduce(const Mat & image,
Mat & resul,
int div = 64);
void colorreduce(Mat & img, int div = 64) {
int n1 = img.rows;
int nc = img.cols * img.channels();
if (img.isContinuous())
{
nc = nc * n1;
n1 = 1;
}
for (int j = 0; j < n1; j++) {
uchar* data = img.ptr<uchar>(j);
data[i] = data[i] / div * div + div / 2;
}
}
if (img.isContinuous())
{
img.reshape(1,img.cols * img.rows);
}
int n1 = img.rows;
int nc = img.cols * img.channels();
namedWindow("input");
imshow("input", img);
waitKey(0);
return 0;
}
cols代表宽度,rows代表高度,step代表以字节为单位的图像有效宽度。elemSize函数能得到像素大小,channels方法得到图像通道数,total函数返回矩阵像素个数。 in-place变换:直接在输入的图像上进行操作,不然就是新创建一个图像 ,“深拷贝”的方式是调用clone函数。
3.高效的遍历连续图像 若不对图像行尾经行填补的时候,图像视为一个长WxH的一位数组。通过cv::Mat的一个成员函数isContinuous方法来判断是否经行了填补,真,没有填补。
4.底层指针运算 容易出错,还不能定义感兴趣区域,不好用。
2.4 迭代器遍历图像 面向对象中常用迭代器编历数据集合,迭代器是一种特殊的类。不管数据类型是什么,都可以用相似的方式遍历集合,STL标准模板库为每一个容器类提供了迭代器,cv::Mat提供与STL迭代器兼容的迭代器。
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
void colorreduce(Mat& img, int div = 64) {
Mat_<Vec3b>::iterator it = img.begin<Vec3b>();
Mat_<Vec3b>::iterator itend = img.end<Vec3b>();
for (; it != itend; ++it) {
(*it)[0] = (*it)[0] / div * div + div / 2;
(*it)[1] = (*it)[1] / div * div + div / 2;
(*it)[2] = (*it)[2] / div * div + div / 2;
}
}
int main() {
Mat img = imread("D:/opencvcode/img/13.png");
if (img.empty()) {
printf("not img!");
return -1;
}
colorreduce(img);
namedWindow("input");
imshow("input", img);
waitKey(0);
return 0;
}
在循环体内部,使用解引用操作符*来读写当前元素。读,element=*it,写,*it=element。 若操作对象是const cv::Mat或者强调当前循环不会对cv::Mat的实列进行修改,要创建常量迭代器。 创建方法1:cv::Mat ConstIterator_cv::Vec3bit; 方法2:cv::Mat_cv::Vec3b::const_iterator it;
迭代器初始化(法2)
cv::Mat_<cv::Vec3b>cimage=image;
cv::Mat_<cv::Vec3b>::iterator it =cimage.begin();
cv::Mat_<cv::Vec3b>::iteratoe itend=cimage.end();
面向对象的迭代器概念,阅读STL中迭代器相关的入门文章。关键字:STL Iterator
2.5. 高效的图像遍历循环
分析效率,提高运行效率。
opencv中, cv:: getTickCount()函数用来检测一段代码的运行时间,计算从上次开机算起的时钟周期数。我们需要计算某个代码段的运行毫秒数,用cv::getTickFrequency(),此函数返回每秒内的时钟周期数。
double duration;
duration =static_cast<double>(cv::getTickCount());
colorreduce(image);
duration =static_cast<double>(cv::getTickCount())-duration;
duration/=cv::getTickFrequency();
at方法实现:
for(int j=0;j<nl;j++){
for(int i=0;i<nc;i++){
image.at<cv::Vec3b>(j,i)[0]=image.at<cv::Vec3b>(j,i)[0]/div*div+div/2;
image.at<cv::Vec3b>(j,i)[1]=image.at<cv::Vec3b>(j,i)[1]/div*div+div/2;
image.at<cv::Vec3b>(j,i)[2]=image.at<cv::Vec3b>(j,i)[2]/div*div+div/2;
}
}
几种函数时间效率的分析:
3-channel loop实现方法,速度最快的版本:
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
void colorreduce(Mat& image, int div = 64) {
int n1 = image.rows;
int nc = image.cols;
if (image.isContinuous()) {
nc = nc * n1;
n1 = 1;
}
int n = static_cast<int>(
log(static_cast<double>(div)) / log(2.0));
uchar mask = 0xFF << n;
for (int j = 0; j < nc; j++) {
uchar* data = image.ptr<uchar>(j);
for (int i = 0; i < nc; i++) {
*data++ = *data & mask + div / 2;
*data++ = *data & mask + div / 2;
*data++ = *data & mask + div / 2;
}
}
}
int main() {
Mat img;
img = imread("D:/opencvcode/img/13.png");
double duration;
duration = static_cast<double>(cv::getTickCount());
colorreduce(img);
duration = static_cast<double>(cv::getTickCount()) - duration;
duration /= cv::getTickFrequency();
namedWindow("input");
imshow("input", img);
waitKey(0);
return 0;
}
double duration;
duration =static_cast<double>(cv::getTickCount());
colorreduce(image);
duration =static_cast<double>(cv::getTickCount())-duration;
duration/=cv::getTickFrequency();
for(int j=0;j<nl;j++){
for(int i=0;i<nc;i++){
image.at<cv::Vec3b>(j,i)[0]=image.at<cv::Vec3b>(j,i)[0]/div*div+div/2;
image.at<cv::Vec3b>(j,i)[1]=image.at<cv::Vec3b>(j,i)[1]/div*div+div/2;
image.at<cv::Vec3b>(j,i)[2]=image.at<cv::Vec3b>(j,i)[2]/div*div+div/2;
}
}
2.6. 遍历图像与邻域操作 由当前位置的相邻像素计算新的像素值。 对图像进行锐化,基于拉普拉斯算子。处理过后图像的边缘部分将得到放大,细节更加锐利。
sharpened_pixel=5*current-left-right-up-down; left代表在当前像素左边紧挨着它的像素……
实现方式:3个指针,指向当前行,上一行,下一行。因为每一个像素值都要计算上下左右4个相邻像素,所以第一行,最后一行, 第一列,最后一列不能计算到。
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
void sharpen2D(const cv::Mat& image, cv::Mat& result) {
cv::Mat kernel(3, 3, CV_32F, cv::Scalar(0));
kernel.at<float>(1, 1) = 5.0;
kernel.at<float>(0, 1) = -1.0;
kernel.at<float>(2, 1) = -1.0;
kernel.at<float>(1, 0) = -1.0;
kernel.at<float>(1, 2) = -1.0;
cv::filter2D(image, result, image.depth(), kernel);
}
int main() {
Mat image = imread("D:/opencvcode/img/13.png");
namedWindow("input1");
imshow("input1", image);
sharpen2D(image, image);
namedWindow("input2");
imshow("input2", image);
waitKey(0);
return 0;
}
3个指针同时进行,计算输出像素值时,模板函数cv::saturate_cast用来截断计算结果。
边缘处理: 边缘像素不好处理,在程序中用两个函数设为0。方法2是,使用setTo 函数来设置矩阵的值,这个函数会将矩阵的所有元素都设为指定的值。
result.row(0).setTo(cv::Scalar(0));将结果第一行的所有像素设置为0。
三通道的彩色图,cv::Scalar(a,b,c)来指定像素的三个通道目标值。
2.7. 简单的图像计算 图像是矩阵,可进行加减乘除运算。 1.图像相加cv::add,完整的cv::addWeighted //c[i]=a[i]+b[i]; cv::add(imageA,imageB,resultC);
cv::addWeighted(image1,0.7,image2,0.9,0.,result);==
用c++写,重载图像操作符:result=0.7*image1+0.9*image2;
//把一个图像加到蓝色通道上
//创建一个图像向量 std::vectorcv::Matplanes; //将一个三通道图像分离为三个单通道图像 cv::split(image1,planes); //将新图层叠加到蓝色通道 planes[0]+=image2; //将三个单通道图像重新合并为一个三通道图像 cv::merge(planes,result);
//cv::merge是cv::split的对偶运算,它将三个单通道图像合并为一个彩色三通道图像
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
void sharpen2D(const cv::Mat& image, cv::Mat& result) {
cv::Mat kernel(3, 3, CV_32F, cv::Scalar(0));
kernel.at<float>(1, 1) = 5.0;
kernel.at<float>(0, 1) = -1.0;
kernel.at<float>(2, 1) = -1.0;
kernel.at<float>(1, 0) = -1.0;
kernel.at<float>(1, 2) = -1.0;
cv::filter2D(image, result, image.depth(), kernel);
}
int main() {
Mat image = imread("D:/opencvcode/img/13.png");
Mat image2 = imread("D:/opencvcode/img/13-1png");
cv::addWeighted(image, 0.7, image2, 0.9, 0., result);
sharpen2D(image, image);
namedWindow("input");
imshow("input", image);
waitKey(0);
return 0;
}
2.8. 定义感兴趣区域 合并两个不同大小的图像,cv::add合并两个相同尺寸的图像,所以不能用。在使用之前定义感兴趣区域(ROI),感兴趣区域的大小与图像大小相同时,cv::add才能工作。ROI的位置决定了插入图像的位置。
源码中创建ROI方式和原始图像共享数据缓冲区,对ROI的任何操作都会影响到原始图像的对应区域。因为创建ROI时不会拷贝数据。
以下创建包含原始图像特定行的ROI
cv::Mat imageROI=image.rowRange(start,end);
cv::Mat imageROI=image.colRange(start,end);
源代码:
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
int main() {
Mat image = imread("D:/opencvcode/img/13.png");
Mat logo = imread("D:/opencvcode/img/13-2.png");
cv::Mat imageROI;
imageROI = image(cv::Rect(385, 270,logo.cols.logo.rows));
cv::addWeighted(imageROI, 1.0, logo, 0.3, 0., imageROI);
namedWindow("input1");
imshow("input1", image);
waitKey(0);
return 0;
}
第三章 基于类的图像处理 目标: 在算法设计中使用策略(Strategy)模式 使用控制器(Controller)实现模块间通信 使用单件(Singleton)设计模式 使用模型-视图-控制器(Model-View-Controller)架构设计引用程序 颜色空间转换
- 3.1设计模式,软件工程中的概念,程序员要熟悉现有的模式。操作图像中的颜色,检测拥有特定颜色的像素。
- 3.2 策略设计模式
将算法封装在类中,替换一个现有的算法。
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
class ColorDetector {
private:
int minDist;
cv::Vec3b target;
cv::Mat result;
};
int main()
{
ColorDetector cdetect;
Mat image = imread("D:/opencvcode/img/13.png ");
if (!image.data)
return 0;
cdetect.setTargetColor(130, 190, 230);
namedWindow("result", WINDOW_AUTOSIZE);
imshow("result",cdetect.process(image));
waitKey(0);
return 0;
}
3.3使用控制器实现模块间通信
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main() {
class ColorDetectController {
private:
ColorDetector * cdetect;
Mat image;
Mat result;
public:
ColorDetectController() {
cdetect = new ColorDetector();
}
void setColorDistanceThreshold(int distance) {
cdetect->setColorDistanceThreshold(distance);
}
int getColorDistanceThreshold() const {
return cdetect->getColorDistanceThreshold();
}
void setTargetColor(unsigned char red, unsigned char green, unsigned char blue) {
cdetect->setTargetColor(red, green, blue);
}
void setTargetColor(unsigned char &red, unsigned char &green, unsigned char &blue)const {
Vec3b color = cdetect->getTargetColor();
red = color[2];
green = color[1];
blue = color[0];
}
bool setInputImage(std::string filename) {
image = cv::imread(filename);
if (!image.data)
return false;
else
return true;
}
const cv::Mat getInputImage()const {
return image;
}
void process() {
result = cdetect->process(image);
}
const cv::Mat getLastResult() const {
return result;
}
~ColorDetectController() {
delete cdetect;
}
void onOpen() {
CFileDialog dlg(TRUE, T("*.bmp"), NULL,OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY,_T("image files(*.bmp;*,jpg)| *.bmp; *.jpg | All Files(*.*) | *.* || "),NULL);
dlg.m_ofn.lpstrTitle = _T("Open Image");
if (dlg.DoModal() == IDOK) {
std::string filename = dlg.GetPathName();
colordetect.setInputImage(filename);
cv::imshow("input Image", colordetect.getInputImage());
}
}
void OnProcess() {
colordetect.setTargetColor(130, 190, 230);
colordetect.process();
cv::imshow("output Result", colordetect.getLastResult());
}
waitKey(0);
return 0;
}
};
3.4使用单件设计模式 单件(Singleton)模式
3.5使用模式-视图-控制器(Model-View-Controller)构架设计应用程序 MVC,基于Qt的GUI
3.6颜色空间转换 使用cv::cvtColor可以轻易在不同颜色空间经行转换,
第4章 使用直方图统计像素 计算图像的直方图 通过查找表修改图像外观 直方图均衡化 反投影直方图以检测特定的图像内容 使用均值漂移(Mean Shift)计算查找物体 通过比较直方图检索相似图片
4.2计算图像的直方图 归一化 cv::calcHist函数,可计算任意类型的多通道图像
4.3使用查找表修改图像外观
4.4直方图均衡化 均衡,
4.5反投影直方图以检测特定的图像内容 检测特定的内容,首先选取感兴趣区域(ROI), 一个概率映射
4.6使用均值漂移(Mean Shift)计算查找物体 知道物体的近似位置,用迭代移动,找到精确的位置。 HSV颜色空间,
4.7通过比较直方图检索相似图片 cv::compareHist函数
第5章 基于形态学运算的图像变换 使用形态学滤波对图像进行腐蚀、膨胀运算 使用形态学滤波对图像进行开闭运算 使用形态学滤波对图像进行边缘及角点检测 使用分水岭算法对图像进行分割 使用GrabCut算法提取前景物体
5.2使用形态学滤波对图像进行腐蚀、膨胀运算 腐蚀和膨胀函数:cv::erode,cv::dilate
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;
int main() {
Mat image = imread("D:/opencvcode/img/my1.png");
imshow("input ", image);
Mat eroded;
erode(image, image, Mat());
imshow("Eroded Image", image);
Mat dilated;
dilate(image, dilated,Mat());
namedWindow("Dilated Image");
imshow("Dilated Image", dilated);
waitKey(0);
return 0;
}
5.3使用形态学滤波对图像进行开闭运算 cv::morphologyEx函数 闭运算 开运算
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;
int main() {
Mat image = imread("D:/opencvcode/img/my1.png");
imshow("input ", image);
Mat dilated;
dilate(image, dilated, Mat());
Mat result;
dilate(image, result, Mat());
erode(result, result, Mat());
imshow("opened Image", result);
namedWindow("Dilated Image");
imshow("Dilated Image", dilated);
waitKey(0);
return 0;
}
5.4使用形态学滤波对图像进行边缘及角点检测 形态学滤波用于检测图像中的特征,检测灰度图中的直线和角点
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(){
class MorphoFeatures
{
private:
int threshold;
Mat cross;
Mat diamond;
Mat square;
Mat x;
cv::Mat getEdges(const Mat& image)
{
Mat result;
morphologyEx(image, result, MORPH_GRADIENT, Mat());
applyThreshold(result);
return result;
}
void applyThreshold(Mat& result) {
if (threshold > 0)
cv:: threshold(result, result, threshold, 255, THRESH_BINARY);
}
};
MorphoFeatures() : threshold(-1),
cross(5, 5, CV_8U, cv::Scalar(0)),
diamond(5, 5, CV_8U, Scalar(1)),
square(5, 5, CV_8U, Scalar(1)), x(5, 5, CV_8U, Scalar(0)) {
for (int i = 0; i < 5; i++) {
cross.at<uchar>(2, i) = 1;
cross.at<uchar>(i, 2) = 1;
}
diamond.at<uchar>(0, 0) = 0;
diamond.at<uchar>(0, 1) = 0;
diamond.at<uchar>(1, 0) = 0;
diamond.at<uchar>(4, 4) = 0;
diamond.at<uchar>(3, 4) = 0;
diamond.at<uchar>(4, 3) = 0;
diamond.at<uchar>(4, 0) = 0;
diamond.at<uchar>(4, 1) = 0;
diamond.at<uchar>(3, 0) = 0;
diamond.at<uchar>(0, 4) = 0;
diamond.at<uchar>(0, 3) = 0;
diamond.at<uchar>(1, 4) = 0;
for (int i = 0; i < 5; i++) {
x.at<uchar>(i, i) = 1;
x.at<uchat>(4 - i, i) = 1;
}
}
cv::Mat getCorners(const cv::Mat &image) {
Mat result;
dilate(image, result,cross);
cv::erode(result, result, diamond);
cv::Mat result2;
cv::dilate(image, result2, x);
cv::erode(result2, result2, square);
applyThreshold(result);
return result;
}
void drawOnImage(const cv::Mat& binary,
cv::Mat& image) {
cv::Mat_<uchar>::const_iterator it = binary.begin<uchar>();
cv::Mat_<uchar>::const_iterator itend = binary.end<uchar>();
for (int i = 0; it != itend; ++it, ++i) {
if (!*it)
cv::circle(image,
cv::circle(image,
cv::Point(i % image.step, i / image.step),
5, cv::Scalar(255, 0, 0));
}
}
Mat image = imread("D:/opencvcode/img/gry.png");
MorphoFeatures morpho;
morpho.setThreshold(40);
Mat edges;
edges = morpho.getEdges(image);
Mat corners;
corners = morpho.getCorners(image);
morpho.drawOnImage(corners, image);
cv::nameWindow("Corners on Image");
cv::imshow("Corners on Image", image);
waitKey(0);
return 0;
}
5.5使用分水岭算法对图像进行分割 快速分割图像为同类区域 分割结果通过cv::watershed函数获得,
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;
int main() {
Mat image = imread("D:/opencvcode/img/my1.png");
class WatershedSegment {
private:
cv::Mat markers;
public:
void setMarkers(const cv::Mat& markerImage) {
markerImage.convertTo(markers, CV_32S);
}
cv::Mat process(const Mat & image) {
watershed(image, markers);
return markers;
}
cv::Mat fg;
cv::erode(binary, fg, Mat(), Point(-1, -1), 6);
Mat bg;
dilate(binary, bg, Mat(), Point(-1, -1), 6);
threshold(bg, bg, 1, 128, THRESH_BINARY_INV);
Mat markers(binary.size(), CV_8U, Scalar(0));
markers = fg + bg;
WatershedSegment segmenter;
segmenter.setMarkers(markers);
segmenter.process(image);
Mat getSetmentation() {
Mat tmp;
markers.convertTo(tmp, CV_8U);
return tmp;
}
Mat getWatersheds() {
Mat tmp;
markers.convertTo(tmp, CV_8U, 255, 255);
return tmp;
}
};
namedWindow("Dilated Image");
imshow("Dilated Image", image);
waitKey(0);
return 0;
}
5.6使用GrabCut算法提取前景物体 与分水岭算法有相似之处,但更加复杂,产生的结果精确很多。 从静态图像中提前前景物体的应用,如,将一幅图中的物体剪贴到另一幅图中。是最佳算法。
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main() {
Mat image;
image = imread("D:/opencvcode/img/13.png");
Rect rectangle(10, 100, 380, 180);
Mat result;
Mat bgModel, fgmodel;
grabCut(image,
result,
rectangle,
bgModel, fgmodel,
5,
GC_INIT_WITH_RECT);
compare(result, GC_PR_FGD,result, CMP_EQ);
result = result & 1;
Mat foreground(image.size(), CV_8UC3,
Scalar(255, 255, 255));
image.copyTo(foreground,
result);
imshow("output", image);
waitKey(0);
return 0;
}
第6章 图像滤波 使用低通滤波器 使用中值滤波器 使用方向滤波器检测边缘 计算图像的拉普拉斯变换
6.1滤波操作,增强 部分频段,限制其他频段。低通滤波器去除了图像中的高频成分,高通滤波器去除了低频成分。
6.2使用低通滤波器 降低图像变化的幅度,将每个像素替换为相邻像素的平均值。使用cv::blur函数
cv::blur(image,result,cv::Size(5,5));
高斯函数,cv::GaussianBlur(image,result,cv::Size(5,5),1.5);
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main() {
Mat image = imread("D:/opencvcode/img/13.png");
imshow("intput", image);
Mat result;
cv::GaussianBlur(image, result, cv::Size(5, 5), 1.5);
Mat reducedImage;
pyrDown(image, reducedImage);
Mat resizedImage;
resize(image, resizedImage,Size(image.cols/3,image.rows/3));
imshow("output", result);
waitKey(0);
return 0;
}
6.3使用中值滤波器 上一节线性滤波器,这一节中值滤波器就是一种非线性滤波器。 中值滤波器对去除噪点很有用,
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
void salt(Mat& image, int n) {
for (int k = 0; k < n; k++)
{
int i = rand() % image.cols;
int j = rand() % image.rows;
if (image.channels() == 1) {
image.at<uchar>(j, i) = 255;
}
else if (image.channels() == 3)
{
image.at<Vec3b>(j, i)[0] = 255;
image.at<Vec3b>(j, i)[1] = 255;
image.at<Vec3b>(j, i)[2] = 255;
}
}
}
int main() {
Mat src,result;
src = imread("D:/opencvcode/img/13.png");
if (!src.data) {
printf("not img!");
return -1;
}
salt(src, 3000);
namedWindow("inputimg");
imshow("inputimg", src);
medianBlur(src, result,5);
namedWindow("outputimg");
imshow("outputimg", result);
waitKey(0);
return 0;
}
6.4使用方向滤波器检测边缘 强调图像中的高频分量,Sobel滤波器。具有方向性
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
int main() {
Mat src, sobelX, sobelY;
src = imread("D:/opencvcode/img/13.png");
if (!src.data) {
printf("not img!");
return -1;
}
namedWindow("inputimg");
imshow("inputimg", src);
cv::Sobel(src,sobelX,CV_8U, 1, 0, 3, 0.4, 128);
Sobel(src,sobelY, CV_8U, 0, 1, 3, 0.4, 128);
Sobel(src, sobelX, CV_16S, 1, 0);
Sobel(src,sobelY, CV_16S,0,1);
Mat sobel;
sobel = abs(sobelX) + abs(sobelY);
double sobmin, sobmax;
minMaxLoc(sobel, &sobmin, &sobmax);
Mat sobelImage;
sobel.convertTo(sobelImage, CV_8U, -255. / sobmax, 255);
namedWindow("outputimg");
imshow("outputimg", src);
waitKey(0);
return 0;
}
6.5计算图像的拉普拉斯变换 一种基于图像导数的高通线性滤波器。 函数Laplacion与Sobel函数很类似。
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main() {
class LaplacianZC {
private:
Mat img;
Mat laplace;
int aperture;
public:
LaplacianZC() :aperture(3) {}
void setAperture(int a) {
aperture = a;
}
Mat computeLaplacian(const Mat& image) {
Laplacian(image, laplace, CV_32F, aperture);
img = image.clone();
return laplace;
}
};
Mat getLaplacianImage(double scale = -1.0) {
if (scale < 0)
{
double lapmin, lapmax;
minMaxLoc(laplace, &lapmin, &lapmax);
scale = 127 / std::max(-lapmin, lapmax);
}
Mat laplaceImage;
laplace.convertTo(laplaceImage, CVV_8U, scale, 128);
return laplaceImage;
}
LaplacianZC laplacian;
laplacian.setAperture(7);
Mat flap = laplacian.computeLacian(image);
laplace = laplacian.getLaplacianImage();
Mat getZeroCrossings(float threshold = 1.0)
{
Mat_<float>::const_iterator it =
Laplace.begin<float>() + lapace.stepl();
Mat_<float>::const_iterator itend =
laplace.end<float>();
Mat_<float>::const_iterator itup =
laplace.begin<float>();
Mat binary(laplace.size(), CV_8U, Scalar(255));
Mat_<uchar>::iterator itout =
binary.begin<uchar>() + binary.stepl();
threshold *= -1.0;
for (; it! = itend; ++it, ++itup, ++itout)
if (*it * *(it - 1) < threshold)
*itout = 0;
else if (*it * *itup < threshold)
*itout = 0;
}
return binary;
Mat image= imread("D:/opencvcode/img/13.png");
namedWindow("inputimg");
imshow("inputimg", image);
waitKey(0);
return 0;
}
|