1

バイキュービック画像補間を実装しようとしています。コードの関連するセクションのみを貼り付けました。画像をバッファにロードしたり、ピクセルからピクセルを読み取ったりするコードをスキップしました。計算が正しいと合理的に確信しています。ただし、出力にひどいアーティファクトが含まれているようです。

すべてのアクションは、resizeメソッドで発生します。

経験豊富なグラフィックプログラマーが、私が間違っている可能性があることについて彼らの勘を共有できるかもしれないことを望んでいます。

以下は、入力の幅と高さを2倍に変更したときに取得する入力画像と出力画像です。

入力画像 出力画像

double Interpolator::interpolate(const double p0, const double p1, const double p2, const double p3, const double x){
    return  (-0.5f*p0+1.5f*p1-1.5f*p2+0.5*p3)*pow(x,3)+
            (p0-2.5f*p1+2.f*p2-0.5f*p3)*pow(x,2)+
            (-0.5f*p0+0.5f*p2)*x+
            p1;
}

bool Image::equals(double a, double b, double threshold){
        if(fabs(a-b)<=threshold)
            return true;
        return false;
}


void Image::interpolate(const Pixel p[], double offset, Pixel& result){
    result.r = Interpolator::interpolate(p[0].r,p[1].r,p[2].r,p[3].r,offset);
    result.g = Interpolator::interpolate(p[0].g,p[1].g,p[2].g,p[3].g,offset);
    result.b = Interpolator::interpolate(p[0].b,p[1].b,p[2].b,p[3].b,offset);
    result.a = Interpolator::interpolate(p[0].a,p[1].a,p[2].a,p[3].a,offset);   
}

void Image::getSamplingCoords(const int nearest,
                              const int max,
                              int coords[]){

    coords[0] = nearest-1;
    if(coords[0]<0)
        coords[0] = nearest;
    coords[1] = nearest;
    coords[2] = nearest+1;
    if(coords[2]>=max)
        coords[2] = nearest;    
    coords[3] = nearest+2;
    //The following check should not be necessary
    //since this is never expected to occur. Nevertheless...
    if(coords[3]>=max)
        coords[3] = nearest;
}

void Image::interpolateAlongY(int x, int y, int yMax, double yOffset, Pixel& result){

    if(equals(yOffset,0.f,ERROR_THRESHOLD)){
        //No interpolation required
        getPixel(x,y,result);
        return;
    }

    int yCoords[4];
    getSamplingCoords(y, yMax, yCoords);    
    Pixel interpolants[4];
    for(int i=0; i<4; ++i){
        getPixel(x, yCoords[i], interpolants[i]);
    }   
    interpolate(interpolants, y, result);
}

void Image::resize(const int newWidth, const int newHeight){
    //Ensure that we have a valid buffer already
    if(buffer==NULL){
        printf("ERROR: Must load an image before resizing it!");
        assert(false);
    }   
    //We first need to create a new buffer with the new dimensions
    unsigned char* newBuffer = new unsigned char[newWidth*newHeight*channelCount];
    for(int j=0; j<newHeight; ++j){
        for(int i=0; i<newWidth; ++i){
            size_t newIndexOffset = (j*newWidth+i)*channelCount;
            //For this pixel in the target image we
            //a) Find the nearest pixel in the source image
            //b) Find the offset from the aforementioned nearest pixel
            int xNear,yNear;
            double xOffset,yOffset;
            double x = ((double)width/(double)newWidth)*i;
            double y = ((double)height/(double)newHeight)*j;    
            xNear = floor(x);
            yNear = floor(y);   
            xOffset = x-xNear;
            yOffset = y-yNear;

            //If offset is 0, we don't need any interpolation
            //we simply need to sample the source pixel and proceed
//          if(equals(xOffset,0.f,ERROR_THRESHOLD) && equals(yOffset,0.f,ERROR_THRESHOLD)){
//              Pixel result;
//              getPixel(xNear, yNear, result);
//              *(newBuffer+newIndexOffset) = result.r;
//              *(newBuffer+newIndexOffset+1) = result.g;
//              *(newBuffer+newIndexOffset+2) = result.b;
//              if(channelCount==4)
//                  *(buffer+newIndexOffset+3) = result.a;
//              continue;
//          }           
            //We make a check that xNear and yNear obtained above
            //are always smaller than the edge pixels at the extremeties
            if(xNear>=width || yNear>=height){
                printf("ERROR: Nearest pixel computation error!");
                assert(false);
            }           
            //Next we find four pixels along the x direction around this
            //nearest pixel
            int xCoords[4];
            getSamplingCoords(xNear,width,xCoords);

            //For each of these sampling xCoords, we interpolate 4 nearest points
            //along Y direction
            Pixel yInterps[4];
            for(int k=0; k<4; k++){
                interpolateAlongY(xCoords[k], yNear, height, yOffset, yInterps[k]);
            }           
            //Finally, the resultant pixel is a cubic interpolation
            //on the 4 obtained pixels above
            Pixel result;
            if(equals(xOffset,0.f,ERROR_THRESHOLD)){
                result.r = yInterps[0].r;
                result.g = yInterps[0].g;
                result.b = yInterps[0].b;
                result.a = yInterps[0].a;
            }else{
                interpolate(yInterps, xOffset, result);
            }
            *(newBuffer+newIndexOffset) = result.r;
            *(newBuffer+newIndexOffset+1) = result.g;
            *(newBuffer+newIndexOffset+2) = result.b;
            if(channelCount==4)
                *(newBuffer+newIndexOffset+3) = result.a;
        }   
    }   
    //Now we can deallocate the memory of our current buffer
    delete [] buffer;
    //Reassign our newly sampled buffer to our own
    buffer = newBuffer;
    //Reset our image dimensions
    height = newHeight;
    width = newWidth;
}
4

2 に答える 2

4

interpolateAlongY間違っています、最後の行は

interpolate(interpolants, y, result);

とする必要があります

interpolate(interpolants, yOffset, result);

さらに、のコメントgetSamplingCoordscoords[3]間違っています。

編集:

JansonD​​の回答を調べた後、さらに2つの問題に気づきました。例外if(equals(xOffset,0.f,ERROR_THRESHOLD))は必要ありません。オフセットの値が0に近い場合、補間関数は正しいことを行います。

もう1つは、隣接するピクセルから情報を追加するときに、補間関数がおそらくローパスフィルターであるため、エッジなどの高周波データの詳細が失われている可能性があるということです。

于 2012-12-23T16:09:21.190 に答える
1

に関するジョセフの答えに加えて:

interpolate(interpolants, y, result);

参照する必要がありますyOffset、またあります:

if(equals(xOffset,0.f,ERROR_THRESHOLD)){
            result.r = yInterps[0].r;
            result.g = yInterps[0].g;
            result.b = yInterps[0].b;
            result.a = yInterps[0].a;

どちらを使用する必要がありますyInterps[1]

また、出力値はいずれもクランプされていないため、鋭いエッジの周りにアンダーフローとオーバーフローが発生する可能性があります。

そしてコメント:

if(coords[3]>=max)
    coords[3] = nearest;

間違っているのはそれだけではありませんが、エッジピクセルを繰り返すのではなく反射する場合を除いて、クランプはでありmax-1、ではありません。nearest

于 2012-12-23T17:07:00.703 に答える