完全に実力不足な理系大学生からの成長ブログ

プログラミング能力皆無、でも頑張ります。

OpenCV(C++)で自作二次元フィルタと正規化(0~255)

今回は教授から突然2次元フィルタかけてって言われたのでかけましたってだけの話です笑
メモ程度です.

OpenCVってfilter2Dって関数があるからそこにぶっこんでおけば
ガウシアンフィルターとかメディアンフィルターとか簡単にかけれちゃいますよね.
でも自分で作ったフィルターかけるにはどうすればいいの?
って時のためのメモです.

ていうか今回はあえてfilter2Dを使わないでやったんですけど,
本当はfilter2Dを使った方が早いです.
下記のサイトを参考にしてください.
画像フィルタリング — opencv v2.1 documentation

おそらくfilter2Dだとフーリエ変換して周波数空間でフィルターかけてるんだと思います.
よくわからないですけど笑

とにかく今回は画像に直接フィルターをかけます.

早速ソースコードから.

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <math.h>
#include <fstream>
#include <string>
#include <sstream>

using namespace cv;
using namespace std;

//0~255に正規化
Mat normalize(Mat in) {

    //最大と最小の初期値を設定(minが適当なのはやばそう)
    double max = 0;
    double min = 9999;

    //めんどくさいから入力画像をclone
    Mat out = in.clone();

    //最大と最小を取得
    for(int j = 0; j < in.rows; j++){
        for(int i = 0; i < in.cols; i++){
            if(max < in.at<double>(j,i)){
                max = in.at<double>(j,i);
            }
            if(min > in.at<double>(j,i)){
                min = in.at<double>(j,i);
            }
        }
    }
    
    //取得した最大値と最小値で正規化
    for(int j = 0; j < in.rows; j++){
        for(int i = 0; i < in.cols; i++){
            out.at<double>(j,i) = in.at<double>(j,i) * (max - min)/ 255;
        }
    }
  //0~255になったMatを返す
  return in;
}

int main(int argc, char *argv[]){

    //画像入力
    Mat inputimg = imread("01_Lena_syukukaku.bmp_img_6.bmp",0);
    
    //入力画像のコピー
    Mat img = inputimg.clone();
    
    //Matの型をdouble型に変換
    img.convertTo(img,CV_64F);

    //出力用のMat配列を用意
    Mat out = Mat::zeros(inputimg.rows, inputimg.cols, CV_64F);

    //自作二次元フィルター1
    Mat filter1 = (Mat_<double>(3,3) << -0.0625, 0.125, -0.0625,
                                        0.125, 0.75, 0.125,
                                        -0.0625, 0.125, -0.0625);

    //自作二次元フィルター2
    Mat filter2 = (Mat_<double>(3,3) << 0, 0.125, 0,
                                        0.125, 0.5, 0.125,
                                        0, 0.125, 0);

    //画像サイズ分のforループ
    for(int j = 0; j < inputimg.rows; j++){
        for(int i = 0; i < inputimg.cols; i++){
            if(j == 0 || i == 0 || j == inputimg.rows-1 || i == inputimg.cols-1){
                out.at<double>(j,i) = img.at<double>(j,i);
                continue;
                }
            //フィルターサイズ文のforループ
            for(int l = 0; l < filter1.rows; l++){
                for(int k = 0; k < filter1.cols; k++){
                    out.at<double>(j,i) += img.at<double>(j+l-1,i+k-1) * filter2.at<double>(l,k);
                }
            }
        }
    }


    //出力用Mat
    Mat outputimg;
    
    //正規化(上記参照)
    outputimg = normalize(out);
    
    //Matの型変換
    outputimg.convertTo(outputimg,CV_8U);

    //出力
    imwrite("01_Lena_filt2.bmp",outputimg);
    
    //表示
    imshow("out",outputimg);
    //waitKey(0);

    return 0;
}

ポイントは1画素ずつアクセスするところ.
.atを使ってます.
OpenCVは画素単位のアクセスが非常にめんどくさい.
頑張って書きましょう笑

あとはMatの型に注意しなきゃいけないってことくらいですかね.

以上です.