簡體   English   中英

如何在 C++ 中就地轉置位圖

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM