我正在尝试实现双三次图像插值。我只粘贴了代码的相关部分。我跳过了处理将图像加载到缓冲区并从中读取像素等的代码。我有理由确定我的数学是正确的。但是,我似乎在输出中有可怕的伪影。
所有操作都发生在 resize 方法中。
我希望有经验的图形程序员能够分享他们对我可能做错的预感。
以下是将输入调整为宽度和高度的两倍时得到的输入和输出图像。
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;
}