C++ 用于图像缩放的双三次插值算法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15176972/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Bi-Cubic Interpolation Algorithm for Image Scaling
提问by Channel72
I'm trying to write a basic bicubic resize algorithm to resize a 24-bit RGB bitmap. I have a general understanding of the mathinvolved, and I'm using this implementationfrom Google Code as a guide. I'm not using any external libraries here - I'm just experimenting with the algorithm itself. The bitmap is represented as a plain std::vector<unsigned char>
:
我正在尝试编写一个基本的双三次调整大小算法来调整 24 位 RGB 位图的大小。我对所涉及的数学有一个大致的了解,并且我使用Google Code 中的这个实现作为指导。我在这里没有使用任何外部库——我只是在试验算法本身。位图表示为一个普通的std::vector<unsigned char>
:
inline unsigned char getpixel(const std::vector<unsigned char>& in,
std::size_t src_width, std::size_t src_height, unsigned x, unsigned y, int channel)
{
if (x < src_width && y < src_height)
return in[(x * 3 * src_width) + (3 * y) + channel];
return 0;
}
std::vector<unsigned char> bicubicresize(const std::vector<unsigned char>& in,
std::size_t src_width, std::size_t src_height, std::size_t dest_width, std::size_t dest_height)
{
std::vector<unsigned char> out(dest_width * dest_height * 3);
const float tx = float(src_width) / dest_width;
const float ty = float(src_height) / dest_height;
const int channels = 3;
const std::size_t row_stride = dest_width * channels;
unsigned char C[5] = { 0 };
for (int i = 0; i < dest_height; ++i)
{
for (int j = 0; j < dest_width; ++j)
{
const int x = int(tx * j);
const int y = int(ty * i);
const float dx = tx * j - x;
const float dy = ty * i - y;
for (int k = 0; k < 3; ++k)
{
for (int jj = 0; jj < 4; ++jj)
{
const int z = y - 1 + jj;
unsigned char a0 = getpixel(in, src_width, src_height, z, x, k);
unsigned char d0 = getpixel(in, src_width, src_height, z, x - 1, k) - a0;
unsigned char d2 = getpixel(in, src_width, src_height, z, x + 1, k) - a0;
unsigned char d3 = getpixel(in, src_width, src_height, z, x + 2, k) - a0;
unsigned char a1 = -1.0 / 3 * d0 + d2 - 1.0 / 6 * d3;
unsigned char a2 = 1.0 / 2 * d0 + 1.0 / 2 * d2;
unsigned char a3 = -1.0 / 6 * d0 - 1.0 / 2 * d2 + 1.0 / 6 * d3;
C[jj] = a0 + a1 * dx + a2 * dx * dx + a3 * dx * dx * dx;
d0 = C[0] - C[1];
d2 = C[2] - C[1];
d3 = C[3] - C[1];
a0 = C[1];
a1 = -1.0 / 3 * d0 + d2 -1.0 / 6 * d3;
a2 = 1.0 / 2 * d0 + 1.0 / 2 * d2;
a3 = -1.0 / 6 * d0 - 1.0 / 2 * d2 + 1.0 / 6 * d3;
out[i * row_stride + j * channels + k] = a0 + a1 * dy + a2 * dy * dy + a3 * dy * dy * dy;
}
}
}
}
return out;
}
Problem: When I use this algorithm to downscale an image, it works except the output image contains all black pixels on the right side for some reason, giving the appearance that it's been "cropped".
问题:当我使用此算法缩小图像的比例时,它可以正常工作,但由于某种原因,输出图像在右侧包含所有黑色像素,看起来像是被“裁剪”了。
Example:
例子:
INPUT IMAGE:
输入图像:
OUTPUT IMAGE:
输出图像:
Question: Reviewing the algorithm, I can't see why this would happen. Does anyone see the flaw here?
问题:回顾算法,我不明白为什么会发生这种情况。有没有人看到这里的缺陷?
回答by V-X
try not exchanging width and height.
尽量不要交换宽度和高度。
for (int i = 0; i < dest_width; ++i)
{
for (int j = 0; j < dest_height; ++j)
回答by Vladimir Fedosov
I suggest don't use this function because it was written very bad. You need to make two convolutions: at first by X coordinate then by Y. In this function all these convolutions are making in the same time that leads to very slow work. And if You would look at jj loop body you could notice that all second part of body begining from "d0 = C[0] - C[1];" could be moved outside jj loop because only the last iteration of this loop takes effect on out[] array (all previous iterations results will be overwrited).
我建议不要使用这个功能,因为它写得很糟糕。你需要做两个卷积:首先是 X 坐标,然后是 Y。在这个函数中,所有这些卷积都是在同一时间进行的,这导致工作非常缓慢。如果你看 jj 循环体,你会注意到所有的第二部分都是从“d0 = C[0] - C[1];”开始的。可以移到 jj 循环之外,因为只有此循环的最后一次迭代才会对 out[] 数组生效(所有先前的迭代结果都将被覆盖)。
回答by xuyibo
In getpixel(in, src_width, src_height, z, x, k)
:
在getpixel(in, src_width, src_height, z, x, k)
:
z mean horizontal offset
x mean vertical offset
So just need patch the getpixel
function, below is the patched code:
所以只需要修补这个getpixel
函数,下面是修补后的代码:
inline unsigned char getpixel(const std::vector<unsigned char>& in,
std::size_t src_width, std::size_t src_height, unsigned y, unsigned x, int channel)
{
if (x < src_width && y < src_height)
return in[(y * 3 * src_width) + (3 * x) + channel];
return 0;
}
std::vector<unsigned char> bicubicresize(const std::vector<unsigned char>& in,
std::size_t src_width, std::size_t src_height, std::size_t dest_width, std::size_t dest_height)
{
std::vector<unsigned char> out(dest_width * dest_height * 3);
const float tx = float(src_width) / dest_width;
const float ty = float(src_height) / dest_height;
const int channels = 3;
const std::size_t row_stride = dest_width * channels;
unsigned char C[5] = { 0 };
for (int i = 0; i < dest_height; ++i)
{
for (int j = 0; j < dest_width; ++j)
{
const int x = int(tx * j);
const int y = int(ty * i);
const float dx = tx * j - x;
const float dy = ty * i - y;
for (int k = 0; k < 3; ++k)
{
for (int jj = 0; jj < 4; ++jj)
{
const int z = y - 1 + jj;
unsigned char a0 = getpixel(in, src_width, src_height, z, x, k);
unsigned char d0 = getpixel(in, src_width, src_height, z, x - 1, k) - a0;
unsigned char d2 = getpixel(in, src_width, src_height, z, x + 1, k) - a0;
unsigned char d3 = getpixel(in, src_width, src_height, z, x + 2, k) - a0;
unsigned char a1 = -1.0 / 3 * d0 + d2 - 1.0 / 6 * d3;
unsigned char a2 = 1.0 / 2 * d0 + 1.0 / 2 * d2;
unsigned char a3 = -1.0 / 6 * d0 - 1.0 / 2 * d2 + 1.0 / 6 * d3;
C[jj] = a0 + a1 * dx + a2 * dx * dx + a3 * dx * dx * dx;
d0 = C[0] - C[1];
d2 = C[2] - C[1];
d3 = C[3] - C[1];
a0 = C[1];
a1 = -1.0 / 3 * d0 + d2 -1.0 / 6 * d3;
a2 = 1.0 / 2 * d0 + 1.0 / 2 * d2;
a3 = -1.0 / 6 * d0 - 1.0 / 2 * d2 + 1.0 / 6 * d3;
out[i * row_stride + j * channels + k] = a0 + a1 * dy + a2 * dy * dy + a3 * dy * dy * dy;
}
}
}
}
return out;
}
回答by Amgad
You should switch the x
and z
when you call getpixel
, and in getpixel
you should index the array using:
您应该在调用时切换x
和,并且应该使用以下方法索引数组:z
getpixel
getpixel
[(y * 3 * src_width) + (3 * x) + channel]