2009-03-05 111 views
4

我正在開發一個使用Qt 4.5的圖形應用程序,並將圖像放在QPixmapCache中,我想優化這個,這樣如果用戶插入一個已經在緩存中的圖像,它將使用它。獲取QPixmap散列的最佳方式是什麼?

現在每個圖像都有一個唯一的ID,它有助於在繪畫事件上優化自己。但是我意識到,如果我可以計算圖像的哈希,我可以查找緩存以查看它是否已經存在並使用它(當然,這對於重複對象會有更多幫助)。

我的問題是,如果它的一個大的QPixmap將它的散列計算放慢了速度還是有更快的方法呢?

+0

標題中的錯字(怎麼可能是......):) – claf 2009-03-05 10:28:28

回答

3

在這個一對夫婦的意見:

  1. 如果你將要產生一個像素圖的哈希/緩存鍵,那麼你可能想跳過QPixmapCache和使用QCache直接。這將消除使用QStrings作爲鍵的一些開銷(除非您也想使用文件路徑來定位項目)

  2. 從Qt4.4開始,QPixmap有一個與其關聯的「散列」值(請參閱QPixmap :: cacheKey())。該文檔聲稱「不同的QPixmap對象只能具有相同的緩存鍵如果它們引用相同的內容。」但是,由於Qt使用共享數據複製,這可能只適用於複製的像素圖,而不適用於從同一圖像加載的兩個不同的像素圖。一些測試會告訴你它是否有效,如果它有效,它會讓你輕鬆獲得散列值。

  3. 如果你真的想做一個好,相當快的緩存,刪除重複,你可能想看看,根據大小,顏色深度,圖像類型,事情如排序你自己的數據結構 。然後,只有在找到具有相同尺寸,位深度等的相同類型圖像後,才需要對實際圖像數據進行散列處理。當然,如果用戶通常用相同的方法打開大量圖像,它不會根本沒有幫助。

  4. 性能:不要忘記Qt在4中添加的基準測試內容。5,它可以讓你比較你的各種哈希思想,並看看哪一個運行速度最快。我還沒有檢查出來,但它看起來很整齊。

1

根據您使用的算法,哈希計算應該非常快速(如果沒有涉及到磁盤I/O,則應高於100 MB/s)。在散列之前,您還可以進行一些快速測試來整理潛在候選人 - f.e.圖像必須具有相同的寬度和高度,否則無法比較其散列值。

當然,您還應該保留插入圖像的哈希值,以便您只需計算新圖像的哈希值,而不必再爲緩存圖像計算哈希值。

如果圖像足夠不同,它可能足以不散列整個圖像,但較小的縮略圖或圖像的一部分(第一和最後10行),這將會更快,但會導致更多的碰撞。

1

我假設你正在討論的是實際計算圖像數據的散列而不是獲取由QT生成的唯一ID。
根據你的圖像,你可能不需要遍歷整個圖像來計算散列。也許只能讀取前10個像素?第一條掃描線?
也許從整個圖像的像素僞隨機選擇? (使用已知的種子,以便可以重複該序列)不要忘記將圖像的大小添加到散列。

3

萬一有人遇到這個問題(是不是太可怕與哈希的事情,尤其是像圖像經歷),這裏是一個非常簡單的解決方案,我用散列QPixmaps和它們輸入查找表後面的比較:

qint32 HashClass::hashPixmap(QPixmap pix) 
{ 
    QImage image = pix.toImage(); 
    qint32 hash = 0; 

    for(int y = 0; y < image.height(); y++) 
    { 
     for(int x = 0; x < image.width(); x++) 
     { 
      QRgb pixel = image.pixel(x,y); 

      hash += pixel; 
      hash += (hash << 10); 
      hash ^= (hash >> 6); 
     } 
    } 

    return hash; 
} 

這裏是哈希函數本身(你可以把它散列到一個qint64,如果你希望值的碰撞)。正如您所看到的,我將像素圖轉換爲QImage,並簡單地遍歷它的尺寸並對每個像素執行一個非常簡單的一次性散列並返回最終結果。有很多方法可以改進這個實現(請參閱這個問題的其他答案),但這是需要完成的基本要點。

OP提到他將如何使用這個散列函數來構造查找表,以便以後比較圖像。這將需要一個非常簡單的查找初始化函數 - 是這樣的:

void HashClass::initializeImageLookupTable() 
{ 
    imageTable.insert(hashPixmap(QPixmap(":/Image_Path1.png")), "ImageKey1"); 
    imageTable.insert(hashPixmap(QPixmap(":/Image_Path2.png")), "ImageKey2"); 
    imageTable.insert(hashPixmap(QPixmap(":/Image_Path3.png")), "ImageKey2"); 
// Etc... 
} 

我使用的是這裏所謂的QMAP其中imageTable將需要在類被聲明爲這樣:

QMap<qint32, QString> imageTable; 

然後,最後,當你想將圖像與查找表中的圖像進行比較時(例如:「我認識的圖像中是什麼圖像,這個圖像是什麼?」),你只需要調用哈希函數該圖像(我假設也將是一個QPixmap)和返回的QString值將允許你弄清楚。像這樣的東西可以工作:

void HashClass::compareImage(const QPixmap& pixmap) 
{ 
    QString value = imageTable[hashPixmap(pixmap)]; 
    // Do whatever needs to be done with the QString value and pixmap after this point. 
} 

就是這樣。我希望這能幫助某人 - 這會爲我節省一些時間,儘管我很高興有這種經歷。

相關問題