影象卷積操作的手動實現(基於opencv的C++編譯環境)
opencv環境下有自帶的filter2D()函式可以實現影象的卷積,自己寫一個卷積函式函式貌似是沒事找事。。。。好吧,事實是這是我們計算機視覺課程上的一項作業。我們很多演算法過程僅僅只呼叫別人寫好的介面,即使原理我們已經清楚,但是真正編寫程式碼的時候很多細節我們可能還是沒有意識到,也許自己再實現一遍是一種深入學習的途徑吧。
OK,先上程式碼為敬
這裡定義了一個My_Convolution的類,以下是標頭檔案和原始檔
#ifndef MY_CONVOLUTION #define MY_CONVOLUTION #include <opencv2/opencv.hpp> class My_Convolution{ public: My_Convolution(); ~My_Convolution(); bool load_kernal(cv::Mat kernal);//載入卷積核 void convolute(const cv::Mat &image,cv::Mat &dst);//卷積操作 private: bool kernal_loaded;//是否已經載入卷積核 cv::Mat curr_kernal;//當前卷積核 int bios_x,bios_y;//記錄偏移量 void compute_sum_of_product(int i,int j,int chan,cv::Mat &complete_image,cv::Mat & dst);//計算每一個畫素的掩模乘積之和 void complete_image_transform(const cv::Mat &image,cv::Mat &dst);//將原影象轉換成邊框補全的影象 }; #endif // MY_CONVOLUTION
#include "my_convolution.h" using namespace std; using namespace cv; My_Convolution::My_Convolution(){ kernal_loaded=false; } My_Convolution::~My_Convolution(){} //載入卷積核 bool My_Convolution::load_kernal(Mat kernal){ if(kernal.cols%2==1&&kernal.rows%2==1){ curr_kernal=kernal.clone(); bios_x=(kernal.cols-1)/2; bios_y=(kernal.rows-1)/2; kernal_loaded=true; return true; } else{ cout<<"The size of kernal is not suitable!"<<endl; return false; } } //卷積操作 void My_Convolution::convolute(const Mat &image, Mat &dst){ if(!kernal_loaded){ cout<<"kernal is empty!Please load the kernal first!"<<endl;return; } Mat complete_image; complete_image_transform(image,complete_image); dst=Mat::zeros(image.rows,image.cols,image.type()); int channels=image.channels();//獲取影象的通道數 if(channels==3){ for(int chan=0;chan<channels;chan++){ for(int i=0;i<dst.rows;i++){ for(int j=0;j<dst.cols;j++){ compute_sum_of_product(i,j,chan,complete_image,dst); } } } return ; } if(channels==1){ for(int i=0;i<dst.rows;i++){ for(int j=0;j<dst.cols;j++){ compute_sum_of_product(i,j,0,complete_image,dst); } } } } //計算掩模乘積之和 void My_Convolution::compute_sum_of_product(int i, int j,int chan,Mat &complete_image, Mat &dst){ if(complete_image.channels()==3){ float sum=0; int bios_rows=i; int bios_cols=j; for(int curr_rows=0;curr_rows<curr_kernal.rows;curr_rows++){ for(int curr_cols=0;curr_cols<curr_kernal.cols;curr_cols++){ float a=curr_kernal.at<float>(curr_rows,curr_cols)*complete_image.at<Vec3b>(curr_rows+bios_rows,curr_cols+bios_cols)[chan]; sum+=a; } } dst.at<Vec3b>(i,j)[chan]=(int)sum; } else{ if(complete_image.channels()==1){ float sum=0; int bios_rows=i; int bios_cols=j; for(int curr_rows=0;curr_rows<curr_kernal.rows;curr_rows++){ for(int curr_cols=0;curr_cols<curr_kernal.cols;curr_cols++){ float a=curr_kernal.at<float>(curr_rows,curr_cols)*complete_image.at<uchar>(curr_rows+bios_rows,curr_cols+bios_cols); sum+=a; } } dst.at<uchar>(i,j)=(int)sum; } else{ cout<<"the type of image is not suitable!"<<endl;return ; } } } //邊框畫素補全 void My_Convolution::complete_image_transform(const Mat &image,Mat &dst){ if(!kernal_loaded){ cout<<"kernal is empty!"<<endl; return ; } dst=Mat::zeros(2*bios_y+image.rows,2*bios_x+image.cols,image.type());//初始化一個補全影象的大小。 Rect real_roi_of_image=Rect(bios_x,bios_y,image.cols,image.rows); Mat real_mat_of_image=dst(real_roi_of_image); image.copyTo(dst(real_roi_of_image)); }
My_Convolution類對外提供了load_kernal()和convolute()兩個函式介面,分別用來載入卷積核和進行卷機操作。若在convolute操作之前沒有載入卷積核,則會給出提醒。
當卷積核以邊緣畫素為基準點進行卷積操作的時候,會出現卷積核內部分的值無法在原影象上找到相互對應的畫素點,因為原圖必然有邊界。這裡採取的辦法是,在載入卷積核之後,獲取卷積核的尺寸,根據卷積核的尺寸適當地在原影象的邊緣增加bios_x列和bios_y行的畫素,因此補全之後帶有原圖資訊的新的影象的尺寸為(2*bios_x+image.cols)*(2*bios_y+image.rows)。
得到補全的影象之後,使用一個2層的for迴圈來對原影象進行卷積操作,新產生的影象的尺寸要和原影象保持一致。
main函式裡的測試程式碼:
#include <iostream>
#include <opencv2/opencv.hpp>
#include "my_convolution.h"
using namespace std;
using namespace cv;
//高斯核建構函式
Mat Gaussian_kernal(int kernal_size, int sigma)
{
const double PI = 3.14159265358979323846;
int m = kernal_size / 2;
Mat kernal(kernal_size, kernal_size, CV_32FC1);
float s = 2 * sigma*sigma;
for (int i = 0; i < kernal_size; i++)
{
for (int j = 0; j < kernal_size; j++)
{
int x = i - m, y=j - m;
kernal.ptr<float>(i)[j] = exp(-(x*x + y*y) / s) / (PI*s);
}
}
return kernal;
}
//3*3均值卷積核
cv::Mat average_kernal_3 = (Mat_<float>(3,3) << 0.111, 0.111 ,0.111,
0.111, 0.111, 0.111,
0.111, 0.111, 0.111);
//5*5均值卷積核
cv::Mat average_kernal_5 = (Mat_<float>(3,3) << 0.04, 0.04 ,0.04, 0.04, 0.04,
0.04, 0.04 ,0.04, 0.04, 0.04,
0.04, 0.04 ,0.04, 0.04, 0.04,
0.04, 0.04 ,0.04, 0.04, 0.04,
0.04, 0.04 ,0.04, 0.04, 0.04);
//sobel邊緣檢測運算元
cv::Mat sobel_y_kernal= (Mat_<float>(3,3) << -1, -2 ,-1,
0, 0 , 0,
1, 2 , 1);
cv::Mat sobel_x_kernal= (Mat_<float>(3,3) << -1, 0 , 1,
-2, 0 , 2,
-1, 0 , 1);
//prewitt邊緣檢測運算元
cv::Mat prewitt_y_kernal= (Mat_<float>(3,3) << -1, -1 ,-1,
0, 0 , 0,
1, 1 , 1);
cv::Mat prewitt_x_kernal= (Mat_<float>(3,3) << -1, 0 , 1,
-1, 0 , 1,
-1, 0 , 1);
int main(){
My_Convolution myconvolution;
Mat image=imread("lena.jpg");
imshow("src",image);
Mat dst_prewitt;
//高斯卷積
Mat dst_gaussian;
myconvolution.load_kernal(Gaussian_kernal(7,2));
myconvolution.convolute(image,dst_gaussian);
imshow("dst_gaussian",dst_gaussian);
//均值3*3
Mat dst_aver_3;
myconvolution.load_kernal(average_kernal_3);
myconvolution.convolute(image,dst_aver_3);
imshow("dst_aver_3",dst_aver_3);
//均值5*5
Mat dst_aver_5;
myconvolution.load_kernal(average_kernal_5);
myconvolution.convolute(image,dst_aver_5);
imshow("dst_aver_5",dst_aver_5);
//sobel操作
Mat dst_sobel_x;
Mat dst_sobel_y;
myconvolution.load_kernal(sobel_x_kernal);
myconvolution.convolute(image,dst_sobel_x);
imshow("dst_sobel_x",dst_sobel_x);
myconvolution.load_kernal(sobel_y_kernal);
myconvolution.convolute(image,dst_sobel_y);
imshow("dst_sobel_y",dst_sobel_y);
//prewitt操作
Mat dst_prewitt_x;
Mat dst_prewitt_y;
myconvolution.load_kernal(prewitt_x_kernal);
myconvolution.convolute(image,dst_prewitt_x);
imshow("dst_prewitt_x",dst_prewitt_x);
myconvolution.load_kernal(prewitt_y_kernal);
myconvolution.convolute(image,dst_prewitt_y);
imshow("dst_prewitt_y",dst_prewitt_y);
waitKey(0);
return 0;
}
main函式裡分別對兩個尺寸的均值卷積和高斯卷積進行了測試,高斯卷積的尺寸和其中的引數可以使用Guassian_kernal()函式生成。這裡因為程式中只給出了sobel和prewitt兩個方向上的卷積核並只分別單純地做了卷積運算,並沒有根據sobel和prewitt運算元的完整計算過程算出其卷積影象,如果想知道sobel邊緣檢測和prewitt邊緣檢測是什麼效果的朋友可以自己試一試。
最後給出效果圖:
src原圖
dst_guassian——高斯
dst_aver_3——3*3均值
dst_aver_5——5*5均值
dst_prewitt_x——x方向prewitt運算元
dst_prewitt_y——y方向prewitt運算元
dst_sobel_x——x方向sobel運算元
dst_sobel_y——y方向sobel運算元
相關推薦
【python實現卷積神經網路】卷積層Conv2D實現(帶stride、padding)
關於卷積操作是如何進行的就不必多說了,結合程式碼一步一步來看卷積層是怎麼實現的。 程式碼來源:https://github.com/eriklindernoren/ML-From-Scratch 先看一下其基本的元件函式,首先是determine_padding(filter_shape, ou
影象卷積操作的手動實現(基於opencv的C++編譯環境)
opencv環境下有自帶的filter2D()函式可以實現影象的卷積,自己寫一個卷積函式函式貌似是沒事找事。。。。好吧,事實是這是我們計算機視覺課程上的一項作業。我們很多演算法過程僅僅只呼叫別人寫好的介面,即使原理我們已經清楚,但是真正編寫程式碼的時候很多細節
影象卷積的fft實現驗證(python)
1、Caffe的卷積操作時間主要在矩陣乘法,假設一個m*n卷積核,且輸入通道數為1,輸出特徵圖大小為h*w,則乘法個數m*n*h*w,這裡的優化僅限於對矩陣的乘法優化,因此,只要選擇適合的矩陣計算庫就
【深度學習入門】——親手實現影象卷積操作
深度學習中有一個很重要的概念就是卷積神經網路 CNN,卷積神經網路中又有卷積層、池化層的概念。尤其是卷積層,理解難度比較大,雖然書中或者是視訊中都有詳細介紹過它的基礎概念,但對於求知慾望很強烈的我,我總心裡癢癢的,總想親手實現,看看效果,怕的就是自己會眼高手低,
springcloud的Hystrix turbine斷路器聚合監控實現(基於springboot2.02版本)
events 單個 res source IE lease ans discover www 本文基於方誌朋先生的博客實現:https://blog.csdn.net/forezp/article/details/70233227 一、準本工作 1、工具:Idea,JD
DES加密演算法的java實現(基於java類庫)
嗯嗯........這個是我新開的部落格上的第一篇的文章,這裡小白希望自己的技術能夠一天比一天好(p≧w≦q),加油! 好吧,現在來一個基於java類庫的DES加密演算法的實現吧~網上不少的程式碼要不執行有問題,要不就是簡簡單單內建一個固定的加密字串就簡單完事了。好吧,我承認我現在是為懶人服務
JNI Demo實現(基於IEDA,VS2008)
一、簡介: JNI是Java Native Interface的縮寫,它提供了若干的API實現了Java和其他語言的通訊(主要是C&C++) 二、步驟概要: 編寫帶有native宣告的方法的java類 使用IDE或javac命令編譯所編寫的java類 使用jav
排序演算法之——優先佇列經典實現(基於二叉堆)
許多應用都需要處理有序的元素,但有時,我們不要求所有元素都有序,或是一定要一次就將它們排序,許多情況下,我們會收集這些元素裡的最大值或最小值。 這種情況下一個合適的資料結構應該支援兩種操作:插入元素、刪除最大元素。 優先佇列與棧和佇列類似,但它有自己的奇妙之處。 在本文中,會講解基於二叉堆的一種優先佇列
DCT變換及量化的c++實現(基於opencv矩陣運算)
由於DCT的數學原理不好描述,直接放程式碼了: #include<iostream> #include<fstream> #include<opencv2/core/core.hpp> #include<opencv2/highg
【特徵工程】一種異常值檢測方法、原理、程式碼實現 (基於箱線圖)
先介紹使用到的方法原理,也就是一種異常檢測的方法。 首先要先了解箱線圖。 箱線圖 箱線圖(Boxplot)也稱箱須圖(Box-whisker Plot),是利用資料中的五個統計量:最小值、第一四分位數、中位數、第三四分位數與最大值來描述資料的一種方法,它也可以
C++ 智慧指標的一個實現(基於模板和Shared_ptr)
自己實現了一個C++的智慧指標。 基於Shared_ptr來實現,支援預設構造,拷貝構造,移動構造, 引用計數器, 注意智慧指標中存放的指標地址一旦被一個智慧指標物件託管以後,不要再直接拿此地址來初始化其他物件,否則會引發多次洩漏的問題。(所以還是要特別小心) 如果要初始化
STM32CubeIDE下載安裝-GPIO基本配置操作-Debug除錯(基於CMSIS DAP Debug)
1、在ST官網下載STM32CubeIDE而不是STM32CubeMX,並且STM32CubeIDE是免費的。(STM32CubeIDE不支援中文路徑,不然編譯會出錯) 2、如果你用的是keil開發環境那麼 STM32CubeIDE =(STM32CubeMX + Keil)STM32CubeMX只是一個配置
手動實現卷積神經網路中的卷積操作(conv2d)
寫這個的原因:一來好像沒怎麼搜到別人手動實現,作為補充;二來鞏固一下基礎。 卷積操作示意 先從一張示意圖說起,卷積基礎概念和操作步驟就不囉嗦了,只講這張圖,大意就是,有in-channel,有out-channel,你需要把in-channel都做卷積操作,然
OpenCV--如何對影象進行卷積操作(9)
附程式碼如下: import cv2 as cv import numpy as np def blur1(): src = cv.imread("D:/matplotlib/0.jpg") cv.imshow("input",src) blur2 = cv.blur(
吳恩達作業9:卷積神經網路實現手勢數字的識別(基於tensorflow)
提供資料集程式碼放在cnn_utils.py裡。 import math import numpy as np import h5py import matplotlib.pyplot as plt import tensorflow as tf from tensorfl
caffe原始碼深入學習6:超級詳細的im2col繪圖解析,分析caffe卷積操作的底層實現
在先前的兩篇部落格中,筆者詳細解析了caffe卷積層的定義與實現,可是在conv_layer.cpp與base_conv_layer.cpp中,卷積操作的實現仍然被隱藏,通過im2col_cpu函式和caffe_cpu_gemm函式(後者實現矩陣乘法)實現,在此篇部落格中,筆者旨在向大家展示,caf
0024-利用OpenCV的filter2D函式作影象的卷積操作和協相關操作
影象的卷積操作是影象處理中最常用的操作之一,一般是用核算子來實現卷積操作。什麼叫核算子?請移步博文https://blog.csdn.net/lehuoziyuan/article/details/84101788 OpenCV用函式filter2D來實現對影象或矩陣的卷積操作。這個函式本質上做
影象卷積、相關以及在MATLAB中的操作
原文:http://www.cnblogs.com/zjutzz/p/5661543.html 影象卷積、相關以及在MATLAB中的操作 區分卷積和相關 影象處理中常常需要用一個濾波器做空間濾波操作。空間濾波操作有時候也被叫做卷積濾波,或者乾脆叫卷積(離散的卷積,不是微
續(利用tensorflow實現簡單的卷積神經網路-對程式碼中相關函式介紹)——遷移學習小記(三)
上篇文章對cnn進行了一些介紹,附了完整小例子程式碼,介紹了一部分函式概念,但是對我這樣的新手來說,程式碼中涉及的部分函式還是無法一下子全部理解。於是在本文中將對程式碼中使用的函式繼續進行一一介紹。 具體程式碼見上一篇(二) 一、 #定義輸入的placehoder,x是特徵
理解影象中卷積操作的含義
上文用生動的例子來解釋卷積記載了卷積的含義,現在就來看看卷積在影象處理中的應用吧。(ps:本文大部分內容系轉載大神的部落格,現在csdn強制圖片水印,實在感到很無奈!!!) 數字影象處理中卷積 數字影象是一個二維的離散訊號,對數字影象做卷積操作其實就是利用卷