1、灰度圖像直方圖統(tǒng)計
直方圖統(tǒng)計是圖像處理中的一個非常重要的操作。VTK中實現(xiàn)直方圖統(tǒng)計功能的filter是vtkImageAccumulate。其將每個組分的數(shù)值范圍劃分為離散的間隔,然后統(tǒng)計每個灰度間隔上的像素數(shù)目。vtkImageAccumulate輸入和輸出都是vtkImageData類型,因此直方圖也可以看做是一幅圖像;對于輸入圖像的像素數(shù)據(jù)類型可以是任意的,但是最大支持3個組分像素類型,而輸出圖像的像素數(shù)據(jù)類型為int型。一個灰度圖像的直方圖為一個一維圖像。
#include"vtkSmartPointer.h"
#include"vtkJPEGReader.h"
#include"vtkImageAccumulate.h"
#include"vtkImageData.h"
#include"vtkIntArray.h"
#include"vtkDataObject.h"
#include"vtkFieldData.h"
#include"vtkBarChartActor.h"
#include"vtkProperty2D.h"
#include"vtkTextProperty.h"
#include"vtkLegendBoxActor.h"
#include"vtkImageActor.h"
#include"vtkRenderer.h"
#include"vtkRenderWindow.h"
#include"vtkRenderWindowInteractor.h"int main()
{vtkSmartPointer<vtkJPEGReader>reader = vtkSmartPointer<vtkJPEGReader>::New();//讀入一幅灰度圖像reader->SetFileName("data\\lena-gray.jpg");reader->Update();int bins = 16; //直方圖一維數(shù)組的維數(shù)int comps = 1;vtkSmartPointer<vtkImageAccumulate>histogram = vtkSmartPointer<vtkImageAccumulate>::New();histogram->SetInputData(reader->GetOutput());histogram->SetComponentExtent(0, bins - 1, 0, 0, 0, 0);  //6個參數(shù)是RGB3個組分直方圖的最大最小值histogram->SetComponentOrigin(0, 0, 0);          //函數(shù)設(shè)置的是統(tǒng)計每個組分直方圖時的起始灰度值histogram->SetComponentSpacing(256.0 / bins, 0, 0);  //設(shè)置直方圖每個間隔代表的灰度范圍histogram->Update();int* output = static_cast<int*>(histogram->GetOutput()->GetScalarPointer());vtkSmartPointer<vtkIntArray> frequencies = vtkSmartPointer<vtkIntArray>::New();frequencies->SetNumberOfComponents(1);for (int j = 0; j < bins; ++j){for (int i = 0; i < comps; ++i){frequencies->InsertNextTuple1(*output++);}}vtkSmartPointer<vtkDataObject> dataObject = vtkSmartPointer<vtkDataObject>::New();dataObject->GetFieldData()->AddArray(frequencies);/*******************************************************/vtkSmartPointer<vtkBarChartActor> barChart = vtkSmartPointer<vtkBarChartActor>::New();barChart->SetInput(dataObject);barChart->SetTitle("Histogram");barChart->GetPositionCoordinate()->SetValue(0.05, 0.05, 0.0);barChart->GetPosition2Coordinate()->SetValue(0.95, 0.95, 0.0);barChart->GetProperty()->SetColor(0, 0, 0);barChart->GetTitleTextProperty()->SetColor(0, 0, 0);barChart->GetLabelTextProperty()->SetColor(0, 0, 0);barChart->GetLegendActor()->SetNumberOfEntries(dataObject->GetFieldData()->GetArray(0)->GetNumberOfTuples());//barChart->LegendVisibilityOff();//barChart->LabelVisibilityOff();double colors[3][3] = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } };int count = 0;for (int i = 0; i < bins; ++i){for (int j = 0; j < comps; ++j){barChart->SetBarColor(count++, colors[j]);//單通道紅色,colors數(shù)組第一個元素的值}}vtkSmartPointer<vtkImageActor> imgActor = vtkSmartPointer<vtkImageActor>::New();imgActor->SetInputData(reader->GetOutput());/*******************************************************/double imgView[4] = { 0.0, 0.0, 0.5, 1.0 };double barView[4] = { 0.5, 0.0, 1.0, 1.0 };vtkSmartPointer<vtkRenderer> barRender = vtkSmartPointer<vtkRenderer>::New();barRender->SetViewport(barView);barRender->AddActor(barChart);barRender->SetBackground(1, 1, 1);vtkSmartPointer<vtkRenderer> imgRender = vtkSmartPointer<vtkRenderer>::New();imgRender->SetViewport(imgView);imgRender->AddActor(imgActor);imgRender->SetBackground(1, 1, 1);vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();renderWindow->AddRenderer(barRender);renderWindow->AddRenderer(imgRender);renderWindow->SetSize(640, 320);renderWindow->Render();renderWindow->SetWindowName("Gray-Image Histogram");vtkSmartPointer<vtkRenderWindowInteractor> rwi = vtkSmartPointer<vtkRenderWindowInteractor>::New();rwi->SetRenderWindow(renderWindow);rwi->Initialize();rwi->Start();return EXIT_SUCCESS;
}
運行結(jié)果:
代碼分析:
首先是讀入一副灰度圖像,一般的灰度圖像的灰度范圍為0-255。定義了一個變量bins = 16,表示要圖像灰度范圍上的間隔數(shù)目,也可以理解為直方圖一維數(shù)組的維數(shù)。然后定義vtkImageAccumulate對象,并設(shè)置輸入數(shù)據(jù)為我們讀入的圖像數(shù)據(jù),接著調(diào)用了三個函數(shù):
SetComponentExtent(0,bins-1, 0, 0, 0, 0),該函數(shù)設(shè)置要計算每個組分的直方圖的最小和最大值。vtkImageAccumulate最大支持像素值為三個組分(如RGB圖像)的直方圖,支持共有六個參數(shù)。分別表示每個組分的直方圖最小和最大值。該例中由于計算的是灰度圖像直方圖,只有一個組分,因此第二個和第三個組分都設(shè)置為0;而第一組分直方圖維數(shù)為bins = 16,那么其最小和最大范圍為0和bins-1。
SetComponentOrigin(0,0,0),該函數(shù)設(shè)置的是統(tǒng)計每個組分直方圖時的起始灰度值,這里設(shè)置為0,表示灰度從0開始統(tǒng)計直方圖。同樣,vtkImageAccumulate最大支持像素值為三個組分,這里也要設(shè)置三個參數(shù)。如果圖像的灰度范圍為[1000, 2000],那么計算直方圖時,其起始灰度應(yīng)該設(shè)置為1000。
SetComponentSpacing(16,0, 0),設(shè)置直方圖每個間隔代表的灰度范圍,例如當(dāng)一個圖像灰度范圍為[1000, 2000],統(tǒng)計直方圖的間隔數(shù)bins為100時,那么對應(yīng)的space應(yīng)該設(shè)置為SetComponentSpacing(100, 0, 0)。
參數(shù)設(shè)置完畢后執(zhí)行Update()即可計算直方圖。前面已經(jīng)提到過,vtkImageAccumulate的輸出結(jié)果也是一個vtkImageData類型,這樣就可以方便的訪問圖像的每個數(shù)據(jù)。圖像像素訪問前面已經(jīng)介紹過,這里就不在重述。需要注意的是輸出直方圖圖像的數(shù)據(jù)類型為int。
這里再順便簡單的介紹一下直方圖的顯示。
雖然vtkImageAccumulate的輸出類型為vtkImageData但是并不能直接按照圖像的方式進行顯示。VTK中定義了vtkBarChartActor用來顯示條形圖,因此可以利用其來顯示直方圖。但是該類接收的數(shù)據(jù)類型為vtkDataObject類型,因此需要先將直方圖數(shù)據(jù)進行轉(zhuǎn)換。首先將直方圖數(shù)組存儲到vtkIntArray數(shù)組frequencies中,通過函數(shù)vtkDataObject函數(shù)GetFieldData()->AddArray(frequencies)將其添加到vtkDataObject對象中。vtkBarChartActor對象接收vtkDataObject對象作為輸入,另外還需要設(shè)置圖表的名字,顏色等,需要注意兩個函數(shù):
barChart->GetPositionCoordinate()->SetValue(0.05,0.05,0.0);
barChart->GetPosition2Coordinate()->SetValue(0.95,0.95,0.0);
這里設(shè)置的是窗口中顯示圖表的所在矩形的左下角點和右上角點坐標(biāo),VTK的坐標(biāo)系原點位于左下角點,設(shè)置時需要格外注意。設(shè)置完畢后,即可定義相應(yīng)的vtkRenderer,vtkRenderWindow和vtkRenderWindowInteractor對象顯示圖像直方圖。
2、彩色圖像直方圖統(tǒng)計
彩色圖像由于內(nèi)部有三個通道,不能直接計算直方圖,需要提取RGB三個通道數(shù)據(jù),分別計算直方圖。每個通道計算直方圖的方法與灰度圖像直方圖計算方法一致。
#include <vtkSmartPointer.h>
#include <vtkBMPReader.h>
#include <vtkImageData.h>
#include <vtkXYPlotActor.h>  //vtkXYPlotActor類可以用來顯示二維曲線,它可以接收多個輸入數(shù)據(jù)
#include <vtkAxisActor2D.h>
#include <vtkImageAccumulate.h>
#include <vtkImageExtractComponents.h>
#include <vtkProperty2D.h>
#include <vtkTextProperty.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>//測試圖像:../data/lena.bmp
int main()
{vtkSmartPointer<vtkBMPReader> reader = vtkSmartPointer<vtkBMPReader>::New();reader->SetFileName("data//lena.bmp");reader->Update();int numComponents = reader->GetOutput()->GetNumberOfScalarComponents();
//**********************************************************************/vtkSmartPointer<vtkXYPlotActor> plot = vtkSmartPointer<vtkXYPlotActor>::New();//使用vtkBarChartActor柱狀圖來顯示直方圖,本例使用vtkXYPlotActor曲線來表示直方圖。plot->ExchangeAxesOff();plot->SetLabelFormat("%g");plot->SetXTitle("Intensity");plot->SetYTitle("Frequency");plot->SetXValuesToValue();plot->GetProperty()->SetColor(0.0, 0.0, 0.0);plot->GetAxisLabelTextProperty()->SetColor(0.0, 0.0, 0.0);plot->GetAxisTitleTextProperty()->SetColor(0.0, 0.0, 0.0);double colors[3][3] = {{ 1, 0, 0 },{ 0, 1, 0 },{ 0, 0, 1 }};const char* labels[3] = { "Red", "Green", "Blue" };int xmax = 0;int ymax = 0;for (int i = 0; i < numComponents; ++i){vtkSmartPointer<vtkImageExtractComponents> extract = vtkSmartPointer<vtkImageExtractComponents>::New();extract->SetInputConnection(reader->GetOutputPort()); //彩色圖像不能直接計算直方圖,因此需要先通過vtkImageExtractComponents來提取每個通道圖像extract->SetComponents(i);extract->Update();double range[2];extract->GetOutput()->GetScalarRange(range);int extent = static_cast<int>(range[1]) - static_cast<int>(range[0]) - 1;vtkSmartPointer<vtkImageAccumulate> histogram =	vtkSmartPointer<vtkImageAccumulate>::New();histogram->SetInputConnection(extract->GetOutputPort());histogram->SetComponentExtent(0, extent, 0, 0, 0, 0);histogram->SetComponentOrigin(range[0], 0, 0);  //灰度起點為圖像的最小灰度值histogram->SetComponentSpacing(1, 0, 0); //直方圖的間隔取(1, 0, 0),每個灰度計算統(tǒng)計一個頻率histogram->SetIgnoreZero(1);  //統(tǒng)計直方圖時,像素值為0的像素不進行統(tǒng)計histogram->Update();
**********************************************************************/if (range[1] > xmax){xmax = range[1];}if (histogram->GetOutput()->GetScalarRange()[1] > ymax){ymax = histogram->GetOutput()->GetScalarRange()[1];}plot->AddDataSetInput(histogram->GetOutput());plot->SetPlotColor(i, colors[i]);plot->SetPlotLabel(i, labels[i]);plot->LegendOn(); //red\green\blue注釋}plot->SetXRange(0, xmax);plot->SetYRange(0, ymax);vtkSmartPointer<vtkRenderer> renderer =	vtkSmartPointer<vtkRenderer>::New();renderer->AddActor(plot);renderer->SetBackground(1.0, 1.0, 1.0);vtkSmartPointer<vtkRenderWindow> renderWindow =	vtkSmartPointer<vtkRenderWindow>::New();renderWindow->AddRenderer(renderer);renderWindow->SetSize(640, 480);renderWindow->Render();renderWindow->SetWindowName("ImageAccumulateExample2");vtkSmartPointer<vtkRenderWindowInteractor> interactor =	vtkSmartPointer<vtkRenderWindowInteractor>::New();interactor->SetRenderWindow(renderWindow);interactor->Initialize();interactor->Start();return EXIT_SUCCESS;
} 
運行結(jié)果:
代碼分析:
由于彩色圖像不能直接計算直方圖,因此需要先通過vtkImageExtractComponents來提取每個通道圖像,然后再利用vtkImageAccumulate統(tǒng)計直方圖。在本例中計算直方圖的間隔取(1, 0, 0),即每個灰度計算統(tǒng)計一個頻率,而且灰度起點為圖像的最小灰度值,這樣間隔的個數(shù)即為:最大灰度值減去最小灰度值,再減1,如第37行代碼。同時,設(shè)置了SetIgnoreZero()為1,即在統(tǒng)計直方圖時,像素值為0的像素不進行統(tǒng)計。
在灰度圖像直方圖實例中,我們使用的是vtkBarChartActor柱狀圖來顯示直方圖,在本例中則使用vtkXYPlotActor曲線來表示直方圖。
vtkXYPlotActor類可以用來顯示二維曲線,它可以接收多個輸入數(shù)據(jù),如本例中我們輸入了三條曲線,分別是圖像紅色分量直方圖區(qū)域,綠色分量直方圖曲線和藍色分量直方圖曲線。SetXRange()和SetYRange()用來設(shè)置X軸和Y軸的數(shù)據(jù)范圍,另外還可以設(shè)置X軸和Y軸的名字,曲線的標(biāo)題等屬性,詳細可以查閱vtkXYPlotActor類的文檔。vtkXYPlotActor類是一個vtkActor2D的子類,因此定義相應(yīng)的vtkRenderer,vtkRenderWindow和vtkRenderWindowInteractor對象建立可視化管道來顯示圖像直方圖曲線。
注:此文知識學(xué)習(xí)筆記,僅記錄完整程序和實現(xiàn)結(jié)果,具體原理參見:
https://blog.csdn.net/www_doling_net/article/details/8541534
https://blog.csdn.net/shenziheng1/article/category/6114053/4
參考資料:
1.《The Visualization Toolkit – AnObject-Oriented Approach To 3D Graphics (4th Edition)》
2. 張曉東, 羅火靈. VTK圖形圖像開發(fā)進階[M]. 機械工業(yè)出版社, 2015.
所用軟件:vtk7.0+visual studio 2013
                            總結(jié)
                            
                                以上是生活随笔為你收集整理的5.3.6 直方图统计的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
                            
                            
                                如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。