我正在尝试实现双三次图像插值。我只粘贴了代码的相关部分。我已经跳过了将图像加载到缓冲区并从中读取像素的代码。我有理由相信我的数学是正确的。但是,我似乎在输出中有可怕的伪像。
所有操作都发生在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;
}
答案 0 :(得分:4)
interpolateAlongY
错了,最后一行是
interpolate(interpolants, y, result);
应该是
interpolate(interpolants, yOffset, result);
此外getSamplingCoords
coords[3]
中的评论错误。
编辑:
在我查看了JansonD的答案之后,我注意到了另外两个问题,if(equals(xOffset,0.f,ERROR_THRESHOLD))
的例外情况甚至不必要,当偏移的值接近0时,插值函数做正确的事。
另一方面,当您从相邻像素添加信息时,插值函数可能是低通滤波器,因此您可能会丢失像边缘这样的高频数据的细节。
答案 1 :(得分: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
,除非你的意思是反映边缘像素而不是重复它们。