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;
    }

}


インデックスに戻る
トップページに戻る