Qt/Gcc
2012.08.16作成
2-1&2.cfftdatacreate2d、cdatacreate2dクラス
この2つのクラスは二次元配列を動的に確保するクラスです。そして、 画像を読み込んで、読み込んだ画像を格納するクラスを作成します。
これは、C++ならばVectorを使うのがよいと思いますが、newで確保して使用します。
画像処理なので、1次元配列でも問題ありませんが、ソースコードの読みやすさ等を考慮して二次元配列としてアクセスできるようにしています。
以下、4つのファイルを作成してクラスを定義します。
CDataCreate2D.h
CDataCreate2D.cpp
Cfftdatacreate2d.h
Cfftdatacreate2d.cpp
●CdataCreate2Dクラス
★CDataCreate2D.h
以下のように記載します。
#ifndef CDATACREATE2D_H
#define CDATACREATE2D_H
#include
<QObject>
#include <qimage.h>
class
CDataCreate2D
{
public:
CDataCreate2D(int hsize,int
vsize);
CDataCreate2D(int lhsize,int lvsize,unsigned char
value);
~CDataCreate2D();
unsigned char **Rdata;//Red用データのポインタのポインタ
unsigned
char **Gdata;//Green用データのポインタのポインタ
unsigned char
**Bdata;//Blue用データのポインタのポインタ
int
hsize;
int vsize;
QImage
outImage;
//QImageから作業用配列にデータをコピー
void mImage2data(QImage
*linImage);//ポインタ渡し
void mImage2data(QImage
linImage);//参照渡し
void mdata2Image(QImage
*linImage);//作業用配列からQImageにコピーする
};
#endif // CDATACREATE2D_H
コンストラクタを定義します。これは、以下の2つを多重継承します。それぞれ、画像の画素数を引数として、片方は初期化のときに
配列を0で初期化するかvalurで与えた値で初期化するかを分けています。
CDataCreate2D(int hsize,int
vsize);//画像の水平、垂直の画素数を引数とする。初期値は0クリア
CDataCreate2D(int
lhsize,int lvsize,unsigned char
value);//画像の水平、垂直の画素数を引数とする。初期化の値はvalueで初期化
ポインタの配列を用意して2次元配列を確保するための変数を定義します。
unsigned char
**Rdata;//Red用データのポインタのポインタ
unsigned char
**Gdata;//Green用データのポインタのポインタ
unsigned char
**Bdata;//Blue用データのポインタのポインタ
このクラスでは、読み込んだ画像データである、QImageから配列へのコピー用のメンバー関数を定義します。
以下のように、ポインタ渡しと参照渡し用のメンバー関数を多重定義します。
//QImageから作業用配列にデータをコピー
void mImage2data(QImage
*linImage);//ポインタ渡し
void mImage2data(QImage
linImage);//参照渡し
void mdata2Image(QImage *linImage);//作業用配列からQImageにコピーする
★CDataCreate2D.cpp
このクラスの実装を以下に記載します。
・コンストラクタ
CDataCreate2D::CDataCreate2D(int
lhsize,int lvsize)
{
/*****二次元配列の確保*/
Rdata=NULL;
hsize=lhsize;
vsize=lvsize;
Rdata = (unsigned char
**)new unsigned char *[hsize];
Gdata = (unsigned char
**)new unsigned char *[hsize];
Bdata = (unsigned char
**)new unsigned char *[hsize];
for(int
k=0;k<hsize;k++){
Rdata[k] = (unsigned char *)new
unsigned char[vsize];
Gdata[k] =
(unsigned char *)new unsigned
char[vsize];
Bdata[k] = (unsigned
char *)new unsigned char[vsize];
}
//上記の場合は、以下のような配列アクセスが可能
for(int
loop_y=0; loop_y<vsize;
loop_y++){
for(int loop_x=0; loop_x<hsize;
loop_x++){
Rdata[loop_x][loop_y] =(unsigned
char)0;
Gdata[loop_x][loop_y] =(unsigned
char)0;
Bdata[loop_x][loop_y] =(unsigned
char)0;
}
}
/*上記の場合は、以下のような配列アクセスが可能
for(int loop_y=0;
loop_y<vsize; loop_y++){
for(int loop_x=0; loop_x<hsize;
loop_x++){
r = qRed(lImage->pixel(loop_x,
loop_y));
g = qGreen(lImage->pixel(loop_x,
loop_y));
b = qBlue(lImage->pixel(loop_x, loop_y));
Rdata[loop_x][loop_y] =(unsigned
char)r;
Gdata[loop_x][loop_y] =(unsigned
char)g;
Bdata[loop_x][loop_y] =(unsigned
char)b;
}
}
*/
}
二次元配列は以下の配列を確保するようにします。
Rdata[loop_x][loop_y]
この配列を確保する考え方は、一次元配列を確保して、その配列のポインタ(アドレス)を格納する
配列を作る、という形です。
まずは、ポインタを確保するための配列を確保します。
Rdata = (unsigned char **)new unsigned char *[hsize];//水平方向の画素数
そして、格納したポインタそれぞれに対して、実態のある配列を確保(代入:結びつける)します。
for(int
k=0;k<hsize;k++)
Rdata[k] =
(unsigned char *)new unsigned char[vsize];
これを、必要な配列数確保します。この場合はR、G、Bの各データを確保するために
3つ用意します。
この二次元配列へのアクセスは例えば以下のようなループとなります。
for(int loop_y=0; loop_y<vsize;
loop_y++){
for(int loop_x=0; loop_x<hsize;
loop_x++){
r = qRed(lImage->pixel(loop_x,
loop_y));//赤色のデータ取得
Rdata[loop_x][loop_y]
=(unsigned char)r;//赤色のデータを二次元配列に入力する
}
}
・デストラクタ
ここは、コンストラクタで確保したメモリの開放を実施しています。
以下のように、実態のある配列のポインタ先にデリートして、その後、ポインタの配列を
デリートします。
CDataCreate2D::~CDataCreate2D()
{
/*****二次元配列の破棄*/
if(Rdata!=NULL){
for(int
k=0;k<hsize;k++){
delete
Rdata[k];
delete
Gdata[k];
delete Bdata[k];
}
delete
Rdata;
delete
Gdata;
delete
Bdata;
}
}
・QImageからデータ配列にコピーするmImage2data(QImage
*linImage)、mImage2data(QImage
linImage)
QImageクラスを使って、画像を読み込みました。このクラスのままデータ処理できますが、後の処理を
楽にするのと、画像処理は汎用の関数(既刊の書籍など記載)を使用したいため、コンストラクタで作成した
配列にQImageのデータをコピーします。
以下にこのメンバー関数の実装を示します。
//linImage:画像データが確保されているQImageクラスの変数
void
CDataCreate2D::mImage2data(QImage *linImage)
{
int
r,g,b;
for(int loop_y=0; loop_y<vsize;
loop_y++){
for(int loop_x=0;
loop_x<hsize;
loop_x++){
r = qRed(linImage->pixel(loop_x,
loop_y));//ピクセルからRedデータを取り出す。
g = qGreen(linImage->pixel(loop_x,
loop_y));//ピクセルからGreenデータを取り出す。
b = qBlue(linImage->pixel(loop_x, loop_y));//ピクセルからBlueデータを取り出す。
Rdata[loop_x][loop_y] =(unsigned
char)r;
Gdata[loop_x][loop_y] =(unsigned
char)g;
Bdata[loop_x][loop_y] =(unsigned
char)b;
}
}
}
ここでは、qRedなどの関数を使って、QImageのpixel()メンバー関数の戻り値から各ピクセルのRGB値を取り出して、各配列に確保しています。
もう一つの関数、CDataCreate2D::mImage2data(QImage
linImage)は、各ピクセルへのアクセスを実体で実施しているだけであとは、
上記と同じです。
・データ配列からQImageにデータをセットするmdata2Image(QImage
*linImage)関数
確保した配列のデータを使って、なんらか画像処理を実施して、その結果をQImageにセットします。
このやり取りによって、処理されたデータQImageで扱えるため、ファイルの保存、画像表示などが
可能になります。
以下にこのメンバー関数の実装を示します。
void CDataCreate2D::mdata2Image(QImage *linImage)
{
unsigned int
r,g,b;
int loop_y;
int
loop_x;
for(loop_y=0; loop_y<vsize;
loop_y++){
for(loop_x=0;
loop_x<hsize;
loop_x++){
r=Rdata[loop_x][loop_y];
g=Gdata[loop_x][loop_y];
b=Bdata[loop_x][loop_y];
//Gray scale? or
color
if(linImage->allGray()){
//lImage->isGrayscale()
linImage->setPixel(loop_x,
loop_y,qGray(r,g,b));//グレースケールの場合
}
else{
linImage->setPixel(loop_x,
loop_y,qRgb(r,g,b));//カラー画像の場合
}
}
}
}
ここでは、「if(linImage->allGray())」のように読み込んだ画像がグレースケールだったかどうかで処理を変更していますが、
この関数を呼び出す前に処理(新たに作成したQImageのRGB値に同じ値を書き込んでおく)をしておけば不要です。
値を配列にとりこんだときと逆に、配列の値をQImageクラスのメンバー関数であるsetPixel関数を用いて、QImageにセットします。
●cfftdatacreate2dクラス
このクラスはCDataCreate2Dクラスからの派生クラスです。CDataCreate2Dクラスでは、unsigned
charの配列確保のみをしていましたが、
画像処理で2次元FFT(ファーストフーリエトランスフォーム)を実施します。そのため、double型(フーリエ変換の虚数部と実部)の配列を
用意します。
考え方はCDataCreate2Dと同じです。
★cfftdatacreate2d.h
#ifndef
CFFTDATACREATE2D_H
#define CFFTDATACREATE2D_H
#include
"cdatacreate2d.h"
class Cfftdatacreate2d : public
CDataCreate2D//親クラスを継承する
{
public:
Cfftdatacreate2d(int lhsize,int lvsize);
Cfftdatacreate2d(int lhsize,int lvsize,unsigned char
lvalue);//親クラスのCDataCreate2Dをlvalueで初期化するときのコンストラクタ
~Cfftdatacreate2d();
double
**Rar;//画像のRデータFFT結果の実部
double
**Rai;//画像のRデータFFT結果の虚数部
double
**Gar;
double **Gai;
double
**Bar;
double **Bai;
int
fhsize;//FFT結果後の水平方向データサイズ
int
fvsize;//FFT結果後の垂直方向データサイズ
};
★cfftdatacreate2d.cpp
cfftdatacreate2dクラスの実体です。
コンストラクタで水平方向と垂直方向のサイズを受け取り、実部と虚数部の配列をRGB毎に確保しています。
水平方向と垂直方向のサイズは、基本的には画像のサイズと同じですので、親クラスであるCDataCreate2Dクラスに
渡します。
#endif // CFFTDATACREATE2D_H
#include "cfftdatacreate2d.h"
#include
"cdatacreate2d.h"
//コンストラクタ1:初期化は各クラスまかせ
//lhsize:データ(画像)の水平方向サイズ(画素数)
//lvsize:データ(画像)の水平方向サイズ(画素数)
Cfftdatacreate2d::Cfftdatacreate2d(int
lhsize,int lvsize)
:
CDataCreate2D(lhsize,lvsize)
{
fhsize =
lhsize;
fvsize = lvsize;
Rar = NULL;
Rar = (double **)new double
*[fhsize];
Gar = (double **)new double
*[fhsize];
Bar = (double **)new double
*[fhsize];
Rai = (double **)new double
*[fhsize];
Gai = (double **)new double
*[fhsize];
Bai = (double **)new double
*[fhsize];
for(int
k=0;k<fhsize;k++){
Rar[k] =
(double *)new double[fvsize];
Gar[k] = (double *)new
double[fvsize];
Bar[k] = (double
*)new double[fvsize];
Rai[k] = (double *)new
double[fvsize];
Gai[k] = (double
*)new double[fvsize];
Bai[k] =
(double *)new double[fvsize];
}
//念のため0.0で初期化
for(int loop_y=0; loop_y<vsize;
loop_y++){
for(int loop_x=0;
loop_x<hsize;
loop_x++){
Rar[loop_x][loop_y]
=(double)0.0;
Gar[loop_x][loop_y]
=(double)0.0;
Bar[loop_x][loop_y]
=(double)0.0;
Rai[loop_x][loop_y]
=(double)0.0;
Gai[loop_x][loop_y]
=(double)0.0;
Bai[loop_x][loop_y] =(double)0.0;
}
}
}
//コンストラクタ2:任意の数字で親クラスを初期化
//lhsize:データ(画像)の水平方向サイズ(画素数)
//lvsize:データ(画像)の水平方向サイズ(画素数)
Cfftdatacreate2d::Cfftdatacreate2d(int
lhsize,int lvsize,unsigned char lvalue)
:
CDataCreate2D(lhsize,lvsize,lvalue)
{
fhsize =
lhsize;
fvsize = lvsize;
Rar = NULL;
Rar = (double **)new double
*[fhsize];
Gar = (double **)new double
*[fhsize];
Bar = (double **)new double
*[fhsize];
Rai = (double **)new double
*[fhsize];
Gai = (double **)new double
*[fhsize];
Bai = (double **)new double
*[fhsize];
for(int
k=0;k<fhsize;k++){
Rar[k] =
(double *)new double[fvsize];
Gar[k] = (double *)new
double[fvsize];
Bar[k] = (double
*)new double[fvsize];
Rai[k] = (double *)new
double[fvsize];
Gai[k] = (double
*)new double[fvsize];
Bai[k] =
(double *)new double[fvsize];
}
for(int loop_y=0;
loop_y<vsize; loop_y++){
for(int loop_x=0; loop_x<hsize;
loop_x++){
Rar[loop_x][loop_y]
=(double)0.0;
Gar[loop_x][loop_y]
=(double)0.0;
Bar[loop_x][loop_y]
=(double)0.0;
Rai[loop_x][loop_y]
=(double)0.0;
Gai[loop_x][loop_y]
=(double)0.0;
Bai[loop_x][loop_y] =(double)0.0;
}
}
}
Cfftdatacreate2d::~Cfftdatacreate2d()
{
if(Rar!=NULL){//配列が確保されているときは、各配列を開放する。
for(int
k=0;k<fhsize;k++){
delete
Rar[k];
delete
Gar[k];
delete Bar[k];
delete
Rai[k];
delete
Gai[k];
delete Bai[k];
}
delete
Rar;
delete
Gar;
delete Bar;
delete Rai;
delete
Gai;
delete
Bai;
}
}