文章目录
一些基本的灰度变换函数
图像反转
//图像翻转 s = L - 1 - r
//使用书中的图片直接反转会产生错误 只会反转左侧少部分,
//注:在给Mat矩阵中像素位置赋值的时候 要先初始化Mat矩阵各个位置像素值,否则会报异常
// 应该养成定义矩阵的时候就将其像素值初始化。
void ImageInvert()
{
Mat src;
Mat src1, src2, src3;
Mat merge[] = {src1, src2, src3};
//这个tif格式文件明明只有一个通道 但是opencv函数计算却是三个通道 但是对三个通道进行操作 原因未知
//src = imread("E:\\冈萨雷斯OpenCV重写\\DIP3E_CH03_Original_Images\\DIP3E_Original_Images_CH03\\Fig0304(a)(breast_digital_Xray).tif",0);
//这个jpg为三通道
src = imread("E:\\冈萨雷斯OpenCV重写\\DIP3E_CH03_Original_Images\\DIP3E_Original_Images_CH03\\1.jpg");
int channels = src.channels();
imshow("src", src);
if (channels == 3)
{
cv:; split(src, merge);
}
else if (channels == 1)
{
merge[0] = src;
}
for (int i = 0; i < src.rows; i++)
for (int j = 0; j < src.cols; j++)
{
if (channels == 1)
merge[0].at<uchar>(i, j) = 255 - src.at<uchar>(i, j);
else if (channels == 3)
{
merge[0].at<uchar>(i,j) = 255 - src.at<Vec3b>(i, j)[0];
merge[1].at<uchar>(i, j) = 255 - src.at<Vec3b>(i, j)[1];
merge[2].at<uchar>(i, j) = 255 - src.at<Vec3b>(i, j)[2];
}
}
if (channels == 1)
{
imshow("dst1", merge[0]);
}
else if (channels == 3)
{
cv::merge(merge, 3, src);
imshow("dst1",src);
}
}
对数变换
//对数变换 s = clog(1+r) 对数变换会让低灰度动态范围变大 反对数变换 会让高灰度动态范围变大
//对数变换需要归一化 log(255) = 2.5....
//flag = true 对数变换 flag = false 反对数变换
void ImageLog(bool flag)
{
Mat src = imread("E:\\冈萨雷斯OpenCV重写\\DIP3E_CH03_Original_Images\\DIP3E_Original_Images_CH03\\Fig0305(a)(DFT_no_log).tif",0);
Mat dst = src.clone();
if (flag)
for (int i = 0; i < src.rows;i++)
for (int j = 0; j < src.cols; j++)
{
dst.at<uchar>(i, j) = 1 * log( 1 + src.at<uchar>(i, j))/log(256)*255;
}
else
for (int i = 0; i < src.rows; i++)
for (int j = 0; j < src.cols; j++)
{
dst.at<uchar>(i, j) = (pow(src.at<uchar>(i, j) / 1,2) - 1) / (pow(255,2) - 1) * 255;
}
imshow("src", src);
imshow("dst", dst);
}
- 对数变换 暗区域灰度变化范围变大
- 反对数变换 亮区域灰度变化范围变大
幂律(Gamma)变换
//幂律变换 Gamma > 1 亮区域灰度动态范围扩大, Gamma < 1 暗区域灰度动态范围扩大
void ImageGamma(float Gamma)
{
//Fig0305(a)(DFT_no_log).tif
//Fig0307(a)(intensity_ramp).tif
//Fig0308(a)(fractured_spine).tif
//Fig0309(a)(washed_out_aerial_image).tif
Mat src = imread("E:\\冈萨雷斯OpenCV重写\\DIP3E_CH03_Original_Images\\DIP3E_Original_Images_CH03\\Fig0309(a)(washed_out_aerial_image).tif", 0);
Mat dst = src.clone();
unsigned char LUT[256];
for (int i = 0; i < 256; i++)
{
LUT[i] = cv::saturate_cast<uchar>(pow((float)(i/255.0), Gamma)*255.0);
}
MatIterator_<uchar>iterator = dst.begin<uchar>();
MatIterator_<uchar>iteratorEnd = dst.end<uchar>();
while (iterator != iteratorEnd)
{
*iterator = LUT[*iterator];
iterator++;
}
imshow("src", src);
imshow("dst", dst);
}
-
Gamma < 0.3 暗区域灰度变换变大
-
Gamma > 1 亮区域活动范围变大
-
直方图处理
计算直方图
//显示一副图像的直方图
/*
C++: void calcHist(const Mat* images, int nimages, const int* channels, InputArray mask, OutputArray hist, int dims, const int* histSize, const float** ranges, bool uniform=true, bool accumulate=false )
参数详解:
onst Mat* images:输入图像
int nimages:输入图像的个数
const int* channels:需要统计直方图的第几通道
InputArray mask:掩膜,,计算掩膜内的直方图 ...Mat()
OutputArray hist:输出的直方图数组
int dims:需要统计直方图通道的个数
const int* histSize:指的是直方图分成多少个区间,就是 bin的个数
const float** ranges: 统计像素值得区间
bool uniform=true::是否对得到的直方图数组进行归一化处理
bool accumulate=false:在多个图像时,是否累计计算像素值得个数
*/
/*
normalize 归一化
NORM_MINMAX:数组的数值被平移或缩放到一个指定的范围,线性归一化,一般较常用。
NORM_INF:此类型的定义没有查到,根据OpenCV 1的对应项,可能是归一化数组的C-范数(绝对值的最大值)
NORM_L1 : 归一化数组的L1-范数(绝对值的和)
NORM_L2: 归一化数组的(欧几里德)L2-范数
*/
void HistCalc()
{
string st = "E:\\冈萨雷斯OpenCV重写\\DIP3E_CH03_Original_Images\\DIP3E_Original_Images_CH03\\";
Mat src1 = imread(st + "Fig0316(1)(top_left).tif",0);
Mat src2 = imread(st + "Fig0316(2)(2nd_from_top).tif",0);
Mat src3 = imread(st + "Fig0316(3)(third_from_top).tif",0);
Mat src4 = imread(st + "Fig0316(4)(bottom_left).tif",0);
MatND hist1, hist2, hist3, hist4;
imshow("src1", src1);
imshow("src2", src2);
imshow("src3", src3);
imshow("src4", src4);
const int channles[1] = { 0 };
const int histSize[1] = { 256 };
float range[2] = { 0, 255 };
const float *ranges[1] = {range};
calcHist(&src1, 1,channles, Mat(), hist1, 1,histSize, ranges);
normalize(hist1, hist1, 0, 255, NORM_MINMAX);
calcHist(&src2, 1, channles, Mat(), hist2, 1, histSize, ranges);
normalize(hist2, hist2, 0, 255, NORM_MINMAX);
calcHist(&src3, 1, channles, Mat(), hist3, 1, histSize, ranges);
normalize(hist3, hist3, 0, 255, NORM_MINMAX);
calcHist(&src4, 1, channles, Mat(), hist4, 1, histSize, ranges);
normalize(hist4, hist4, 0, 255, NORM_MINMAX);
Mat maphist2 = Mat::zeros(Size(256, 256), CV_8UC1), maphist3 = Mat::zeros(Size(256, 256), CV_8UC1), maphist4 = Mat::zeros(Size(256, 256), CV_8UC1), maphist1 = Mat::zeros(Size(256, 256), CV_8UC1);
for (int i = 1; i < 256; i++)
{
line(maphist1, Point(i - 1, 255 - hist1.at<float>(i-1)), Point(i, 255 - hist1.at<float>(i)), Scalar(255));
line(maphist2, Point(i - 1, 255 - hist2.at<float>(i - 1)), Point(i, 255 - hist2.at<float>(i)), Scalar(255));
line(maphist3, Point(i - 1, 255 - hist3.at<float>(i - 1)), Point(i, 255 - hist3.at<float>(i)), Scalar(255));
line(maphist4, Point(i - 1, 255 - hist4.at<float>(i - 1)), Point(i, 255 - hist4.at<float>(i)), Scalar(255));
}
imshow("Hist1", maphist1);
imshow("Hist2", maphist2);
imshow("Hist3", maphist3);
imshow("Hist4", maphist4);
}
计算 H-S 直方图
//H-S直方图
//OpenCV中HSV 中H分量范围是0-180 S范围是0-255 V范围是0-255
void HSHist()
{
string st = "E:\\冈萨雷斯OpenCV重写\\DIP3E_CH03_Original_Images\\DIP3E_Original_Images_CH03\\";
Mat src1 = imread(st + "1.jpg");
Mat src2 = imread(st + "2.tif");
Mat dst1 = src1.clone(),dst2 = src2.clone();
Mat HShist1(362, 512, CV_8UC1, Scalar(0)), HShist2(362, 512, CV_8UC1, Scalar(0));
MatND hist1,hist2;
cvtColor(src1, dst1, CV_BGR2HSV);
cvtColor(src2, dst2, CV_BGR2HSV);
const int channles[2] = {0,1};
const int histSize[] = { 180, 255 };
float range1[] = { 0, 180 }, range2[] = { 0, 255 };
const float* ranges[] = { range1, range2 };
calcHist(&dst1, 1, channles, Mat(), hist1, 2, histSize, ranges);
calcHist(&dst2, 1, channles, Mat(), hist2, 2, histSize, ranges);
normalize(hist1, hist1, 0, 255, NORM_MINMAX);
normalize(hist2, hist2, 0, 255, NORM_MINMAX);
for (int i = 0; i < 255;i++)
for (int j = 0; j < 180; j++)
{
rectangle(HShist1, Point(i * 2, j * 2), Point(i * 2 + 2, j * 2 + 2), Scalar(hist1.at<float>(j,i)));
rectangle(HShist2, Point(i * 2, j * 2), Point(i * 2 + 2, j * 2 + 2), Scalar(hist2.at<float>(j,i)));
}
imshow("src1", src1);
imshow("src2", src2);
imshow("HShist1", HShist1);
imshow("HShist2", HShist2);
}
断点 查看 H-S直方图如何生成
//用于观察HS图像是如何形成的 H为行坐标 S为列坐标 组成三维图
void HStest()
{
string st = "E:\\冈萨雷斯OpenCV重写\\DIP3E_CH03_Original_Images\\DIP3E_Original_Images_CH03\\";
Mat src = imread(st + "1.jpg");
Mat dst = src.clone();
cvtColor(src, dst, CV_BGR2HSV);
Mat mats[] = { Mat(dst.size(), CV_8UC1, Scalar(0)), Mat(dst.size(), CV_8UC1, Scalar(0)), Mat(dst.size(), CV_8UC1, Scalar(0)) };
split(dst, mats);
Mat H = mats[0];
Mat S = mats[1];
Mat V = mats[2];
Mat histS,histH,histHS;
const int channles[] = { 0};
const int histSize[] = { 180 };
float range1[] = { 0, 180 };
const float* ranges[] = { range1 };
calcHist(&H, 1, channles, Mat(), histH, 1, histSize, ranges);
double maxH, maxS, minH, minS, minHS, maxHS;
const int histSize1[] = { 255 };
float range11[] = { 0, 255 };
const float* ranges1[] = { range11 };
calcHist(&S, 1, channles, Mat(), histS, 1, histSize1, ranges1);
const int channles2[] = { 0,1};
const int histSize2[] = { 180,255 };
float range2[] = { 0, 180 },range3[] = { 0, 255 };
const float* ranges2[] = { range2 ,range3};
calcHist(&dst, 1, channles2, Mat(), histHS, 2, histSize2, ranges2);
minMaxLoc(histH, &minH, &maxH);
minMaxLoc(histS, &minS, &maxS);
minMaxLoc(histHS, &minHS, &maxHS);
}
直方图均衡
void EquHist()
{
string st = "E:\\冈萨雷斯OpenCV重写\\DIP3E_CH03_Original_Images\\DIP3E_Original_Images_CH03\\";
Mat src = imread(st + "Fig0354(a)(einstein_orig).tif", 0);
Mat AfterEquHist(src.rows, src.cols, CV_8UC1, Scalar(0));
MatND hist,hist1;
const int channles[] = { 0 };
const int histSize[] = { 256 };
float range[] = { 0, 255 };
const float* ranges[] = {range};
//const int
calcHist(&src,1, channles, Mat(), hist,1, histSize, ranges);
normalize(hist, hist, 0, 255, NORM_MINMAX);
Mat maphist1(256,256,CV_8UC1,Scalar(0));
for (int i = 1; i < 256; i++)
{
line(maphist1, Point(i - 1, 255 - hist.at<float>(i - 1)), Point(i, 255 - hist.at<float>(i)), Scalar(255));
}
equalizeHist(src, AfterEquHist);
calcHist(&AfterEquHist, 1, channles, Mat(), hist1, 1, histSize, ranges);
normalize(hist1, hist1, 0, 255, NORM_MINMAX);
Mat maphist2(256, 256, CV_8UC1, Scalar(0));
for (int i = 1; i < 256; i++)
{
line(maphist2, Point(i - 1, 255 - hist1.at<float>(i - 1)), Point(i, 255 - hist1.at<float>(i)), Scalar(255));
}
imshow("src",src);
imshow("hist", maphist1);
imshow("AfterEquHist", AfterEquHist);
imshow("hist1", maphist2);
}
类直方图均衡化
///直接形象的归一化但是与公式不同
//类似归一化函数 拓展到0-255
void EquHistNormalize()
{
string st = "E:\\冈萨雷斯OpenCV重写\\DIP3E_CH03_Original_Images\\DIP3E_Original_Images_CH03\\";
Mat grayImage = imread(st + "Fig0354(a)(einstein_orig).tif", 0);
Mat dst = grayImage.clone();
MatND hist;
const int channles[1] = { 0 };
const int histSize[1] = { 256 };
float range[2] = { 0, 255 };
const float* ranges[1] = { range };
calcHist(&grayImage, 1, channles,Mat(), hist, 1, histSize, ranges);
int num = 100;
int minvalue, maxvalue;
for (minvalue = 0; minvalue < hist.rows; minvalue++)
{
if (hist.at<float>(minvalue) > num)
break;
}
for (maxvalue = hist.rows; maxvalue < 0; maxvalue--)
{
if (hist.at<float>(maxvalue) > num)
break;
}
Mat LUTlist(1, 256, CV_8UC1, Scalar(0));
for (int i = 0; i < 256; i++)
{
if (i<minvalue)
LUTlist.at<uchar>(i) = 0;
else if(i>maxvalue)
LUTlist.at<uchar>(i) = 255;
else
LUTlist.at<uchar>(i) = 255*(i-minvalue)/(maxvalue-minvalue);
}
LUT(grayImage, LUTlist, dst);
imshow("原图",grayImage);
imshow("直方图均衡",dst);
}
直方图均衡 与公式匹配
//从公式本质出发实现
void EquHistMath()
{
string st = "E:\\冈萨雷斯OpenCV重写\\DIP3E_CH03_Original_Images\\DIP3E_Original_Images_CH03\\";
Mat grayImage = imread(st + "Fig0316(1)(top_left).tif", 0);
Mat dst = grayImage.clone();
MatND hist;
const int channles[1] = { 0 };
const int histSize[1] = { 256 };
float range[2] = {0,256};
const float* ranges[] = { range };
calcHist(&grayImage, 1, channles, Mat(),hist, 1, histSize, ranges);
Mat LUTlist(1,256,CV_8UC1,Scalar(0));
int tmp = 0;
for (int i = 0; i < hist.rows; i++)
{
tmp += hist.at<float>(i);
LUTlist.at<uchar>(i) = static_cast<uchar>(tmp * 255 / (grayImage.rows*grayImage.cols));
}
LUT(grayImage, LUTlist, dst);
imshow("src",grayImage);
imshow("dst",dst);
}
直方图匹配
//进行直方图匹配操作
//按照直方图规定的公式来看 直方图规定化操作不一定需要两个相同尺寸的图片 只需要一个规定的直方图累积概率分布
void HistMatch()
{
string st = "E:\\冈萨雷斯OpenCV重写\\DIP3E_CH03_Original_Images\\DIP3E_Original_Images_CH03\\";
//加载原图像
Mat srcImage = imread(st + "Fig0316(1)(top_left).tif", 0);
//加载用来匹配的图像
Mat dstImage = imread(st + "1.jpg", 0);
MatND hist1, hist2;
const int channles[1] = { 0 };
const int histSize[1] = { 256 };
float range[2] = { 0, 255 };
const float* ranges[] = { range };
calcHist(&srcImage, 1, channles, Mat(), hist1, 1, histSize, ranges);
calcHist(&dstImage, 1, channles, Mat(), hist2, 1, histSize, ranges);
float histadd1[256];
float histadd2[256];
float tmp1 = 0,tmp2 = 0;
float pixelnum1 = srcImage.rows*srcImage.cols;
float pixelnum2 = dstImage.rows*dstImage.cols;
for (int i = 0; i < 256; i++)
{
tmp1 += hist1.at<float>(i) / pixelnum1;
tmp2 += hist2.at<float>(i) / pixelnum2;
histadd1[i] = tmp1;
histadd2[i] = tmp2;
}
float diff = 1;
Mat LUTlist(1, 256, CV_8UC1, Scalar(0));
for (int i = 0; i < 256; i++)
{
diff = 1;
LUTlist.at<uchar>(i) = i;
for (int j = 0; j < 256; j++)
{
float value = abs(histadd1[i] - histadd2[j]);
if (value < diff)
{
diff = abs(histadd1[i] - histadd2[j]);
LUTlist.at<uchar>(i) = j;
}
}
}
Mat HistMatch(srcImage.rows, srcImage.cols, CV_8UC1, Scalar(0));
LUT(srcImage, LUTlist, HistMatch);
imshow("HistMatch", dstImage);
imshow("src",srcImage);
imshow("AfterMatchHistImage", HistMatch);
}
- 将过曝的图片 根据 Lena 直方图进行直方图匹配
局部直方图均衡
//局部直方图均衡化
void LocalEquHist(Size a)
{
string st = "E:\\冈萨雷斯OpenCV重写\\DIP3E_CH03_Original_Images\\DIP3E_Original_Images_CH03\\";
Mat src = imread(st + "Fig0326(a)(embedded_square_noisy_512).tif", 0);
Mat dst = src.clone();
for (int i = (a.height - 1) / 2 ; i < src.rows - (a.height - 1) / 2 ;i++)
for (int j = (a.width - 1) / 2 ; j < src.cols - (a.width - 1) / 2 ; j++)
{
int x = j - (a.width - 1) / 2;
int y = i - (a.height - 1) / 2;
int h = a.height;
int w = a.width;
Mat tmp = dst(Rect(x,y , w,h )).clone();
equalizeHist(tmp, tmp);
dst.at<uchar>(i, j) = tmp.at<uchar>((a.height - 1) / 2 , (a.width - 1) / 2);
}
imshow("src",src);
imshow("dst", dst);
}
- 核的大小为 11,11
反向投影
反向投影
//反向投影
void BackProject()
{
string st = "E:\\冈萨雷斯OpenCV重写\\DIP3E_CH03_Original_Images\\DIP3E_Original_Images_CH03\\";
Mat srcImage = imread(st + "1.jpg");
Mat hist;
Mat dstImage(srcImage.rows, srcImage.cols, CV_8UC1, Scalar(0));
cvtColor(srcImage, dstImage, CV_BGR2HSV);
const int channles[1] = { 0 };
const int histrange[1] = { 10 };
float range[2] = { 0, 255 };
const float* ranges[] = { range };
calcHist(&srcImage, 1, channles, Mat(), hist, 1, histrange, ranges);
normalize(hist, hist, 0,255, NORM_MINMAX);
//imshow("hist", hist);
Mat backImage;
cv::calcBackProject(&dstImage,1, channles, hist, backImage, ranges);
imshow("backImage",backImage);
imshow("src",srcImage);
}
反省投影匹配
//反向投影 目标匹配 时间比较慢 匹配效果也不算太好
void BackImagematch()
{
string st = "E:\\冈萨雷斯OpenCV重写\\DIP3E_CH03_Original_Images\\DIP3E_Original_Images_CH03\\";
Mat WaitMatchImage = imread(st + "1.jpg");
Mat ModelImage = imread(st + "1match.jpg");
Mat hist1,hist2;
int height = ModelImage.rows;
int weight = ModelImage.cols;
Mat HSV1, HSV2;
const int channles[1] = { 0 };
//这个histrange设置的越大 精度也就越高 量化的太严重 会导致 匹配到多个目标
const int histrange[1] = { 255 };
float range[2] = { 0, 255 };
const float* ranges[] = { range };
cvtColor(WaitMatchImage, HSV1, CV_BGR2HSV);
cvtColor(ModelImage, HSV2, CV_BGR2HSV);
calcHist(&HSV2, 1, channles, Mat(), hist2, 1, histrange, ranges);
Mat MatchResult(WaitMatchImage.rows, WaitMatchImage.cols, CV_8UC1, Scalar(0));
Mat tmp(ModelImage.rows, ModelImage.cols, CV_8UC1, Scalar(0));
int MatchValue = ModelImage.cols * ModelImage.rows;
int MatchValuerow = 0, MatchValuecol = 0;
for (int i = ModelImage.rows / 2; i < WaitMatchImage.rows - ModelImage.rows / 2 - 1; i++)
for (int j = ModelImage.cols / 2; j < WaitMatchImage.cols - ModelImage.cols / 2 - 1; j++)
{
tmp = HSV1(Rect(j - ModelImage.cols / 2, i - ModelImage.rows / 2, ModelImage.cols, ModelImage.rows)).clone();
calcHist(&tmp, 1, channles, Mat(), hist1, 1, histrange, ranges);
int a = 0;
for (int z = 0; z < histrange[0]; z++)
{
a += abs(hist1.at<float>(z)-hist2.at<float>(z));
}
if (MatchValue > a)
{
MatchValue = a;
MatchValuerow = i;
MatchValuecol = j;
}
MatchResult.at<uchar>(i, j) = (uchar)a;
}
normalize(MatchResult, MatchResult, 0, 255, NORM_MINMAX);
imshow("结果图", MatchResult);
imshow("待匹配图像",WaitMatchImage);
imshow("目标图像",ModelImage);
WaitMatchImage(Rect(MatchValuecol - ModelImage.cols / 2, MatchValuerow - ModelImage.rows / 2, ModelImage.cols, ModelImage.rows)) = Scalar::all(255);
imshow("合成图像", WaitMatchImage);
}
混一天和努力一天,
看不出任何差别,
三天看不到任何变化,
七天也看不到任何距离!
但是,一个月后,
会看到话题不同,
三个月后,
会看到气场不同,
半年后,
会看到距离不同,
一到三年后,
会看到人生道路不同!
不要假装很努力,
结果不会陪你演戏!