2018-11-20 10:40:48 这是一份20XX年的代码,大部分来自于itksnap。重新拿出来,一天的时间,深度解析,希望对这段时间有一个交待。
这里只有思路,没有多少能直接用的代码,相信对于真正需要的人能懂它的价值。如果想读源码,请转到开源的itksnap。
2018-11-20 14:56:29 可能会围绕这个软件写一系列的文章,希望能有头有尾。
技术方案:C++,opengl,cmake,itk,vtk,MFC(事实上是MFC和win32的混合编程)
编程语言用的是C++,没什么好说的,中间用到了面向对象、STL、泛型编程,大坑。这里的重头戏应该是在opengl,是本文中想说清楚的部分。至于MFC,新生代员工已经没听过这是什么东西了。cmake,itk,vtk,都属于高级工具流,唯手熟尔~
软件架构:实现了界面、逻辑、数据三家分离,每个功能模块有专门的类来管理UI,从MFC换到Qt,分分钟。
凡事总要有个开始,那就从VectorTypes这个类开始吧~
VectorTypes中引用了C++矩阵运算库VXL(类似eigen),代码中的iris_vector_fixed类继承与vxl中的vnl_vector_fixed。随后是2d与3d的宏定义:
// Common 2D vector types
typedef iris_vector_fixed<int,2> Vector2i;
typedef iris_vector_fixed<unsigned int,2> Vector2ui;
typedef iris_vector_fixed<long,2> Vector2l;
typedef iris_vector_fixed<unsigned long,2> Vector2ul;
typedef iris_vector_fixed<float,2> Vector2f;
typedef iris_vector_fixed<double,2> Vector2d;
// Common 3D vector types
typedef iris_vector_fixed<int,3> Vector3i;
typedef iris_vector_fixed<unsigned int,3> Vector3ui;
typedef iris_vector_fixed<long,3> Vector3l;
typedef iris_vector_fixed<unsigned long,3> Vector3ul;
typedef iris_vector_fixed<float,3> Vector3f;
typedef iris_vector_fixed<double,3> Vector3d;
这么做的意义在于最大程度的将图像矩阵运算的细节封装起来,使用成熟的矩阵计算方案,代码量少,好实现,可操控。
接下来要读的是OpenGLSliceTexture类。在传统魔法时代的opengl(以后统称gl)中,纹理映射是个不可小觑的概念,几乎所有与图相关的内容都是纹理。这个类就是一个从图像到纹理的转换器。
//从itkimage到gl的fliter
#include "itkImage.h"
#include "vectortypes.h"
#include <gl\gl.h>
#include <gl\glu.h>
类的最开始就定义了私有的图像类型和图像指针,格式按照itk中的泛型:
typedef itk::ImageBase<2> ImageBaseType;//定义基础itk格式的二维图像
typedef itk::SmartPointer<ImageBaseType> ImageBasePointer;//定义该图像格式的图像指针
//下面这两个函数几乎获得了图像的所有信息
template<class TPixel> void SetImage(itk::Image<TPixel,2> *inImage)
{
m_Image = inImage;
m_Image->GetSource()->UpdateLargestPossibleRegion();
m_Buffer = inImage->GetBufferPointer();
itk::Size<2> szImage = m_Image->GetLargestPossibleRegion().GetSize();
itk::Vector<float,2> spaceImage=m_Image->GetSpacing();
m_SliceSize=Vector2i(szImage[0],szImage[1]);
m_SliceSpacing=Vector2f(spaceImage[0],spaceImage[1]);
}
ImageBasePointer GetImage()
{
return m_Image;
}
调用gl函数将像素转换成纹理,亮点在最后的glTexSubImage2D:
void OpenGLSliceTexture::Update()
{
// Better have an image
if(m_Image.IsNull())
return;
// Update the image (necessary?)
if(m_Image->GetSource())
m_Image->GetSource()->UpdateLargestPossibleRegion();
// Promote the image dimensions to powers of 2
itk::Size<2> szImage = m_Image->GetLargestPossibleRegion().GetSize();
m_TextureSize = Vector2ui(1);
// Use shift to quickly double the coordinates
for (unsigned int i=0;i<2;i++)
while (m_TextureSize(i) < szImage[i])
m_TextureSize(i) <<= 1;
// Create the texture index if necessary
if(!m_IsTextureInitalized)
{
// Generate one texture
glGenTextures(1,&m_TextureIndex);
m_IsTextureInitalized = true;
}
// Select the texture for pixel pumping
glBindTexture(GL_TEXTURE_2D,m_TextureIndex);
// Properties for the texture
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_InterpolationMode );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, m_InterpolationMode );
// Turn off modulo-4 rounding in GL
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
// Allocate texture of slightly bigger size
glTexImage2D(GL_TEXTURE_2D, 0, m_GlComponents,m_TextureSize(0), m_TextureSize(1),
0, m_GlFormat, m_GlType, NULL);
// Copy a subtexture of correct size into the image
//关键在这里的动态纹理替换,比glTexImage2D或者gluBuild2DMipmaps高效
//void *m_Buffer= inImage->GetBufferPointer();
//m_Buffer就是图像的指针
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, szImage[0], szImage[1],
m_GlFormat, m_GlType, m_Buffer);
}
接下来是图像窗口实现类ImageView,该类的作用是将gl与win32视图绑定。该类将OpenGLSliceTexture *m_MainTexture当作成员变量传进去。
关键词:wglCreateContext
void ImageView::DrawImage()
{
wglMakeCurrent(m_hDC,m_hGLContext);
glViewport(0,0,m_rect.right,m_rect.bottom); // Reset The Current Viewport
glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
glLoadIdentity(); // Reset The Projection Matrix
gluOrtho2D(0,m_rect.right,0,m_rect.bottom);
glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
glLoadIdentity(); // Reset The Modelview Matrix
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Set the color to the background color
// glColor3dv(clrBackground.data_block());
typedef itk::ImageBase<2> ImageBase;
typedef itk::SmartPointer<ImageBase> ImageBasePointer;
ImageBasePointer m_Image=m_Volume->GetDisplaySlice(m_wid);
if(!m_Image.IsNull())
{
// Compute the position of the cross-hairs in display space
Vector3ui cursorImageSpace = m_Volume->GetCursorPosition();
m_CursorPos=cursorImageSpace;
Vector3f cursorDisplaySpace = m_ImageToDisplayTransform.TransformPoint(to_float(cursorImageSpace) + Vector3f(0.5f));
// Get the current slice number
m_ImageSliceIndex = cursorImageSpace[m_ImageAxes[2]];
m_DisplayAxisPosition = cursorDisplaySpace[2];
glPushMatrix();
drawMainTexture();//该函数中调用了OpenGLSliceTexture的draw函数
glPopMatrix();
}
glFlush();
SwapBuffers(m_hDC);
wglMakeCurrent(NULL,NULL);
}
接着读类BloodView,该类继承于MFC的窗口类CScrollView,开始从win32切换到MFC(好像有点复杂了,从itk到gl到win32再到mfc绕了一圈,但这种层层绑定的设计来源于作者的跨UI甚至跨平台,一旦有需求,将会以最小的代价换掉上层的GUI层,而下层则几乎不变)。
在该类中,将ImageView* m_ImageView;当作成员变量传了进去。
//事实上,顶层设计中,除了传传参数,并没有什么实际的内容。
void CBloodView::setSliceIndex(int newindex)
{
if(m_ImageView)
{
m_ImageView->SetSliceIndex(newindex);
m_ImageView->Update();
}
}
读到这里,你是不是以为这样结束了,nonono,还有分窗显示呢,于是设计了3个SliceViewXY /SliceViewXZ /SliceViewYZ 继承于BloodView。
这些类中的实现都是细节上个性化的控制了。包括与控件的交互响应。
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg void OnSliceFirst();
afx_msg void OnSliceLast();
afx_msg void OnSliceNext();
afx_msg void OnSlicePrev();
afx_msg void OnDestroy();
afx_msg void OnPaint();
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
afx_msg void OnRButtonUp(UINT nFlags, CPoint point);
参考文献:
http://www.itksnap.org/pmwiki/pmwiki.php
https://blog.csdn.net/darren817/article/details/79816374
http://vxl.sourceforge.net/
http://vxl.sourceforge.net/#download
https://bitbucket.org/
https://bitbucket.org/libgd/gd-libgd/downloads/
https://blog.csdn.net/zhoubl668/article/details/4563634
http://blog.sina.com.cn/s/blog_eb4d725a0102vwkf.html