EX2: 用CImg改寫canny演算法 EX2
1. canny.h
#ifndef _CANNY_
#define _CANNY_
#include "CImg.h"
#include <vector>
using namespace std;
using namespace cimg_library;
class canny {
private:
CImg<unsigned char> img; //Original Image
CImg<unsigned char> grayscaled; // Grayscale
CImg<unsigned char> gFiltered; // Gradient
CImg<unsigned char> sFiltered; //Sobel Filtered
CImg<unsigned char> angles; //Angle Map
CImg<unsigned char> non; // Non-maxima supp.
CImg<unsigned char> thres; //Double threshold and final
public:
canny(char const*); //Constructor
CImg<unsigned char> toGrayScale();
vector<vector<double> > createFilter(int, int, double); //Creates a gaussian filter
CImg<unsigned char> useFilter(CImg<unsigned char>, vector<vector<double> >); //Use some filter
CImg<unsigned char> sobel(); //Sobel filtering
CImg<unsigned char> nonMaxSupp(); //Non-maxima supp.
CImg<unsigned char> threshold(CImg<unsigned char>, int, int); //Double threshold and finalize picture
};
#endif
2. canny.cpp
Z
#define _USE_MATH_DEFINES
#include "canny.h"
#include <vector>
#include <iostream>
using namespace std;
canny::canny(char const* filename)
{
CImg<unsigned char> temp(filename);
img = temp;
if (0) // Check for invalid input
{
cout << "Could not open or find the image" << std::endl;
}
else
{
vector<vector<double> > filter = createFilter(3, 3, 1);
//Print filter
for (int i = 0; i<filter.size(); i++)
{
for (int j = 0; j<filter[i].size(); j++)
{
cout << filter[i][j] << " ";
}
}
grayscaled = toGrayScale(); //Grayscale the image
gFiltered = useFilter(grayscaled, filter); //Gaussian Filter
sFiltered = sobel(); //Sobel Filter
non = nonMaxSupp(); //Non-Maxima Suppression
thres = threshold(non, 77, 95); //Double Threshold and Finalize 20 40
/***************way1 to display*******************/
CImgDisplay img_disp(img, "Original"),
grayscaled_disp(grayscaled,"GrayScaled"),
gFiltered_disp(gFiltered, "Gaussian Blur"),
sFiltered_disp(sFiltered, "Sobel Filtered"),
non_disp(non, "Non-Maxima Supp."),
thres_disp(thres, "Final");
while (!img_disp.is_closed()
|| !grayscaled_disp.is_closed()
|| !gFiltered_disp.is_closed()
|| !sFiltered_disp.is_closed()
|| !non_disp.is_closed()
|| !thres_disp.is_closed()) {
}
}
}
CImg<unsigned char> canny::toGrayScale() {
grayscaled = CImg<unsigned char>(img.rows, img.cols, 1); // one channel
cimg_forXY(img, x, y)
{
int b = img(x, y, 0);
int g = img(x, y, 1);
int r = img(x, y, 2);
double newValue = (r * 0.2126 + g * 0.7152 + b * 0.0722);
grayscaled(x, y) = newValue;
}
return grayscaled;
}
vector<vector<double>> canny::createFilter(int row, int column, double sigmaIn)
{
vector<vector<double>> filter(row, vector<int>double(col, -1));
int row = img.row;
int col = img.col;
float coordSum = 0;
float constant = 2.0 * sigmaIn * sigmaIn;
// Sum is for normalization
float sum = 0.0;
for (int x = - row/2; x <= row/2; x++)
{
for (int y = -column/2; y <= column/2; y++)
{
coordSum = (x*x + y*y);
filter[x + row/2][y + column/2] = (exp(-(coordSum) / constant)) / (M_PI * constant);
sum += filter[x + row/2][y + column/2];
}
}
// Normalize the Filter
for (int i = 0; i < row; i++)
for (int j = 0; j < column; j++)
filter[i][j] /= sum;
return filter;
}
CImg<unsigned char> canny::useFilter(CImg<unsigned char> img_in, vector<vector<double>> filterIn)
{
int size = (int)filterIn.size()/2;
CImg<unsigned char> filteredImg = CImg<unsigned char>(img_in.rows - 2*size, img_in.cols - 2*size, 1);
for (int i = size; i < img_in.rows - size; i++)
{
for (int j = size; j < img_in.cols - size; j++)
{
double sum = 0;
for (int x = 0; x < filterIn.size(); x++)
for (int y = 0; y < filterIn.size(); y++)
{
sum += filterIn[x][y] * (double)(img_in(i + x - size, j + y - size));
}
filteredImg(i-size, j-size) = sum;
}
}
return filteredImg;
}
CImg<unsigned char> canny::sobel()
{
//Sobel X Filter
double x1[] = {-1.0, 0, 1.0};
double x2[] = {-2.0, 0, 2.0};
double x3[] = {-1.0, 0, 1.0};
vector<vector<double>> xFilter(3);
xFilter[0].assign(x1, x1+3);
xFilter[1].assign(x2, x2+3);
xFilter[2].assign(x3, x3+3);
//Sobel Y Filter
double y1[] = {1.0, 2.0, 1.0};
double y2[] = {0, 0, 0};
double y3[] = {-1.0, -2.0, -1.0};
vector<vector<double>> yFilter(3);
yFilter[0].assign(y1, y1+3);
yFilter[1].assign(y2, y2+3);
yFilter[2].assign(y3, y3+3);
//Limit Size
int size = (int)xFilter.size()/2;
CImg<unsigned char> filteredImg = CImg<unsigned char>(gFiltered.rows - 2*size, gFiltered.cols - 2*size);
angles = CImg<unsigned char>(gFiltered.rows - 2*size, gFiltered.cols - 2*size, 1); //AngleMap
for (int i = size; i < gFiltered.rows - size; i++)
{
for (int j = size; j < gFiltered.cols - size; j++)
{
double sumx = 0;
double sumy = 0;
for (int x = 0; x < xFilter.size(); x++)
for (int y = 0; y < xFilter.size(); y++)
{
sumx += xFilter[x][y] * (double)(gFiltered(i + x - size, j + y - size)); //Sobel_X Filter Value
sumy += yFilter[x][y] * (double)(gFiltered(i + x - size, j + y - size)); //Sobel_Y Filter Value
}
double sumxsq = sumx*sumx;
double sumysq = sumy*sumy;
double sq2 = sqrt(sumxsq + sumysq);
if(sq2 > 255) //Unsigned Char Fix
sq2 =255;
filteredImg(i-size, j-size) = sq2;
if(sumx==0) //Arctan Fix
angles(i-size, j-size) = 90;
else
angles(i-size, j-size) = atan(sumy/sumx);
}
}
return filteredImg;
}
CImg<unsigned char> canny::nonMaxSupp()
{
CImg<unsigned char> nonMaxSupped = CImg<unsigned char>(sFiltered.rows-2, sFiltered.cols-2, CV_8UC1);
for (int i=1; i<sFiltered.rows - 1; i++) {
for (int j=1; j<sFiltered.cols - 1; j++) {
float Tangent = angles(i,j);
nonMaxSupped(i-1, j-1) = sFiltered(i,j);
//Horizontal Edge
if (((-22.5 < Tangent) && (Tangent <= 22.5)) || ((157.5 < Tangent) && (Tangent <= -157.5)))
{
if ((sFiltered(i,j) < sFiltered(i,j+1)) || (sFiltered(i,j) < sFiltered<uchar>(i,j-1)))
nonMaxSupped<uchar>(i-1, j-1) = 0;
}
//Vertical Edge
if (((-112.5 < Tangent) && (Tangent <= -67.5)) || ((67.5 < Tangent) && (Tangent <= 112.5)))
{
if ((sFiltered.at(i,j) < sFiltered(i+1,j)) || (sFiltered(i,j) < sFiltered(i-1,j)))
nonMaxSupped.at(i-1, j-1) = 0;
}
//-45 Degree Edge
if (((-67.5 < Tangent) && (Tangent <= -22.5)) || ((112.5 < Tangent) && (Tangent <= 157.5)))
{
if ((sFiltered(i,j) < sFiltered(i-1,j+1)) || (sFiltered(i,j) < sFiltered(i+1,j-1)))
nonMaxSupped.at<uchar>(i-1, j-1) = 0;
}
//45 Degree Edge
if (((-157.5 < Tangent) && (Tangent <= -112.5)) || ((22.5 < Tangent) && (Tangent <= 67.5)))
{
if ((sFiltered(i,j) < sFiltered(i+1,j+1)) || (sFiltered(i,j) < sFiltered(i-1,j-1)))
nonMaxSupped(i-1, j-1) = 0;
}
}
}
return nonMaxSupped;
}
CImg<unsigned char> canny::threshold(CImg<unsigned char> imgin,int low, int high)
{
if(low > 255)
low = 255;
if(high > 255)
high = 255;
CImg<unsigned char> EdgeMat = CImg<unsigned char>(imgin.rows, imgin.cols, imgin.type());
for (int i=0; i<imgin.rows; i++)
{
for (int j = 0; j<imgin.cols; j++)
{
EdgeMat(i,j) = imgin(i,j);
if(EdgeMat(i,j) > high)
EdgeMat(i,j) = 255;
else if(EdgeMat(i,j) < low)
EdgeMat(i,j) = 0;
else
{
bool anyHigh = false;
bool anyBetween = false;
for (int x=i-1; x < i+2; x++)
{
for (int y = j-1; y<j+2; y++)
{
if(x <= 0 || y <= 0 || EdgeMat.rows || y > EdgeMat.cols) //Out of bounds
continue;
else
{
if(EdgeMat(x,y) > high)
{
EdgeMat(i,j) = 255;
anyHigh = true;
break;
}
else if(EdgeMat(x,y) <= high && EdgeMat(x,y) >= low)
anyBetween = true;
}
}
if(anyHigh)
break;
}
if(!anyHigh && anyBetween)
for (int x=i-2; x < i+3; x++)
{
for (int y = j-1; y<j+3; y++)
{
if(x < 0 || y < 0 || x > EdgeMat.rows || y > EdgeMat.cols) //Out of bounds
continue;
else
{
if(EdgeMat(x,y) > high)
{
EdgeMat(i,j) = 255;
anyHigh = true;
break;
}
}
}
if(anyHigh)
break;
}
if(!anyHigh)
EdgeMat(i,j) = 0;
}
}
}
return EdgeMat;
}
3. 執行效果
3.1 lena
3.2 bigben
3.3 stpetro
3.4 twows
4. 引數說明
4.1 toGrayScale
主要在於double newValue = (r * 0.2126 + g * 0.7152 + b * 0.0722);
這句話,把每個點轉為灰色
4.2 高斯模糊
首先呼叫createFilter
生成卷積核,再用useFilter
對影象進行卷積
4.3 sobel
用Gx和Gy兩個卷積核對影象進行卷積,得到梯度變化大的邊界。再用非極大值抑制法剔除非邊緣的點。
4.4 雙閾值法
剔除那些梯度變化過小或過大的點,以消除噪聲
相關文章
- Windows10系統下打不開Share EX2提示Are you happy怎麼辦WindowsAPP
- 西部資料(WD)的 My Cloud EX2 儲存裝置預設配置洩露檔案資訊Cloud
- 用WITH…AS改寫標量子查詢
- 邊緣檢測演算法——Canny和LoG邊緣檢測演算法演算法
- 前端工具Rome將用Rust改寫前端Rust
- sql改寫SQL
- 用分析函式改寫冗長的sql函式SQL
- 3.Canny邊緣檢測
- SQL抽象語法樹及改寫場景應用SQL抽象語法樹
- SQL 抽象語法樹及改寫場景應用SQL抽象語法樹
- SQL改寫優化SQL優化
- Oracle case when改寫SQLOracleSQL
- Oracle Rownum分頁改寫Oracle
- DB2 SQL改寫DB2SQL
- 一條SQL的改寫SQL
- 用Kotlin改寫PHP程式是什麼樣的體驗KotlinPHP
- Oracle到PostgreSQL等價改寫OracleSQL
- MySQL的SQL等價改寫MySql
- 多屏判斷css改寫CSS
- 改寫不走索引的SQL索引SQL
- opencv——自適應閾值Canny邊緣檢測OpenCV
- OPENCV例程2 :CANNY運算元邊緣檢測OpenCV
- 【OpenCV】影像的變換(三)-Canny邊緣檢測OpenCV
- 什麼樣的演算法可以用Spark重寫演算法Spark
- 分析函式改寫自關聯函式
- MySQL LIMIT 如何改寫成Oracle limitMySqlMITOracle
- MySQL In不能用內連線改寫MySql
- oracle sql tunning 15 --常用改寫OracleSQL
- OpenCV(iOS)的邊緣檢測和Canny運算元OpenCViOS
- 用Python寫演算法 | 蓄水池演算法實現隨機抽樣Python演算法隨機
- 用 Golang 寫一個搜尋引擎(0x09)— 資料增,刪,改Golang
- 用 go 寫的五子棋預測演算法Go演算法
- 負載均衡演算法需要改進負載演算法
- 免費教你寫增刪改查介面
- Oculus Quest 2如何改寫遊戲規則?遊戲
- SQL 改寫系列七:謂詞移動SQL
- SQL改寫的方法,select group by sumSQL
- SQL 改寫系列六:謂詞推導SQL