5.8 直方图统计
5.8.1 灰度圖像直方圖
直方圖統計是圖像處理中的一個非常重要的操作。VTK中實現直方圖統計功能的filter是vtkImageAccumulate。其將每個組分的數值范圍劃分為離散的間隔,然后統計每個灰度間隔上的像素數目。vtkImageAccumulate輸入和輸出都是vtkImageData類型,因此直方圖也可以看做是一幅圖像;對于輸入圖像的像素數據類型可以是任意的,但是最大支持3個組分像素類型,而輸出圖像的像素數據類型為int型。一個灰度圖像的直方圖為一個一維圖像。首先來看一下怎么計算灰度圖像直方圖。
?
?? 1:?????vtkSmartPointer<vtkJPEGReader> reader =
?? 2:?????????vtkSmartPointer<vtkJPEGReader>::New();
?? 3:?????reader->SetFileName ( " lena2.jpg" );
?? 4:?????reader->Update();
?? 5:??
?? 6:?????int bins = 16;
?? 7:??
?? 8:?????vtkSmartPointer<vtkImageAccumulate> histogram =
?? 9:?????????vtkSmartPointer<vtkImageAccumulate>::New();
? 10:?????histogram->SetInput(reader->GetOutput());
? 11:?????histogram->SetComponentExtent(0, bins-1, 0, 0, 0, 0);
? 12:?????histogram->SetComponentOrigin(0, 0, 0);
? 13:?????histogram->SetComponentSpacing(256/bins, 0, 0);
? 14:?????histogram->Update();
? 15:??
? 16:?????vtkSmartPointer<vtkIntArray> frequencies =
? 17:?????????vtkSmartPointer<vtkIntArray>::New();
? 18:??
? 19:?????frequencies->SetNumberOfComponents(1);
? 20:?????frequencies->SetNumberOfTuples(bins);
? 21:?????int* output = static_cast<int*>(histogram->GetOutput()->GetScalarPointer());
? 22:??
? 23:?????for(int j = 0; j < bins; ++j)
? 24:?????{
? 25:?????????frequencies->SetTuple1(j, *output++);
? 26:?????}
? 27:??
? 28:?????vtkSmartPointer<vtkDataObject> dataObject =
? 29:?????????vtkSmartPointer<vtkDataObject>::New();
? 30:?????dataObject->GetFieldData()->AddArray( frequencies );
? 31:??
? 32:?????vtkSmartPointer<vtkBarChartActor> barChart =
? 33:?????????vtkSmartPointer<vtkBarChartActor>::New();
? 34:??
? 35:?????barChart->SetInput(dataObject);
? 36:?????barChart->SetTitle("Histogram");
? 37:?????barChart->GetPositionCoordinate()->SetValue(0.05,0.05,0.0);
? 38:?????barChart->GetPosition2Coordinate()->SetValue(0.95,0.95,0.0);
? 39:?????barChart->GetProperty()->SetColor(1,1,1);
? 40:??
? 41:?????barChart->GetLegendActor()->SetNumberOfEntries(
? 42:?????????????dataObject->GetFieldData()->GetArray(0)->GetNumberOfTuples());
? 43:?????barChart->LegendVisibilityOff();
? 44:?????barChart->LabelVisibilityOff();
? 45:??
? 46:?????double red[3] = {1, 0, 0 };
? 47:?????for(int i = 0; i < bins; ++i)
? 48:?????{
? 49:?????????barChart->SetBarColor(i, red );
? 50:?????}
? 51:??
? 52:?????vtkSmartPointer<vtkRenderer> renderer =
? 53:?????????vtkSmartPointer<vtkRenderer>::New();
? 54:?????renderer->AddActor(barChart);
? 55:??
? 56:?????vtkSmartPointer<vtkRenderWindow> renderWindow =
? 57:?????????vtkSmartPointer<vtkRenderWindow>::New();
? 58:?????renderWindow->AddRenderer(renderer);
? 59:?????renderWindow->SetSize(640, 480);
? 60:??
? 61:?????vtkSmartPointer<vtkRenderWindowInteractor> interactor =
? 62:?????????vtkSmartPointer<vtkRenderWindowInteractor>::New();
? 63:?????interactor->SetRenderWindow(renderWindow);
? 64:?????interactor->Initialize();
? 65:?????interactor->Start();
?
下面來分析一下代碼。首先是讀入一副灰度圖像,一般的灰度圖像的灰度范圍為0-255。定義了一個變量bins = 16,表示要圖像灰度范圍上的間隔數目,也可以理解為直方圖一維數組的維數。然后定義vtkImageAccumulate對象,并設置輸入數據為我們讀入的圖像數據,接著調用了三個函數:
SetComponentExtent(0,bins-1, 0, 0, 0, 0),該函數設置要計算每個組分的直方圖的最小和最大值。vtkImageAccumulate最大支持像素值為三個組分(如RGB圖像)的直方圖,支持共有六個參數。分別表示每個組分的直方圖最小和最大值。該例中由于計算的是灰度圖像直方圖,只有一個組分,因此第二個和第三個組分都設置為0;而第一組分直方圖維數為bins = 16,那么其最小和最大范圍為0和bins-1。
SetComponentOrigin(0,0, 0),該函數設置的是統計每個組分直方圖時的起始灰度值,這里設置為0,表示灰度從0開始統計直方圖。同樣,vtkImageAccumulate最大支持像素值為三個組分,這里也要設置三個參數。如果圖像的灰度范圍為[1000, 2000],那么計算直方圖時,其起始灰度應該設置為1000。
SetComponentSpacing(16,0, 0),設置直方圖每個間隔代表的灰度范圍,例如當一個圖像灰度范圍為[1000, 2000],統計直方圖的間隔數bins為100時,那么對應的space應該設置為SetComponentSpacing(100, 0, 0)。
參數設置完畢后執行Update()即可計算直方圖。前面已經提到過,vtkImageAccumulate的輸出結果也是一個vtkImageData類型,這樣就可以方便的訪問圖像的每個數據。圖像像素訪問前面已經介紹過,這里就不在重述。需要注意的是輸出直方圖圖像的數據類型為int。
這里再順便簡單的介紹一下直方圖的顯示。雖然vtkImageAccumulate的輸出類型為vtkImageData但是并不能直接按照圖像的方式進行顯示。VTK中定義了vtkBarChartActor用來顯示條形圖,因此可以利用其來顯示直方圖。但是該類接收的數據類型為vtkDataObject類型,因此需要先將直方圖數據進行轉換。首先將直方圖數組存儲到vtkIntArray數組frequencies中,通過函數vtkDataObject函數GetFieldData()->AddArray(frequencies)將其添加到vtkDataObject對象中。vtkBarChartActor對象接收vtkDataObject對象作為輸入,另外還需要設置圖表的名字,顏色等,需要注意兩個函數:
barChart->GetPositionCoordinate()->SetValue(0.05,0.05,0.0);
barChart->GetPosition2Coordinate()->SetValue(0.95,0.95,0.0);
這里設置的是窗口中顯示圖表的所在矩形的左下角點和右上角點坐標,VTK的坐標系原點位于左下角點,設置時需要格外注意。設置完畢后,即可定義相應的vtkRenderer,vtkRenderWindow和vtkRenderWindowInteractor對象顯示圖像直方圖。本例的顯示效果如下:
?
圖5.20 VTK直方圖顯示
?
5.8.2 彩色圖像直方圖
彩色圖像由于內部有三個通道,不能直接計算直方圖,需要提取RGB三個通道數據,分別計算直方圖。每個通道計算直方圖的方法與灰度圖像直方圖計算方法一致。下面給出相關代碼:
1:????? vtkSmartPointer<vtkBMPReader>reader =
?? 2:?????????vtkSmartPointer<vtkBMPReader>::New();
?? 3:?????reader->SetFileName ( "lena.bmp" );
?? 4:?????reader->Update();
?? 5:??
?? 6:?????int numComponents =reader->GetOutput()->GetNumberOfScalarComponents();
?? 7:??
?? 8:?????vtkSmartPointer<vtkXYPlotActor> plot =
?? 9:?????????vtkSmartPointer<vtkXYPlotActor>::New();
? 10:?????plot->ExchangeAxesOff();
? 11:?????plot->SetLabelFormat( "%g" );
? 12:?????plot->SetXTitle( "Value" );
? 13:?????plot->SetYTitle( "Frequency" );
? 14:?????plot->SetXValuesToValue();
? 15:??
? 16:?????double colors[3][3] = {
? 17:?????????{ 1, 0, 0 },
? 18:?????????{ 0, 1, 0 },
? 19:?????????{ 0, 0, 1 }
? 20:?? ???};
? 21:??
? 22:?????const char* labels[3] = { "Red", "Green","Blue" };
? 23:??
? 24:?????int xmax = 0;
? 25:?????int ymax = 0;
? 26:??
? 27:?????for( int i = 0; i < numComponents; ++i )
? 28:?????{
? 29:?????????vtkSmartPointer<vtkImageExtractComponents> extract =
? 30:?????????????vtkSmartPointer<vtkImageExtractComponents>::New();
? 31:?????????extract->SetInputConnection( reader->GetOutputPort() );
? 32:?????????extract->SetComponents( i );
? 33:?????????extract->Update();
? 34:??
? 35:?????????double range[2];
? 36:?????????extract->GetOutput()->GetScalarRange( range );
? 37:?????????int extent =static_cast<int>(range[1])-static_cast<int>(range[0])-1;
? 38:??
? 39:?????????vtkSmartPointer<vtkImageAccumulate> histogram =
? 40:?????????????vtkSmartPointer<vtkImageAccumulate>::New();
? 41:?????????histogram->SetInputConnection( extract->GetOutputPort() );
? 42:?????????histogram->SetComponentExtent( 0,extent, 0,0, 0,0);
? 43:?????????histogram->SetComponentOrigin( range[0],0,0 );
? 44:?????????histogram->SetComponentSpacing( 1,0,0 );
? 45:?????????histogram->SetIgnoreZero( 1 );
? 46:?????????histogram->Update();
? 47:??
? 48:?????????if( range[1] > xmax )
? 49:?????????{
? 50:????????????? xmax = range[1];
? 51:? ????????}
? 52:?????????if( histogram->GetOutput()->GetScalarRange()[1] > ymax )
? 53:?????????{
? 54:????????????? ymax =histogram->GetOutput()->GetScalarRange()[1];
? 55:?????????}
? 56:??
? 57:?????????plot->AddInput( histogram->GetOutput() );
? 58:?????????plot->SetPlotColor(i,colors[i]);
? 59:?????????plot->SetPlotLabel(i,labels[i]);
? 60:?????????plot->LegendOn();
? 61:?????}
? 62:??
? 63:?????plot->SetXRange( 0, xmax);
? 64:?????plot->SetYRange( 0, ymax);
? 65:??
? 66:?????vtkSmartPointer<vtkRenderer> renderer =
? 67:?????????vtkSmartPointer<vtkRenderer>::New();
? 68:?????renderer->AddActor(plot);
? 69:??
? 70:?????vtkSmartPointer<vtkRenderWindow> renderWindow =
? 71:?????????vtkSmartPointer<vtkRenderWindow>::New();
? 72:?????renderWindow->AddRenderer( renderer );
? 73:?????renderWindow->SetSize(640, 480);
? 74:??
? 75:?????vtkSmartPointer<vtkRenderWindowInteractor> interactor =
? 76:?????????vtkSmartPointer<vtkRenderWindowInteractor>::New();
? 77:?????interactor->SetRenderWindow( renderWindow );
? 78:??
? 79:?????interactor->Initialize();
? 80:?????interactor->Start();
?
上面代碼說明了怎樣計算彩色圖像直方圖。計算直方圖的主要代碼段是27-61行。由于彩色圖像不能直接計算直方圖,因此需要先通過vtkImageExtractComponents來提取每個通道圖像,然后再利用vtkImageAccumulate統計直方圖。在本例中計算直方圖的間隔取(1, 0, 0),即每個灰度計算統計一個頻率,而且灰度起點為圖像的最小灰度值,這樣間隔的個數即為:最大灰度值減去最小灰度值,再減1,如第37行代碼。同時,設置了SetIgnoreZero()為1,即在統計直方圖時,像素值為0的像素不進行統計。
在灰度圖像直方圖實例中,我們使用的是vtkBarChartActor柱狀圖來顯示直方圖,在本例中則使用vtkXYPlotActor曲線來表示直方圖,這里做一簡單介紹。vtkXYPlotActor類可以用來顯示二維曲線,它可以接收多個輸入數據,如本例中我們輸入了三條曲線,分別是圖像紅色分量直方圖區域,綠色分量直方圖曲線和藍色分量直方圖曲線。SetXRange()和SetYRange()用來設置X軸和Y軸的數據范圍,另外還可以設置X軸和Y軸的名字,曲線的標題等屬性,詳細可以查閱vtkXYPlotActor類的文檔。vtkXYPlotActor類是一個vtkActor2D的子類,因此定義相應的vtkRenderer,vtkRenderWindow和vtkRenderWindowInteractor對象建立可視化管道來顯示圖像直方圖曲線。本例的顯示效果如下:其中,紅色曲線代碼紅色分量的直方圖,綠色代表綠色分量的直方圖曲線,藍色代碼藍色分量的直方圖曲線。
?
圖5.21 彩色圖像直方圖
5.9 圖像重采樣
圖像重采樣是指對采樣后形成的由離散數據組成的數字圖像按所需的像元位置或像元問距重新采樣,以構成幾何變換后的新圖像。重采樣過程本質上是圖像恢復過程,它用輸入的離散數字圖像重建代表原始圖像二維連續函數,再按新的像元間距和像元位置進行采樣。其數學過程是根據重建的連續函數(曲面),用周圍若干像元點的值估計或內插出新采樣點的值。圖像重采樣在圖像處理中應用非常廣泛,如SIFT特征提取。
圖像重采樣后圖像的維數會發生改變。當重采樣圖像小于原圖像維數時,稱為降采樣;當重采樣圖像維數大于原圖像時,稱為升采樣。VTK中可以方便的對圖像進行重采樣。vtkImageShrink3D類實現圖像降采樣。降采樣需要設置每個方向的采樣率,降采樣率越大,圖像越模糊。升采樣的原理與降采樣原理一致,只是增加采樣點數來增加圖像的維數。VTK中vtkImageMagnify來實現圖像的升采樣。下面看一個實例。
?
1:????? vtkSmartPointer<vtkBMPReader>reader =
??2:?????????vtkSmartPointer<vtkBMPReader>::New();
??3:????? reader->SetFileName (" lena.bmp" );
??4:????? reader->Update();
??5:??
??6:?????vtkSmartPointer<vtkImageShrink3D> shrinkFilter =
??7:?????????vtkSmartPointer<vtkImageShrink3D>::New();
??8:?????shrinkFilter->SetInputConnection(reader->GetOutputPort());
??9:?????shrinkFilter->SetShrinkFactors(20,20,1);
?10:?????shrinkFilter->Update();
?11:??
?12:?????vtkSmartPointer<vtkImageMagnify> magnifyFilter =
?13:????????? vtkSmartPointer<vtkImageMagnify>::New();
?14:?????magnifyFilter->SetInputConnection(reader->GetOutputPort());
?15:?????magnifyFilter->SetMagnificationFactors(10,10,1);
?16:?????magnifyFilter->Update();
?17:??
?18:????? int originalDims[3];
?19:????? reader->GetOutput()->GetDimensions(originalDims);
?20:??
?21:????? double originalSpace[3];
?22:?????reader->GetOutput()->GetSpacing(originalSpace);
?23:??
?24:????? int shrinkDims[3];
?25:?????shrinkFilter->GetOutput()->GetDimensions(shrinkDims);
?26:??
?27:????? double shrinkSpace[3];
?28:?????shrinkFilter->GetOutput()->GetSpacing(shrinkSpace);
?29:??
?30:????? int magnifyDims[3];
?31:?????magnifyFilter->GetOutput()->GetDimensions(magnifyDims);
?32:??
?33:????? double magnifySpace[3];
?34:?????magnifyFilter->GetOutput()->GetSpacing(magnifySpace);
?35:??
?36:????? std::cout<<"原圖圖像維數????? :"<<originalDims[0]<< " "<<originalDims[1]<<""<<originalDims[2]<<std::endl;
?37:????? std::cout<<"原圖圖像像素間隔? :"<<originalSpace[0]<< " "<<originalSpace[1]<<""<<originalSpace[2]<<std::endl;
?38:????? std::cout<<"降采樣圖像維數??? :"<<shrinkDims[0]<< " "<<shrinkDims[1]<<""<<shrinkDims[2]<<std::endl;
?39:????? std::cout<<"降采樣圖像像素間隔:"<<shrinkSpace[0]<< " "<<shrinkSpace[1]<<""<<shrinkSpace[2]<<std::endl;
?40:????? std::cout<<"升采樣圖像維數??? :"<<magnifyDims[0]<< " "<<magnifyDims[1]<<""<<magnifyDims[2]<<std::endl;
?41:????? std::cout<<"升采樣圖像像素間隔:"<<magnifySpace[0]<< " "<<magnifySpace[1]<<""<<magnifySpace[2]<<std::endl;
?
vtkImageShrink3D使用時通過SetShrinkFactors()設置X、Y和Z方向的采樣率,參數為三個int類型數據。vtkImageMagnify的使用也是類似,通過SetMagnificationFactors設置相應的放大采樣率。上面例子中分別對原圖像維數縮小原來的1/20和放大為原來的10倍,執行結果如下:
?
圖5.22 圖像重采樣
?
從圖中可以看成,重采樣圖像已經變得十分模糊了。而升采樣圖像則變化不大。程序中輸出了原圖和重采樣圖像的維數和像素間隔,從輸出來看,原圖像的維數為512x512,縮小20倍后,圖像維數變為25x25。需要注意的是,原圖的像素間隔為(1,1,1),而重采樣后像素間隔變為(20, 20, 1),這是因為采樣是在世界坐標系下進行的,當采樣圖像的維數減小時,采樣的像素間隔也相應的變大,這樣保持圖像的區域不會發生改變。同樣的道理,當升采樣后圖像的維數變為原來的10倍,而像素間隔變為原來的十分之一。
另外,圖像的重采樣類有:vtkImageResample。
==========歡迎轉載,轉載時請保留該聲明信息==========
版權歸@東靈工作室所有,更多信息請訪問東靈工作室
教程系列導航:http://blog.csdn.net/www_doling_net/article/details/8763686
================================================
總結
- 上一篇: 5.10 图像运算
- 下一篇: 轨迹相似性度量之基于Hausdorff与