做了以下几部分:
- 根据比特位进行图像的分层;
- 灰度图的均衡化;
- 平滑滤波;
- 中值滤波;
- Laplace锐化;
- Soble滤波。
- 形态学操作:
并实现了通常的掩模滤波的类,参考平滑滤波、中值滤波、Laplace、Sobel可以轻易的使用其它类型的滤波器。
以下为代码:
//============================================================================
// Name : Test.cpp
// Author :
// Version :
// Copyright : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================
#include <string>
#include <vector>
#include <iostream>
#include <iterator>
#include <sstream>
#include <ctime>
using namespace std;
#include <opencv/cv.h>
#include <opencv/cxcore.h>
#include <opencv/highgui.h>
#include <opencv2/legacy/legacy.hpp>
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
using namespace cv;
/*!
* 通过对灰度值根据某一比特位上的值进行分层
*/
void ReadBook3_2_4() {
cv::Mat img = cv::imread("/home/chuanqi/13.jpg", 0);
cv::imshow("origin", img);
cv::Mat img_splited[8];
for (int i = 0; i < 8; ++i) {
img_splited[i] = cv::Mat::zeros(img.rows, img.cols, CV_8UC1);
}
for (int i = 0; i < img.rows; ++i) {
for (int j = 0; j < img.cols; ++j) {
char value = img.at<uchar>(i, j);
for (int k = 0; k < 8; ++k) {
if (value & (1 << k)) {
img_splited[k].at<uchar>(i, j) = 255;
} else {
img_splited[k].at<uchar>(i, j) = 0;
}
}
}
}
for (int i = 0; i < 8; ++i) {
cv::imshow("channel_" + boost::lexical_cast<string>(i), img_splited[i]);
cout << i << ",";
}
cv::waitKey();
}
/*!
* 灰度图的均衡化
*/
void ReadBook3_3_1() {
cv::Mat img = cv::imread("/home/chuanqi/13.jpg", 0);
cv::imshow("origin", img);
cv::Mat img2 = img.clone();
cv::equalizeHist(img2, img2);
cv::imshow("equalized by opencv", img2);
double hist[256];
for (int i = 0; i < 256; ++i) {
hist[i] = 0;
}
for (int i = 0; i < img.rows; ++i) {
for (int j = 0; j < img.cols; ++j) {
++hist[img.at<uchar>(i, j)];
}
}
for (int i = 0; i < 256; ++i) {
hist[i] /= (img.rows * img.cols);
hist[i] *= 255;
}
for (int i = 0; i < img.rows; ++i) {
for (int j = 0; j < img.cols; ++j) {
uchar &value = img.at<uchar>(i, j);
double sum = 0;
for (int i = 0; i < value; ++i) {
sum += hist[i];
}
value = sum;
}
}
cv::imshow("equalized by self", img);
cv::waitKey();
}
/*!
* 空间域滤波器
*/
class SpaceFilter {
public:
/*!
* 响应计算算法的抽象基类
*/
class ResponseCalculatorBase {
public:
virtual ~ResponseCalculatorBase() {}
virtual int CalculateResponse(vector<vector<int> > const &filter, vector<vector<int> > const &f) const = 0;
};
/*!
* 普通模板滤波器的响应计算算法
*/
class CommonResponseCaclulator: public ResponseCalculatorBase {
public:
explicit CommonResponseCaclulator(double divider) : divider_(divider) {
if (divider_ == 0) {
throw runtime_error("divider cannot be 0!");
}
}
virtual int CalculateResponse(vector<vector<int> > const &filter, vector<vector<int> > const &f) const {
int sum = 0;
for (size_t i = 0; i < f.size(); ++i) {
for (size_t j = 0; j < f[0].size(); ++j) {
sum += (filter[i][j] * f[i][j]);
}
}
sum /= divider_;
//必须归一化到0-255之间去
if(sum < 0) sum = 0;
if(sum > 255) sum = 255;
return sum;
}
private:
double divider_;
};
SpaceFilter(cv::Mat &image, vector<vector<int> > const &filter, ResponseCalculatorBase const &valuer)
: image_(image), filter_(filter), valuer_(valuer) {
if (filter_.size() % 2 != 1 || filter_[0].size() % 2 != 1) {
throw runtime_error("filter must be odd!");
}
}
cv::Mat DoFilte() {
cv::Mat filted_image = image_.clone();
int filter_m = filter_.size();
int filter_n = filter_[0].size();
vector<vector<int> > v(filter_m, vector<int>(filter_n));
for (int i = 0; i < image_.rows; ++i) {
for (int j = 0; j < image_.cols; ++j) {
for (int m = 0; m < filter_m; ++m) {
int indexi = i - filter_m / 2 + m;
indexi = indexi > 0 ? indexi : 0;
indexi = indexi < image_.rows ? indexi : image_.rows;
for (int n = 0; n < filter_n; ++n) {
int indexj = j - filter_n / 2 + n;
indexj = indexj > 0 ? indexj : 0;
indexj = indexj < image_.cols ? indexj : image_.cols;
v[m][n] = image_.at<uchar>(indexi, indexj);
}
}
filted_image.at<uchar>(i, j) = valuer_.CalculateResponse(filter_, v);
}
}
return filted_image;
}
private:
cv::Mat &image_;
vector<vector<int> > const &filter_;
ResponseCalculatorBase const &valuer_;
};
/*!
* 平滑滤波
*/
void ReadBook3_6_1() {
cv::Mat img = cv::imread("/home/chuanqi/Noise.jpg", 0);
cv::imshow("origin", img);
/* 滤波器模板如下:
* 1 2 1
* 2 4 2
* 1 2 1
*/
vector<vector<int> > templates(3, vector<int>(3, 1));
templates[0][1] = templates[1][0] = templates[1][2] = templates[2][1] = 2;
templates[1][1] = 4;
SpaceFilter::CommonResponseCaclulator valuer(16);
SpaceFilter filter(img, templates, valuer);
cv::imshow("smooth filted", filter.DoFilte());
cv::waitKey();
}
/*!
* 中值滤波
*/
void ReadBook3_6_2() {
/*!
* 中值滤波器计算响应的算法,与普通的模板计算响应有些不同
*/
class MedianFilterValuer: public SpaceFilter::ResponseCalculatorBase {
public:
virtual int CalculateResponse(vector<vector<int> > const &filter, vector<vector<int> > const &f) const {
vector<int> values;
for (size_t i = 0; i < f.size(); ++i) {
for (size_t j = 0; j < f[0].size(); ++j) {
values.push_back(f[i][j]);
}
}
sort(values.begin(), values.end());
return values[values.size() / 2];
}
};
cv::Mat img = cv::imread("/home/chuanqi/Noise.jpg", 0);
cv::imshow("origin", img);
MedianFilterValuer valuer;
vector<vector<int> > templates(3, vector<int>(3, 1));
SpaceFilter filter(img, templates, valuer);
cv::imshow("median filted", filter.DoFilte());
cv::waitKey();
}
/*!
* 拉普拉斯锐化
*/
void ReadBook3_7_2(){
cv::Mat img = cv::imread("/home/chuanqi/moon.png", 0);
cv::imshow("origin", img);
/* 滤波器模板如下:
* 0 -1 0
* -1 5 -1
* 0 -1 0
*/
vector<vector<int> > templates(3, vector<int>(3, 0));
templates[0][0] = 0; templates[0][1] = -1; templates[0][2] = 0;
templates[1][0] = -1; templates[1][1] = 5; templates[1][2] = -1;
templates[2][0] = 0; templates[2][1] = -1; templates[2][2] = 0;
SpaceFilter::CommonResponseCaclulator valuer(1);
SpaceFilter filter(img, templates, valuer);
cv::imshow("Laplace filted", filter.DoFilte());
cv::waitKey();
}
/*!
* Sobel滤波
*/
void ReadBook3_7_3(){
cv::Mat img = cv::imread("/home/chuanqi/moon.png", 0);
cv::imshow("origin", img);
/* 滤波器模板如下:
* -1 -2 -1
* 0 2 0
* 1 2 1
*/
vector<vector<int> > templates(3, vector<int>(3, 0));
templates[0][0] = -1; templates[0][1] = -2; templates[0][2] = -1;
templates[1][0] = 0; templates[1][1] = 0; templates[1][2] = 0;
templates[2][0] = 1; templates[2][1] = 2; templates[2][2] = 1;
SpaceFilter::CommonResponseCaclulator valuer(1);
SpaceFilter filter(img, templates, valuer);
cv::imshow("Sobel filted", filter.DoFilte());
cv::waitKey();
}
以下为效果图:
cv::Mat img = cv::imread("/home/chuanqi/ImageDataset/13.jpg");
cv::imshow("origin", img);
cv::cvtColor(img, img, CV_BGR2HSV);
vector<cv::Mat> splited(3);
splited[0] = cv::Mat::zeros(img.rows, img.cols, CV_8UC1);
splited[1] = cv::Mat::zeros(img.rows, img.cols, CV_8UC1);
splited[2] = cv::Mat::zeros(img.rows, img.cols, CV_8UC1);
cv::split(img, splited);
cv::equalizeHist(splited[0], splited[0]);
cv::equalizeHist(splited[1], splited[1]);
cv::equalizeHist(splited[2], splited[2]);
cv::merge(splited, img);
cv::cvtColor(img, img, CV_HSV2BGR);
cv::imshow("abc", img);
cv::waitKey();
/*
* 形态学学习
*/
int main(){
string const img_path = "/home/chuanqi/ImageDataset/13.jpg";
cv::Mat const img = cv::imread(img_path, 0);
cv::imshow("origin", img);
//腐蚀
cv::Mat img_erode;
int erosion_size = 1;
cv::Mat erosion_element = cv::getStructuringElement( MORPH_RECT,
cv::Size( 2*erosion_size + 1, 2*erosion_size+1 ),
cv::Point( erosion_size, erosion_size ) );
cv::erode(img, img_erode, erosion_element);
cv::imshow("erode", img_erode);
//膨胀
cv::Mat img_dilate;
int dilation_size = 2;
cv::Mat dilation_element = cv::getStructuringElement( MORPH_RECT,
cv::Size( 2*dilation_size + 1, 2*dilation_size+1 ),
cv::Point( dilation_size, dilation_size ) );
cv::dilate(img, img_dilate, dilation_element);
cv::imshow("dilate", img_dilate);
//基础形态学操作
string morphology_operation_name[] = {"ERODE","DILATE","OPEN","CLOSE","GRADIENT","TOPHAT","BLACKHAT"};
int morph_size = 1; //采用 2×1+1=3 为大小的核
Mat element = getStructuringElement( MORPH_RECT, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size));
for (int operation = 0; operation < 7; ++operation) {
cv::Mat img_morphology_result;
cv::morphologyEx(img, img_morphology_result, operation, element);
cv::imshow(morphology_operation_name[operation], img_morphology_result);
}
cv::waitKey();
}
因为在灰度图上操作时,形态学的两个基本操作膨胀和腐蚀采用了取最大最小值的方式,所以使得:
形态学操作在灰度图上结果的局部方块性太严重了,因此说形态学还是更适合于二值图像,对灰度图像起的效果并不是很明显。