OpenCV中查詢表修改畫素與LUT用法
1、查詢表
之前我在OpenCV中影象遍歷與畫素操作中有提到過查詢表遍歷影象的API函式cv::LUT,鑑於其大巧於內的美感[笑],特意寫一篇部落格探討一下。從數學上來看查詢表是一個簡單的一對一或多對一的函式,定義瞭如何將畫素轉換為新的值。從資料的組織關係上來看,查詢表是一維或多維的陣列,儲存了不同輸入值所對應的輸出值。資料表在影象處理中主要用於畫素的點運算,尤其是畫素之間無位置相關性的操作中。比如我們在上面提到的部落格中,求影象映象變換的示例就很難運用查詢表的方法來實現。而在顏色縮減、圖片取反以及直方圖均衡化等不涉及畫素位置相關性的演算法中我們都可以應用。當然,查詢表的優勢也很明顯[大巧於內],只需讀取、無需計算。
先上幾個例子,直觀感受一下。
1.顏色空間縮減:將現有顏色空間值除以某個輸入值,以獲得較少的顏色數。例如,顏色值0到9可取為新值0,10到19可取為10,以此類推。(示例來源於OpenCV官網)。
顯然這是一個多對一的對映,I[new] = I[old]/10*10。很容易想到,只要遍歷影象矩陣的每一個畫素,對畫素應用上述公式就可以完成任務。只是這裡用到了除法和乘法運算,而這兩種運算又特別費時。鑑於一幅影象只涉及256個畫素,我們大可開一個長度為256的陣列,讓其下標代表舊畫素值,陣列值代表新的畫素值,如lookup[256]={0,…,0,10,…,10,20,…,20,…,250,…,250}。這樣我們遍歷修改時不就可以通過畫素值從表中查出要改變的畫素值了麼,而且這一過程只有賦值運算。
2.影象取反:反轉影象的畫素強度,使影象中的前景變為背景,背景變為前景。
顯然這是一個一對一的對映,即畫素值0變為255,1變為254…254變為1,255變為0。對應的查詢表為lookup[256]={255,254,…,1,0}。
程式碼如下:
#include <iostream>
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
using namespace std ;
using namespace cv;
void Invert(Mat &img, const uchar* const lookup)
{
int rows=img.rows;
int cols=img.cols*img.channels();
for(int i=0; i<rows; i++)
{
uchar *p=img.ptr<uchar>(i);
for(int j=0; j<cols;j++)
p[j]=lookup[p[j]];
}
}
int main()
{
Mat src=imread("test.jpg"); //將任意一張名為test.jpg的圖片放置於工程資料夾test中
if(!src.data)
{
cout<<"error! The image is not built!"<<endl;
return -1;
}
// 為了演示效果,將圖片轉換成灰度圖片
Mat img1=src;
//cvtColor( src, img1, CV_RGB2GRAY );
imshow("First",img1);
//建立查詢表
uchar lookup[256];
for(int i=0; i<256; i++)
lookup[i]=255-i;
//呼叫自定義影象取反函式
Invert(img1, lookup);
imshow("Second",img1);
waitKey();
return 0;
}
執行結果:
本來覺得上彩圖難看,結果發現灰度圖也難看的過分,那就這樣吧。
2、LUT函式
上面我們程式中我們建立查詢表之後,對影象的遍歷是手動實現的,其實OpenCV中為我們提供了一個非常高效的API函式cv::LUT,可以應用查詢表生成新影象。LUT是OpenCV的core mudle中的函式,其函式原型如下:
void LUT(InputArray src, InputArray lut, OutputArray dst)
Parameters:
第一個引數:原始影象的地址;
第二個引數:查詢表的地址,對於多通道影象的查詢,它可以有一個通道,也可以與原始影象有相同的通道;
第三個引數:輸出影象的地址。
典型用法(藉助影象取反示例說明)是:
Mat lookUpTable(1, 256, CV_8U);
uchar* p = lookUpTable.data;
for( int i = 0; i < 256; ++i)
p[i] = 255-i;
呼叫就很簡單啦,LUT(src, lookUpTable,dst )
。
還是貼一下全部程式碼:
#include<iostream>
#include<opencv2/core/core.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat src=imread("test.jpg"); //將任意一張名為test.jpg的圖片放置於工程資料夾test中
if(!src.data)
{
cout<<"error! The image is not built!"<<endl;
return -1;
}
// 為了演示效果,將圖片轉換成灰度圖片
Mat img1=src;
//cvtColor( src, img1, CV_RGB2GRAY );
imshow("First",img1);
//建立查詢表
Mat lookUpTable(1, 256, CV_8U);
uchar *p = lookUpTable.data;
for(int i=0; i<256; i++)
p[i]=255-i;
//通過LUT函式實現影象取反
LUT(img1,lookUpTable,img1);
imshow("Second",img1);
waitKey();
return 0;
}
最後嘮叨兩句,雖然手動遍歷可以達到同樣效果,但儘量使用 OpenCV 內建函式。呼叫LUT 函式可以獲得最快的速度,這是因為OpenCV庫可以通過英特爾執行緒架構啟用多執行緒。