名前空間とクラスの宣言

日記放置してたけど暇になったし、せっかくだからメモ代わりに書いとこう。
c++で、名前空間とクラスの宣言についてちょっと嵌った。

class piyo::fuga;

じゃダメで、

namespace piyo{ class fuga; }

で宣言すりゃokって話なんだけど。
なんのこっちゃ、と思うだろうがとりあえず以下にプログラムを。


fuga.h

#include <iostream>

namespace piyo{

class fuga{
public:
	void print(){ std::cout << "fuga" << std::endl; }
};

}



hoge.h

namespace piyo{ class fuga; }
//class piyo::fuga;		//こっちはだめ

class hoge{
public:
	hoge(piyo::fuga *f){ mf=f; }
	piyo::fuga *mf;
};



main.cpp

#include "hoge.h"
#include "fuga.h"
#include <iostream>
using namespace std;
using namespace piyo;

int main(int argc, char **argv)
{
	fuga f;
	hoge h(&f);
	h.mf->print();

	char c;
	cin >> c;
	return 0;
}



hoge.hが話のキモ。
コメントにもあるようにclass piyo::fuga;での宣言はコンパイルが通らない。
具体的には、namespace piyo{ class fuga; }をコメントアウトして、class piyo::fuga;でコンパイルしようとすると、
hoge.h(2): error C2653: 'piyo' : 識別子がクラス名でも名前空間名でもありません。
というエラーをくらう。(Microsoft Visual C++ 2010 Express の場合)
とりあえずnamespaceで囲う感じで宣言すりゃおk、ということは分かった。
こんな感じで使えるんじゃねーの?と思ってたが、実は違うのだ。というのが話のオチ。

OpenCVのラプラシアンフィルタ

OpenCVラプラシアンフィルタがずぅーっと謎だった。
ラプラシアンフィルタでよく見かけるのはこういうパターンのやつ。

0 1 0
1 -4 1
0 1 0

でも、これってcvLaplaceのデフォルトじゃないんだよね。
んじゃぁどんな値使ってんのー・・・と。
別段困ることもないので放置してましたが、ひょんな事から謎が解明。
まぁ、ひょんな事ってか、OpenCVのソース読んだんですが。
んで、確かめた結果こんな具合のフィルタ使ってた。

2 0 2
0 -8 0
2 0 2

はっはぁ〜ん。なるほどなるほど。
斜め方向で差分をとってあげるのですな。ふぅむ。
というわけで実装して確かめてみた。
結果がこちら。ついでにソベルフィルタも作ってみた。


元画像

自作ソベル

OpenCVソベル

自作ラプラシアン

OpenCVラプラシアン

わぁーい。大体似たような感じになったよぉー。
というわけでラプラシアンフィルタの謎がとけたのでした。
めでたしめでたし。

プログラムはこちら

/******************************************************************************
*
* Laplacianフィルタを実装してみた。
* ついでにSobelも。
*
******************************************************************************/
#include <cv.h>
#include <highgui.h>
#include <math.h>
#include <stdio.h>


//フィルターの中心点(x,y)を指定し、フィルターをかけた値を計算
unsigned char filtResult(const IplImage *img, int x, int y, const int filt[3][3])
{
	int result = 0;
	x--;	//フィルターの左上から計算する
	y--;
	//フィルターをかける
	for(int i=0; i<3; i++){		//y軸
		int yyy = y+i;
		if(yyy<0 || yyy>=img->width){
			continue;		//範囲チェックして範囲外なら次のステップへ
		}
		for(int j=0; j<3; j++){	//x軸
			int xxx = x+j;
			if(xxx<0 || xxx>=img->width){
				continue;	//範囲チェックして範囲外なら次のステップへ
			}
			result += filt[i][j] * CV_IMAGE_ELEM(img, uchar, yyy, xxx);
		}
	}
	//修正して結果を返す
	if(result < 0){ result = -result; }	//絶対値を取る
	if(result > 255){ result = 255; }	//大きすぎる値は255にしておく
	return static_cast<unsigned char>(result);
}


//srcにフィルターをかけたものがdestに入る src!=destとしないと結果がおかしなことになるので注意
void filteringImage(const IplImage *src, IplImage *dest, const int filt[3][3])
{
	for(int y=0; y<src->height; y++){
		for(int x=0; x<src->width; x++){
			CV_IMAGE_ELEM(dest, uchar, y, x) = filtResult(src, x, y, filt);
		}
	}
}


//imgにラプラシアンフィルタをかける
void myLaplacian(IplImage *img)
{
	//2次微分フィルタ
	int filt[3][3] = {
		{2, 0, 2},
		{0,-8, 0},
		{2, 0, 2}
	};

	IplImage *lap = cvCloneImage(img);
	filteringImage(img, lap, filt);
	
	cvCopy(lap, img);
	cvReleaseImage(&lap);
}


//imgにソベルフィルタをかける
void mySobel(IplImage *img)
{
	//x方向微分
	int xfilt[3][3] = {
		{-1, 0, 1},
		{-2, 0, 2},
		{-1, 0, 1}
	};
	//y方向微分
	int yfilt[3][3] = {
		{-1,-2,-1},
		{ 0, 0, 0},
		{ 1, 2, 1}
	};

	IplImage *xsobel = cvCloneImage(img);
	IplImage *ysobel = cvCloneImage(img);

	filteringImage(img, xsobel, xfilt);
	filteringImage(img, ysobel, yfilt);

	//x,y方向微分画像を統合
	for(int y=0; y<img->height; y++){
		for(int x=0; x<img->width; x++){
			int xv = CV_IMAGE_ELEM(xsobel, uchar, y, x);
			int yv = CV_IMAGE_ELEM(ysobel, uchar, y, x);
			int val = sqrt(xv*xv + yv*yv);
			if(val > 255){val=255;}         //オーバーフロー防止
			CV_IMAGE_ELEM(img, uchar, y, x) = val;
		}
	}

	cvReleaseImage(&xsobel);
	cvReleaseImage(&ysobel);
}




//main
int main(int argc, char **argv)
{
	if(argc < 2){
		printf("%s file.jpg\n", argv[0]);
		return 0;
	}

	IplImage *srcimg, *mylap, *cvlap, *mysobel, *cvsobel, *tmp;
	IplImage **pimgArray[] = {&srcimg, &mylap, &cvlap, &mysobel, &cvsobel};
	char *windowName[] = {"srcimg", "mylap", "cvlap", "mysobel", "cvsobel"};

	srcimg = cvLoadImage(argv[1], CV_LOAD_IMAGE_GRAYSCALE);
	if(!srcimg){ perror(argv[1]); exit(1); }

	//自作laplacian,sobelを使う
	mylap = cvCloneImage(srcimg);
	myLaplacian(mylap);
	mysobel = cvCloneImage(srcimg);
	mySobel(mysobel);

	//tmpはオーバーフロー防止のために使われる
	tmp     = cvCreateImage(cvGetSize(srcimg), IPL_DEPTH_16S, 1);
	cvlap   = cvCreateImage(cvGetSize(srcimg), IPL_DEPTH_8U, 1);
	cvsobel = cvCreateImage(cvGetSize(srcimg), IPL_DEPTH_8U, 1);
	//laplacian
	cvLaplace(srcimg, tmp);
	cvConvertScaleAbs(tmp, cvlap);
	//sobel
	cvSobel(srcimg, tmp, 1, 0);
	cvConvertScaleAbs(tmp, cvsobel);
	cvSobel(srcimg, tmp, 0, 1);
	//x,y方向微分画像を統合
	for(int y=0; y<cvsobel->height; y++){
		for(int x=0; x<cvsobel->width; x++){
			int xval = CV_IMAGE_ELEM(cvsobel, uchar, y, x);
			int yval = CV_IMAGE_ELEM(tmp, short, y, x);
			int val = sqrt(xval*xval + yval*yval);
			CV_IMAGE_ELEM(tmp, short, y, x) = val;
		}
	}
	cvConvertScaleAbs(tmp, cvsobel);
	//tmpはいらないので後始末する
	cvReleaseImage(&tmp);

	int max = sizeof(windowName)/sizeof(windowName[0]);
	for(int i=0; i<max; i++){
		cvNamedWindow(windowName[i]);
		cvShowImage(windowName[i], *pimgArray[i]);
	}
	cvWaitKey(0);
	for(int i=0; i<max; i++){
		cvDestroyWindow(windowName[i]);
		cvReleaseImage(pimgArray[i]);
	}

	return 0;
}