1、灰度图像直方图统计
直方图统计是图像处理中的一个非常重要的操作。VTK中实现直方图统计功能的filter是vtkImageAccumulate。其将每个组分的数值范围划分为离散的间隔,然后统计每个灰度间隔上的像素数目。vtkImageAccumulate输入和输出都是vtkImageData类型,因此直方图也可以看做是一幅图像;对于输入图像的像素数据类型可以是任意的,但是最大支持3个组分像素类型,而输出图像的像素数据类型为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()
{
vtkSmartPointerreader = vtkSmartPointer::New();//读入一幅灰度图像
reader->SetFileName("data\\lena-gray.jpg");
reader->Update();
int bins = 16; //直方图一维数组的维数
int comps = 1;
vtkSmartPointerhistogram = vtkSmartPointer::New();
histogram->SetInputData(reader->GetOutput());
histogram->SetComponentExtent(0, bins - 1, 0, 0, 0, 0); //6个参数是RGB3个组分直方图的最大最小值
histogram->SetComponentOrigin(0, 0, 0); //函数设置的是统计每个组分直方图时的起始灰度值
histogram->SetComponentSpacing(256.0 / bins, 0, 0); //设置直方图每个间隔代表的灰度范围
histogram->Update();
int* output = static_cast(histogram->GetOutput()->GetScalarPointer());
vtkSmartPointer frequencies = vtkSmartPointer::New();
frequencies->SetNumberOfComponents(1);
for (int j = 0; j < bins; ++j)
{
for (int i = 0; i < comps; ++i)
{
frequencies->InsertNextTuple1(*output++);
}
}
vtkSmartPointer dataObject = vtkSmartPointer::New();
dataObject->GetFieldData()->AddArray(frequencies);
/*******************************************************/
vtkSmartPointer barChart = vtkSmartPointer::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数组第一个元素的值
}
}
vtkSmartPointer imgActor = vtkSmartPointer::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 barRender = vtkSmartPointer::New();
barRender->SetViewport(barView);
barRender->AddActor(barChart);
barRender->SetBackground(1, 1, 1);
vtkSmartPointer imgRender = vtkSmartPointer::New();
imgRender->SetViewport(imgView);
imgRender->AddActor(imgActor);
imgRender->SetBackground(1, 1, 1);
vtkSmartPointer renderWindow = vtkSmartPointer::New();
renderWindow->AddRenderer(barRender);
renderWindow->AddRenderer(imgRender);
renderWindow->SetSize(640, 320);
renderWindow->Render();
renderWindow->SetWindowName("Gray-Image Histogram");
vtkSmartPointer rwi = vtkSmartPointer::New();
rwi->SetRenderWindow(renderWindow);
rwi->Initialize();
rwi->Start();
return EXIT_SUCCESS;
}
运行结果:
代码分析:
首先是读入一副灰度图像,一般的灰度图像的灰度范围为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。
2、彩色图像直方图统计
彩色图像由于内部有三个通道,不能直接计算直方图,需要提取RGB三个通道数据,分别计算直方图。每个通道计算直方图的方法与灰度图像直方图计算方法一致。
#include
#include
#include
#include //vtkXYPlotActor类可以用来显示二维曲线,它可以接收多个输入数据
#include
#include
#include
#include
#include
#include
#include
#include
#include
//测试图像:../data/lena.bmp
int main()
{
vtkSmartPointer reader = vtkSmartPointer::New();
reader->SetFileName("data//lena.bmp");
reader->Update();
int numComponents = reader->GetOutput()->GetNumberOfScalarComponents();
//**********************************************************************/
vtkSmartPointer plot = vtkSmartPointer::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 extract = vtkSmartPointer::New();
extract->SetInputConnection(reader->GetOutputPort()); //彩色图像不能直接计算直方图,因此需要先通过vtkImageExtractComponents来提取每个通道图像
extract->SetComponents(i);
extract->Update();
double range[2];
extract->GetOutput()->GetScalarRange(range);
int extent = static_cast(range[1]) - static_cast(range[0]) - 1;
vtkSmartPointer histogram = vtkSmartPointer::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),每个灰度计算统计一个频率
histogram->SetIgnoreZero(1); //统计直方图时,像素值为0的像素不进行统计
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 renderer = vtkSmartPointer::New();
renderer->AddActor(plot);
renderer->SetBackground(1.0, 1.0, 1.0);
vtkSmartPointer renderWindow = vtkSmartPointer::New();
renderWindow->AddRenderer(renderer);
renderWindow->SetSize(640, 480);
renderWindow->Render();
renderWindow->SetWindowName("ImageAccumulateExample2");
vtkSmartPointer interactor = vtkSmartPointer::New();
interactor->SetRenderWindow(renderWindow);
interactor->Initialize();
interactor->Start();
return EXIT_SUCCESS;
}
运行结果:
代码分析:
注:此文知识学习笔记,仅记录完整程序和实现结果,具体原理参见:
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图形图像开发进阶[M]. 机械工业出版社, 2015.