[英]How to transpose in-place a bitmap in C++
我试图创建一个函数来就地转置位图。 但是到目前为止,我得到的结果都是一团糟,我找不到我做错了什么。
源位图是 ARGB 格式的一维像素阵列。
void transpose(uint8_t* buffer, const uint32_t width, const uint32_t height)
{
const size_t stride = width * sizeof(uint32_t);
for (uint32_t i = 0; i < height; i++)
{
uint32_t* row = (uint32_t*)(buffer + (stride * i));
uint8_t* section = buffer + (i * sizeof(uint32_t));
for (uint32_t j = i + 1; j < height; j++)
{
const uint32_t tmp = row[j];
row[j] = *((uint32_t*)(section + (stride * j)));
*((uint32_t*)(section + (stride * j))) = tmp;
}
}
}
更新:
为了澄清和避免混淆,因为似乎有些人认为这只是一个旋转图像问题。 转置图像由 2 个变换组成:1) 水平翻转 2) 逆时针旋转 90。 (如图像示例所示,请参阅箭头方向)
我认为问题比您意识到的要复杂,而且不仅仅是将x, y
的像素与y, x
处的像素交换的情况。 如果您考虑一个 3*7 像素的图像,其中我已将像素标记为a
- u
:
abcdefg
hijklmn
opqrstu
旋转此图像给出:
aho
bip
cjq
dkr
els
fmt
gnu
将两个图像转换为一维数组给出:
abcdefghijklmnopqrstu
ahobipcjqdkrelsfmtgnu
请注意, b
已移动到d
的位置,但已被h
替换。
重新思考您的算法,将其绘制为小图像,并在尝试实施之前确保它有效。
由于任务的复杂性,它实际上可能最终更快地创建一个临时缓冲区,旋转到该缓冲区然后复制回来,因为它可能会比您提出的就地算法得到更少的副本(每个像素 2 个)。
大部分等效的代码应该更容易调试:
inline uint32_t * addr(uint8_t* buffer, const uint32_t width, uint32_t i, uint32_t j) {
uint32_t * tmp = buffer;
return tmp+i*width+j;
}
void transpose(uint8_t* buffer, const uint32_t width, const uint32_t height) {
for (uint32_t i = 0; i < min(width,height); i++) {
for (uint32_t j = 0; j < i; j++) {
uint32_t * a = addr(buffer, width, i, j);
uint32_t * b = addr(buffer, width, j, i);
const uint32_t tmp = *a;
*a = *b;
*b = tmp;
}
}
}
如果这不起作用,它可能不仅需要知道图片的宽度,还需要知道底层缓冲区的宽度。 这只会翻转左上角的方形部分,非方形位图需要做更多的工作。 (或者在使用前将所有东西都填充成正方形......)
请注意,当N!=M
时,原地转置矩阵并非易事。 有关详细信息,请参见例如此处。
原因是当N=M
您可以简单地遍历矩阵的一半并交换元素。 当N!=M
,情况并非如此。
为了说明,考虑一个更简单的案例:
首先是一维数据的二维视图:
struct my2dview {
std::vector<int>& data;
int width,height;
my2dview(std::vector<int>& data,int width,int height):data(data),width(width),height(height){}
int operator()(int x,int y) const { return data[x*width + y]; }
int& operator()(int x,int y){ return data[x*width + y]; }
my2dview get_transposed() { return my2dview(data,height,width);}
};
std::ostream& operator<<(std::ostream& out, const my2dview& x){
for (int h=0;h<x.height;++h){
for (int w=0;w<x.width;++w){
out << x(h,w) << " ";
}
out << "\n";
}
return out;
}
现在一个适用于N=M
的转置:
my2dview broken_transpose(my2dview x){
auto res = x.get_transposed();
for (int i=0;i<x.height;++i){
for (int j=0;j<x.width;++j){
res(j,i) = x(i,j);
}
}
return res;
}
将它用于一些小矩阵
int main() {
std::vector<int> x{1,2,3,4,5,6};
auto v = my2dview(x,2,3);
std::cout << v << '\n';
std::cout << v.get_transposed() << '\n';
auto v2 = broken_transpose(v);
std::cout << v2;
}
印刷
1 2
3 4
5 6
1 2 3
4 5 6
1 3 2
2 2 6
结论:朴素的交换元素方法不适用于非方阵。
实际上,这个答案只是改写了@Alan Birtles 的答案。 我觉得受到了他的挑战
由于任务的复杂性,创建临时缓冲区实际上可能会更快 [...]
只是为了得出同样的结论;)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.