对“lena.jpg”变换后的灰度图像进行如下操作:
1) 统计每个灰度级(0-255)的像素点数,并打印显示;
原理:灰度级是表示灰度图像的亮度层次范围,每个灰度级都对应着相应的像素点数
实验步骤:分析实验过程——编写代码——运行——获得实验结果
源代码:
#include <iostream>
#include "opencv.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
//计算图像直方图
class Histogram1D{
private:
int histSize[1];
float hranges[2];
const float* ranges[1];
int channels[1];
public: Histogram1D() {
histSize[0] = 256;
hranges[0] = 0.0;
hranges[1] = 256.0;
ranges[0] = hranges;
channels[0] = 0; }
cv::Mat getHistogram(const cv::Mat& image) {
cv::Mat hist;
cv::calcHist(&image,
1,//仅为一个图像的直方图
channels,//使用的通道
cv::Mat(),//不使用掩码
hist,//作为结果的直方图
1,//这时一维的直方图
histSize,//箱子数量
ranges//像素值的范围
);
return hist;
}
};
int main(){
cv::Mat image = cv::imread("C:\\Users\\15518\\Desktop\\opencv\\lena.jpg", 0);
cv::imshow("lena", image);
Histogram1D ch;
cv::Mat histo = ch.getHistogram(image);
for (int i = 0; i < 256; i++) {
std::cout << "Value " << i << " = " << histo.at<float>(i) << std::endl;
}
cv::waitKey();
}
实验结果:
2)实现幂律变换,并显示;
原理:幂律变换,又称伽马变换,主要用于图像的校正,将灰度过高或者灰度过低的图片进行修正,增强对比度。
实验步骤:分析实验过程和理解幂律变换公式——编写代码——运行——获得实验结果
源代码:
#include "opencv.hpp"
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/imgproc/types_c.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/highgui/highgui_c.h>
#include <iostream>
using namespace cv;
using namespace std;
void MyGammaCorrection(Mat& src, Mat& dst, float fGamma)
{
// build look up table
unsigned char lut[256];
for (int i = 0; i < 256; i++)
{
lut[i] = saturate_cast<uchar>(pow((float)(i / 255.0), fGamma) * 255.0f);
}
dst = src.clone();
const int channels = dst.channels();
switch (channels)
{
case 1: //灰度图的情况
{
MatIterator_<uchar> it, end;
for (it = dst.begin<uchar>(), end = dst.end<uchar>(); it != end; it++)
//*it = pow((float)(((*it))/255.0), fGamma) * 255.0;
*it = lut[(*it)];
break;
}
case 3: //彩色图的情况
{
MatIterator_<Vec3b> it, end;
for (it = dst.begin<Vec3b>(), end = dst.end<Vec3b>(); it != end; it++)
{
//(*it)[0] = pow((float)(((*it)[0])/255.0), fGamma) * 255.0;
//(*it)[1] = pow((float)(((*it)[1])/255.0), fGamma) * 255.0;
//(*it)[2] = pow((float)(((*it)[2])/255.0), fGamma) * 255.0;
(*it)[0] = lut[((*it)[0])];
(*it)[1] = lut[((*it)[1])];
(*it)[2] = lut[((*it)[2])];
}
break;
}
}
}
int main()
{
Mat image = imread("C:\\Users\\15518\\Desktop\\opencv\\lena.jpg", 0);
if (image.empty())
{
cout << "Error: Could not load image" << endl;
return 0;
}
Mat dst;
float fGamma = 5; //修改Y的大小
MyGammaCorrection(image, dst, fGamma);
imshow("原始灰度图", image);
imshow("y>1的幂律变换图", dst);
waitKey();
return 0;
}
实验结果:
(1)Y<1的幂律变换
(2)Y>1的幂律变换
3)用双线性插值将图像放到 1.5 倍和缩小 0.8 倍,并显示
原理:双线性插值,又称为双线性内插。在数学上,双线性插值是有两个变量的插值函数的线性插值扩展,其核心思想是在两个方向分别进行一次线性插值
实验步骤:分析实验过程和理解双线性插值——编写代码——运行——获得实验结果
源代码:
#include "opencv.hpp"
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/imgproc/types_c.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/highgui/highgui_c.h>
#include <iostream>
using namespace cv;
using namespace std;
//双线性插值
sx,xy -- 缩放因子
void Inter_Linear(cv::Mat& src, cv::Mat& dst, double sx, double sy) {
int dst_rows = round(sx*src.rows);
int dst_cols = round(sy*src.cols);
dst = cv::Mat(dst_rows, dst_cols, src.type());
for (int i = 0; i < dst.rows; i++) {
//几何中心对齐
double index_i = (i + 0.5) / sx - 0.5;
//防止越界
if (index_i<0) index_i = 0;
if (index_i >= src.rows - 1) index_i = src.rows - 2;
//相邻4*4像素的行(坐标)
int i1 = floor(index_i);
int i2 = ceil(index_i);
//u为得到浮点型坐标行的小数部分
double u = index_i - i1;
for (int j = 0; j < dst.cols; j++) {
//几何中心对齐
double index_j = (j + 0.5) / sy - 0.5;
//防止越界
if (index_j<0) index_j = 0;
if (index_j >= src.cols - 1) index_j = src.cols - 2;
//相邻4*4像素的列(坐标)
int j1 = floor(index_j);
int j2 = ceil(index_j);
//v为得到浮点型坐标列的小数部分
double v = index_j - j1;
if (src.channels() == 1) {
//灰度图像
dst.at<uchar>(i, j) = (1 - u)*(1 - v)*src.at<uchar>(i1, j1) + (1 - u)*v*src.at<uchar>(i1, j2) + u * (1 - v)*src.at<uchar>(i2, j1) + u * v*src.at<uchar>(i2, j2);
}
else {
//彩色图像
dst.at<cv::Vec3b>(i, j)[0] = (1 - u)*(1 - v)*src.at<cv::Vec3b>(i1, j1)[0] + (1 - u)*v*src.at<cv::Vec3b>(i1, j2)[0] + u * (1 - v)*src.at<cv::Vec3b>(i2, j1)[0] + u * v*src.at<cv::Vec3b>(i2, j2)[0];
dst.at<cv::Vec3b>(i, j)[1] = (1 - u)*(1 - v)*src.at<cv::Vec3b>(i1, j1)[1] + (1 - u)*v*src.at<cv::Vec3b>(i1, j2)[1] + u * (1 - v)*src.at<cv::Vec3b>(i2, j1)[1] + u * v*src.at<cv::Vec3b>(i2, j2)[1];
dst.at<cv::Vec3b>(i, j)[2] = (1 - u)*(1 - v)*src.at<cv::Vec3b>(i1, j1)[2] + (1 - u)*v*src.at<cv::Vec3b>(i1, j2)[2] + u * (1 - v)*src.at<cv::Vec3b>(i2, j1)[2] + u * v*src.at<cv::Vec3b>(i2, j2)[2];
}
}
}
}
int main()
{
Mat image = imread("C:\\Users\\15518\\Desktop\\opencv\\lena1.jpg", 0);
Mat dst;
double sx = 0.8;
double sy = 0.8;
Inter_Linear(image, dst, sx, sy);
imshow("原始灰度图", image);
imshow("双线性插值0.8倍图", dst);
waitKey();
return 0;
}
改变缩放因子sx,sy,可以改变缩放比例
实验结果:
(1)双线性插值将图像放到 1.5 倍
(2)双线性插值将图像放到 0.8 倍
4)将图像进行直方图均衡化处理,显示图像及其直方图;
原理:直方图均衡化实质上是对图像进行非线性拉伸,重新分配图像象元值,使一定范围内象元值的数量大致相等。
实验步骤:分析实验过程和理解直方图均衡化——编写代码——运行——获得实验结果
源代码:
#include <iostream>
#include "opencv.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
class Histogram1D {
private:
int histSize[1];
float hranges[2];
const float* ranges[1];
int channels[1]; public:
Histogram1D() {
histSize[0] = 256;
hranges[0] = 0.0;
hranges[1] = 256.0;
ranges[0] = hranges;
channels[0] = 0;
}
cv::Mat getHistogram(const cv::Mat& image) {
cv::Mat hist;
cv::calcHist(&image,
1,//仅为一个图像的直方图
channels,//使用的通道
cv::Mat(),//不使用掩码
hist,//作为结果的直方图
1,//这时一维的直方图
histSize,//箱子数量
ranges//像素值的范围
);
return hist;
}
cv::Mat getHistogramImage(const cv::Mat& image, int zoom = 1) {
cv::Mat hist = getHistogram(image);
return getImageofHistogram(hist, zoom);
}
static cv::Mat getImageofHistogram(const cv::Mat &hist, int zoom) {
double maxVal = 0;
double minVal = 0;
cv::minMaxLoc(hist, &minVal, &maxVal, 0, 0);
int histSize = hist.rows;
cv::Mat histImg(histSize *zoom, histSize *zoom, CV_8U, cv::Scalar(255));
//设置最高点为90%箱子的个数
int hpt = static_cast<int>(0.9*histSize);
for (int h = 0; h < histSize; h++)
{
float binVal = hist.at<float>(h);
if (binVal > 0) {
int intensity = static_cast<int>(binVal*hpt / maxVal);
cv::line(histImg, cv::Point(h*zoom, histSize*zoom),
cv::Point(h*zoom, (histSize - intensity)*zoom),
cv::Scalar(0), zoom);
}
}
return histImg;
}
};
int main(){
cv::Mat image = cv::imread("C:\\Users\\15518\\Desktop\\opencv\\lena.jpg", 0);
cv::Mat result;
equalizeHist(image, result);
Histogram1D ch;
cv::Mat histo = ch.getHistogram(result);
/*for (int i = 0; i < 256; i++) {
std::cout << "Value " << i << " = " << histo.at<float>(i) << std::endl;
} */
cv::Mat hist = ch.getHistogramImage(result);
cv::imshow("原图", image);
cv::imshow("均衡化图像", result);
cv::imshow("直方图", hist);
cv::waitKey();
}
实验结果:
5)分别以 3x3、7x7 模板进行均值滤波(或中值滤波)处理,并显示;
原理:均值滤波是典型的线性滤波算法,它是指在图像上对目标像素给一个模板,该模板包括了其周围的临近像素(以目标像素为中心的周围8个像素,构成一个滤波模板,即去掉目标像素本身),再用模板中的全体像素的平均值来代替原来像素值。
实验步骤:分析实验过程和理解直方图均衡化——编写代码——运行——获得实验结果
源代码:
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
int main()
{
//载入原始图
Mat srcImage = imread("C:\\Users\\15518\\Desktop\\opencv\\lena.jpg", 0);
//显示原始图
imshow("原图", srcImage);
//进行均值滤波操作
Mat dstImage;
blur(srcImage, dstImage, Size(7, 7));
//显示效果图
imshow("7x7均值滤波效果图", dstImage);
waitKey(0);
}
调整blur(srcImage, dstImage, Size(7, 7))函数中的Size()可以改变模板大小
实验结果:
(1)3x3 模板进行均值滤波处理的结果
(2)7x7 模板进行均值滤波处理的结果
扩展部分
源代码:
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include<ctime>
using namespace cv;
using namespace std;
//均值滤波
void AverFiltering(const Mat &src, Mat &dst) {
if (!src.data) return;
//at访问像素点
for (int i = 1; i<src.rows; ++i)
for (int j = 1; j < src.cols; ++j) {
if ((i - 1 >= 0) && (j - 1) >= 0 && (i + 1)<src.rows && (j + 1)<src.cols) {//边缘不进行处理
dst.at<uchar>(i, j) = (src.at<uchar>(i, j) + src.at<uchar>(i - 1, j - 1) + src.at<uchar>(i - 1, j) + src.at<uchar>(i, j - 1) +
src.at<uchar>(i - 1, j + 1) + src.at<uchar>(i + 1, j - 1) + src.at<uchar>(i + 1, j + 1) + src.at<uchar>(i, j + 1) +
src.at<uchar>(i + 1, j)) / 9;
}
else {//边缘赋值
dst.at<uchar>(i, j) = src.at<uchar>(i, j);
}
}
}
//图像椒盐化
void salt(Mat &image, int num) {
if (!image.data) return;//防止传入空图
int i, j;
srand(time(NULL));
for (int x = 0; x < num; ++x) {
i = rand() % image.rows;
j = rand() % image.cols;
image.at<uchar>(i, j) = 255;
}
}
void main() {
Mat image = imread("C:\\Users\\15518\\Desktop\\opencv\\lena.jpg",0);
Mat Salt_Image;
image.copyTo(Salt_Image);
salt(Salt_Image, 3000);
Mat image1(image.size(), image.type());
Mat image2;
AverFiltering(Salt_Image, image1);
blur(Salt_Image, image2, Size(3, 3));//openCV库自带的均值滤波函数
//imshow("椒盐化图像", Salt_Image);
imshow("原图", image);
imshow("自定义均值滤波", image1);
imshow("openCV自带的均值滤波", image2);
waitKey();
}
实验结果:
(1)椒盐化图像
3x3模板进行均值滤波处理的图像
6)分别使用拉普拉斯算子和 Sobel 算子对图像进行锐化处理,并显示。
使用拉普拉斯算子对图像进行锐化处理的原理:当邻域中心像素灰度低于它所在的领域内其它像素的平均灰度时,此中心像素的灰度应被进一步降低,当邻域中心像素灰度高于它所在的邻域内其它像素的平均灰度时,此中心像素的灰度应被进一步提高,以此实现图像的锐化处理。
实验步骤:分析实验过程和理解拉普拉斯算子——编写代码——运行——获得实验结果
源代码:
#include<opencv2/core/core.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>
void sharpen(const cv::Mat image, cv::Mat &result){
result.create(image.size(), image.type());
for(int j = 1; j < image.rows-1; j++) {
const uchar* previous = image.ptr<const uchar>(j-1);
const uchar* current = image.ptr<const uchar>(j);
const uchar* next = image.ptr<const uchar>(j+1);
uchar* output = result.ptr<uchar>(j);
for(int i = 1; i < image.cols-1; i++) {
//sharpened_pixel = 5*current-left-right-up-down;
//cv::saturate_cast用以对计算结果进行截断(0-255)
*output++ = cv::saturate_cast<uchar>(
5*current[i]-current[i-1]
-current[i+1]-previous[i]-next[i]);
}
}
result.row(0).setTo(cv::Scalar(0));
result.row(result.rows-1).setTo(cv::Scalar(0));
result.col(0).setTo(cv::Scalar(0));
result.col(result.cols-1).setTo(cv::Scalar(0));
}
int main(int argc,char** argv){
cv::Mat img = cv::imread(argc == 2 ? argv[1] : "C:\\Users\\15518\\Desktop\\opencv\\lena.jpg", 0);
cv::Mat result;
result.create(img.size(), img.type());
sharpen(img, result);
cv::namedWindow("sharpen");
cv::namedWindow("img");
cv::imshow("img",img);
cv::imshow("sharpen", result);
cv::waitKey(0);
}
使用拉普拉斯算子对图像进行锐化处理的实验结果:
使用Sobel 算子对图像进行锐化处理的原理:Sobel算子根据像素点上下、左右邻点灰度加权差,在边缘处达到极值这一现象检测边缘。
实验步骤:分析实验过程和理解Sobel算子——编写代码(高斯平滑,转灰度,求梯度xy,振幅图像(合并近似梯度))——运行——获得实验结果
源代码:
#include <opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
#include <string>
#include<fstream>
#include <opencv2/imgproc/types_c.h>
using namespace cv;
using namespace std;
int main() {
Mat src, gaosrc, graysrc, xdst, ydst, dst;
src = imread("C:\\Users\\15518\\Desktop\\opencv\\lena.jpg");
//imshow("input", src);
GaussianBlur(src, gaosrc, Size(3, 3), 0, 0);
//imshow("gaosioutput", gaosrc);
cvtColor(gaosrc, graysrc, CV_RGB2GRAY);
imshow("grayoutput", graysrc);
Sobel(graysrc, xdst, -1, 1, 0);
//imshow("xdst", xdst);
Sobel(graysrc, ydst, -1, 0, 1);
//imshow("ydst", ydst);
addWeighted(xdst, 0.5, ydst, 0.5, 1, dst);
imshow("SOBEL_OUTPUT", dst);
waitKey(0);
}
使用Sobel 算子对图像进行锐化处理的实验结果:
应用文献:
https://blog.csdn.net/qinghuaci666/article/details/81737624
https://blog.csdn.net/weixin_40647819/article/details/86601070