Vaa3D 3D-Viewer 代码结构概览

本文主要介绍了Vaa3D的3D Viewer中与图像渲染和标注相关的代码结构,涵盖UI、渲染器、数据结构等方面。详细分析了GL窗口类、Renderer_gl类的成员函数,以及部分函数的具体代码,如V3dR_GLWidget的旋转函数和Renderer的渲染函数等。

结构概览

Vaa3D的3d viewer中,所有跟图像渲染和标注相关的代码,基本都在3drenderer文件夹内,基础数据结构、IO函数、部分类的规范定义在basic_c_fun文件夹内,主要代码结构概览本人整理如下几幅图用以直观理解。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

注:笔记中出现的代码,都经过一定的删减。

UI

3d viewer窗口类

3drenderer/v3dr_mainwindow.h中,定义了3d viewer窗口类。

class V3dR_MainWindow : public QWidget

QWidget窗口实例可以通过下面语句激活,

mainwindow_sample->activateWindow();

3d viewer窗口中,定义了,

V3dR_GLWidget * getGLWidget() {
   
   return glWidget;}

是图形窗口。

GL窗口类

3drenderer/v3dr_glwidget.h中,定义了gl窗口类,用来显示、标注图像。

class V3dR_GLWidget :  public QOpenGLWidget, protected QOpenGLFunctions , public View3DControl

这里的View3DControl,定义在basic_c_fun/basic_view3d.h中,里面声明了3D图像基础操作的一系列虚函数。
V3dR_GLWidget中定义的主要对象有,

iDrawExternalParameter* _idep;//
QWidget *mainwindow;
Renderer* renderer;//
QString data_title;
QString dropUrl;
static V3dr_colormapDialog* colormapDlg;
static V3dr_surfaceDialog*  surfaceDlg;
int neuronIndex;
bool show_progress_bar;
MainWindow *v3d_mainwindow;
int currentPluginState;
map<int, void(*)(void*)> pluginLeftMouseFuncs;

此外,注意所有包含在,

#ifdef _NEURON_ASSEMBLER_
/*一堆代码*/
#endif

中的代码,都是跟Terafly相关的。

渲染器相关的成员函数

V3dR_GLWidget类中,首先声明了一些列跟渲染器相关的方法。

void V3dR_GLWidget::deleteRenderer() {
   
   makeCurrent(); DELETE_AND_ZERO(renderer);} //删除渲染器
void V3dR_GLWidget::createRenderer() {
   
   makeCurrent(); deleteRenderer(); initializeGL();} //创建渲染器(初始化)
void V3dR_GLWidget::choiceRenderer()//根据OpenGL的版本选择渲染器
{
   
   
    /*...*/
    if (1 && supported_GLSL())
    {
   
   
        renderer = new Renderer_gl2(this);
    }
    else  // this comment for special version without GL 2.0 support
    if (1) //strlen(glversion)>3 && glversion[0]>='1' && glversion[1]=='.' && glversion[2]>='0')
    {
   
   
        renderer = new Renderer_gl1(this);
    }
    else
    {
   
   
        renderer = new Renderer(this);
    }
}
void V3dR_GLWidget::settingRenderer()//在渲染器导入数据(renderer->setupData)前需要先设置参数
void V3dR_GLWidget::preparingRenderer()//渲染器导入数据

视图窗口相关的成员函数

之后是视图窗口的各种方法。

void V3dR_GLWidget::initializeGL()

这个方法是视图窗口的初始化,该方法最后调用了choiceRenderer()、settingRenderer()和preparingRenderer()。

void V3dR_GLWidget::resizeGL(int width, int height)

这个方法用于调整视图窗口的大小,里面调用了renderer->setupView()。

void V3dR_GLWidget::paintGL()

暂时还没学过OpenGL,只知道里面有旋转和平移。

void V3dR_GLWidget::customEvent(QEvent* e)

应该涉及自定义事件的控制。

bool V3dR_GLWidget::event(QEvent* e)

应该涉及鼠标悬停事件的处理,里面用pos保存了鼠标位置。

void V3dR_GLWidget::enterEvent(QEvent*)

鼠标进入视图。

void V3dR_GLWidget::leaveEvent(QEvent*)

鼠标离开视图。

void V3dR_GLWidget::focusInEvent(QFocusEvent*)

猜测是,鼠标在拖动中(点击移动,例如绘画等)。

void V3dR_GLWidget::focusOutEvent(QFocusEvent*)

猜测是,鼠标离开拖动状态。

绘图相关的成员函数

void V3dR_GLWidget::paintEvent(QPaintEvent *event)
{
   
   
    //QOpenGLWidget_proxy::paintEvent(event);
//	if (! mouse_in_view) //TODO:  use change of viewing matrix
//	{
   
   
//		_still = true;
//		QOpenGLWidget_proxy::paintEvent(event);
//		_still = false;
//	}
//	else
    {
   
   
        _still = false;
        QOpenGLWidget::paintEvent(event);

        if (needStillPaint()) //pending
        {
   
   
            _stillpaint_pending=true;
        }
    }
}

绘图事件,注释掉的部分是因为不需要判断鼠标是否在视图窗口内。猜测是在一个绘图步骤结束后调用触发的事件,内部调用了QOpenGLWidget::paintEvent。

bool V3dR_GLWidget::needStillPaint()
{
   
   
    return  (renderer && renderer->tryTexStream == 1);
}
//注释了
void V3dR_GLWidget::stillPaint()
{
   
   
    if (_still)  return; // avoid re-enter
    if (! _stillpaint_pending) return;

//	if (QCoreApplication::hasPendingEvents())
//	{
   
   
//		_still = false;
//		_stillpaint_pending = true;
//		return;    //continue pending if event loop is busy
//	}
//	else // here system must be idle
    {
   
   
        qDebug()<<"debug in v3dr_glwidget.cpp V3dR_GLWidget::stillPaint()";
        still_timer.stop();
        _still = true;
            DO_updateGL(); // update at once, stream texture for full-resolution
        _still = false;
        _stillpaint_pending = false;
        still_timer.start(still_timer_interval); //restart timer
    }
}

这里,涉及到的变量,

bool _still, _stillpaint_pending;

还没弄懂,猜测stillpaint是指静态绘画,也就是执行完完整的鼠标移动步骤后进行绘画,猜测这三部分代码,将一个绘画步骤分为多个stillpaint,_still变量代表是否动作未完成,_stillpaint_pending变量代表是否stillpaint未完成(待定)。但还需要看应用的代码,这里实在不确定。

鼠标事件相关的成员函数

void V3dR_GLWidget::mousePressEvent(QMouseEvent *event)
{
   
   
    mouse_held = 1;

    if (event->button()==Qt::LeftButton)
    {
   
   
        lastPos = event->pos();
        t_mouseclick_left = clock();
        if(pluginLeftMouseFuncs.find(currentPluginState) != pluginLeftMouseFuncs.end())
        {
   
   
            void(*mouse_func)(void*);
            mouse_func = pluginLeftMouseFuncs[currentPluginState];
            (*mouse_func)((void*)this);
        }
    }

    if (!isoperating&&event->button()==Qt::RightButton && renderer) //right-click
    {
   
   
        int x = event->x();
        int y = event->y();
        #ifdef _ENABLE_MACX_DRAG_DROP_FIX_
        x = 2 * x;
        y = 2 * y;
        #endif

        if (renderer->hitPoint(x,y))  //pop-up menu (selectObj) or marker definition (hitPen)
        {
   
   
           updateTool();
           //POST_updateGL();
        }
        //updateTool();
        POST_updateGL(); //display result after menu   //csz solve the blue screen don't know the reason
    }
}

鼠标点击事件,左键点击时,调用鼠标左键点击反应函数列表中,对应当前状态的函数。右键点击时,打开工具栏。

void V3dR_GLWidget::mouseReleaseEvent(QMouseEvent *event)
{
   
   
    mouse_held = 0;

    if (event->button()==Qt::RightButton && renderer) //right-drag end
    {
   
   
        (renderer->movePen(event->x(), event->y(), false)); //create curve or nothing
        //qDebug() << "done drawing\n";
        updateTool();

        POST_updateGL(); //update display of curve
    }
}

鼠标松开事件,如果是右键移动松开触发的,创建曲线,更新工具栏,显示曲线。

void V3dR_GLWidget::mouseMoveEvent(QMouseEvent *event)
{
   
   
    int dx = event->x() - lastPos.x();
    int dy = event->y() - lastPos.y();
    lastPos = event->pos();

    if ((event->buttons() & Qt::RightButton) && renderer) //right-drag for 3d curve
        if ( ABS(dx) + ABS(dy) >=2 )
    {
   
   
        /*一些代码*/
    }

    if (event->buttons() & Qt::LeftButton)
    {
   
   
        int xRotStep = MOUSE_ROT(dy, MIN(viewW,viewH));
        int yRotStep = MOUSE_ROT(dx, MIN(viewW,viewH));
        int xShiftStep = MOUSE_SHIFT(dx, viewW);
        int yShiftStep = MOUSE_SHIFT(dy, viewH);

        alt_rotation = (IS_ALT_MODIFIER); // alt+mouse control alternate marker center rotation, 081104

        // mouse control view space, transformed to model space, 081026
        if (IS_TRANSLATE_MODIFIER) // shift+mouse control view space translation, 081104
        {
   
   
            setXShift(_xShift + xShiftStep);// move +view -model
            setYShift(_yShift - yShiftStep);// move -view +model
        }
        else if (IS_MODEL_MODIFIER) // ctrl+mouse control model space rotation, 081104
        {
   
   
            //modelRotation(yRotStep, xRotStep, 0); //swap y,x
            modelRotation(xRotStep, yRotStep, 0);
        }
        else // default mouse controls view space rotation
        {
   
   
            viewRotation(xRotStep, yRotStep, 0);
        }
    }
}

鼠标移动事件,如果是鼠标右键按住移动则创建曲线,如果是鼠标左键按住移动则先计算旋转移动角度和平移移动长度,之后根据情况,判断是旋转标记中心,还是平移,还是旋转,还是普通的视角转动。

void V3dR_GLWidget::wheelEvent(QWheelEvent *event)
{
   
   
    /*一大堆代码*/
}

滚轮事件,一般是触发缩放。但按住shift或alt则触发z轴平移或旋转。

键盘事件相关的成员函数

键盘事件分为键盘按压事件和键盘释放事件,但只有数字键有释放事件。

void V3dR_GLWidget::handleKeyPressEvent(QKeyEvent * e)
{
   
   
    switch (e->key())
    {
   
   
    /*一堆case语句*/
    }
    default:
        QOpenGLWidget_proxy::keyPressEvent(e);
        break;
}
update(); //must be here for correct MarkerPos's view matrix
}

e->key()的值有如下几种情况,

基础操作
case Qt::Key_BracketLeft:
    {
   
   
        if (IS_MODEL_MODIFIER) // alt-mouse to control model space rotation
            modelRotation(0, 0, +5);
        else
            viewRotation(0, 0, +5);
    }
    break;
case Qt::Key_BracketRight:
/*一串代码*/

左方括号键,视角沿z方向旋转,或IS_MODEL_MODIFIER条件下,模型沿z方向旋转。与之对应的有Qt::Key_BracketRight。

case Qt::Key_Left: //arrows key must use WITH_?_MODIFIER
    {
   
   
        if (WITH_MODEL_MODIFIER)
            modelRotation(0, -5, 0);
        else if (WITH_TRANSLATE_MODIFIER)
            setXShift(_xShift -1);// move -model
        else
            setXShift(_xShift +1);// move +view
    }
    break;
case Qt::Key_Right:
/*一串代码*/
case Qt::Key_Up:
/*一串代码*/
case Qt::Key_Down:
/*一串代码*/

左方向键,注释中提到,方向键必须用WITH_?_MODIFIER。WITH_MODEL_MODIFIER下模型沿y方向旋转,WITH_TRANSLATE_MODIFIER下模型沿x方向平移,否则视角沿x方向平移。与之对应的有Qt::Key_Right、Qt::Key_Up、Qt::Key_Down。

case Qt::Key_Minus:
    {
   
   
        setZoom(_zoom - 10); // zoom out
    }
    break;
case Qt::Key_Equal:
/*一串代码*/

减号键(-),缩小。与之对应的有 Qt::Key_Equal。

case Qt::Key_Underscore:
    {
   
   
        emit changeMarkerSize(_markerSize - 1);
    }
    break;
case Qt::Key_Plus:
/*一串代码*/

下划线键,缩小标记尺寸。与之对应的有Qt::Key_Plus。
还有一些按键不一一列举解释,见下表。

Qt对象 按键
Qt::Key_Backspace 退格键
Qt::Key_Backslash 反斜杠键
Qt::Key_Comma 逗号键
Qt::Key_Period 句号键
Qt::Key_Slash 斜杠键
Qt::Key_Less 小于号键
Qt::Key_Greater 大于号键
Qt::Key_Question 问号键

这些按键在定义的时候,很多会通过发送信号来执行对应操作(emit语句)。

之后是一些快捷键的定义,

OpenGL快捷键
case Qt::Key_B:
    if (IS_CTRL_MODIFIER)
    {
   
   
        setBright();
    }else if (IS_ALT_MODIFIER)
    {
   
   
        emit changeEditinput("Alt+B: Drawing BBox");
    if(!isoperating)
        isoperating=true;
        callStrokeCurveDrawingBBoxes();//For serial BBoxes curve drawing shortcut
    }
    break;

alt+B时,发送 changeEditinput(“Alt+B: Drawing BBox”) 信号(使右下角的文本框显示输入的字符串),进入标记模式。

case Qt::Key_R:
    if (IS_CTRL_MODIFIER)
    {
   
   
        reloadData();
    }
        else
    {
   
   
        returncheckmode();
    }
    break;

按R键进入检查模型(我自己用的时候显示没找到文件),ctrl+R重新载入数据。

case Qt::Key_U:
    if (IS_CTRL_MODIFIER)
    {
   
   
        updateWithTriView();
    }
    break;

ctrl+U更新三视图。

高级OpenGL快捷键
case Qt::Key_I:
    if ( WITH_SHIFT_MODIFIER && //advanced
        WITH_CTRL_MODIFIER
        )
    {
   
   
        showGLinfo();
    }
    else if (renderer)
    {
   
   
        Renderer_gl1* thisRenderer = static_cast<Renderer_gl1*>(this->getRenderer());
        if (thisRenderer->selectMode == Renderer::smDeleteMultiNeurons)
        {
   
   
            thisRenderer->setDeleteKey(1);
            thisRenderer->deleteMultiNeuronsByStroke();
        }
    }
    break;

shift+ctrl+I显示GL窗口信息,其他有什么用暂时不知道。

case Qt::Key_G:
    if ( WITH_SHIFT_MODIFIER && //advanced
         WITH_CTRL_MODIFIER
        )
    {
   
   
        toggleShader();
    }else if (IS_ALT_MODIFIER)
    {
   
   
        emit changeEditinput("Alt+G: Drawing Global");
        if(!isoperating)
            isoperating=true;
        callStrokeCurveDrawingGlobal();//For Global optimal curve drawing shortcut
    }else
    {
   
   
        emit changeEditinput("Alt+G: GDTraing");
        if(!isoperating)
            isoperating=true;
        callGDTracing();
    }
    break;

shift+ctrl+G对图像做toggleShader(切换着色器)并显示。alt+G也是标注神经元,但目前只知道是global模式的标注,具体是什么还不知道。其他方式按G进入GDTracing标注模式,但具体怎么用还是不知道。

case Qt::Key_F:
    if (IS_CTRL_MODIFIER)
    {
   
   
        toggleTexFilter();
    }
    else if (IS_ALT_MODIFIER)
    {
   
   
    /*非常长的一大串代码*/
    }
    break;
纹理操作

ctrl+F对图像做toggleTexFilter(切换纹理过滤器)并显示。alt+F跟Terafly有关。

case Qt::Key_F1:
/*一串代码*/
case Qt::Key_F2:
/*一串代码*/
case Qt::Key_F3:
/*一串代码*/

这三个也都跟Terafly有关,暂时不看。

case Qt::Key_E:
    if (IS_ALT_MODIFIER)
    {
   
   
        emit changeEditinput("Alt+E");
        if(!isoperating)
            isoperating=true;
        toggleEditMode();
    }
    else if (IS_SHIFT_MODIFIER)
    {
   
   
    /*一串代码*/
    }
    else
    {
   
   
        callcheckmode();
    }
    break;

alt+E切换编辑/非编辑模式,shift+E跟Terafly有关,暂时不看。

case Qt::Key_T:
    if ( WITH_SHIFT_MODIFIER && //advanced
         WITH_CTRL_MODIFIER
        )
    {
   
   
        toggleTex2D3D();
    }else if (IS_ALT_MODIFIER)
    {
   
   
        emit changeEditinput("Alt+T");
        if(!isoperating)
            isoperating=true;
        callStrokeRetypeMultiNeurons();//For multiple segments retyping shortcut
    }
    else if (renderer)
    {
   
   
        /*一串代码*/
    }
    else
        callAutoTracers();
    break;

shift+ctrl+T调用toggleTex2D3D()但暂时不知道什么用,alt+T进入多类型的标注模式,可以画线后选择标记的类型(官方叫法是多神经元片段重新标注类型)。

case Qt::Key_D:
    if (IS_ALT_MODIFIER)
    {
   
   
        emit changeEditinput("Alt+D: Deleting");
        if(!isoperating)
            isoperating=true;
        callStrokeDeleteMultiNeurons(); //For multiple segments deleting shortcut,
    }
    else
    {
   
   
        // delete connected segments that have been highlighted,
        Renderer_gl1* thisRenderer = static_cast<Renderer_gl1*>(this->getRenderer());
        if (thisRenderer->selectMode == Renderer::smShowSubtree)
        {
   
   
        /*一串代码*/
        }
    }
    break;

alt+D进入删除模式。

case Qt::Key_S:
    if (IS_ALT_MODIFIER)
    {
   
   
        emit changeEditinput("Alt+S");
        if(!isoperating)
            isoperating=true;
        callStrokeSplitMultiNeurons();//For multiple segments spliting shortcut, by ZZ,02212018
    }
    /*一串代码*/
    break;

alt+S激活多神经元片段分割函数。

case Qt::Key_C:
    if ( WITH_SHIFT_MODIFIER && //advanced
            WITH_CTRL_MODIFIER
        )
    {
   
   
        toggleTexCompression();
    }else if (IS_ALT_MODIFIER)
    {
   
   
        emit changeEditinput("Alt+C: Connecting");
        if(!isoperating)
            isoperating=true;
        callStrokeConnectMultiNeurons();//For multiple segments connection shortcut
    }
    else if (IS_SHIFT_MODIFIER)
    {
   
   
    /*一串代码*/
    }
    else
    {
   
   
        neuronColorMode = (neuronColorMode==0)?5:0; //0 default display mode, 5 confidence level mode
        updateColorMode(neuronColorMode);
    }
    break;

shift+ctrl+C调用toggleTexCompression(),alt+C激活多神经元片段连接函数。

case Qt::Key_V:
    if ( WITH_SHIFT_MODIFIER && //advanced
            WITH_CTRL_MODIFIER
        )
    {
   
   
        toggleTexStream();
    }
    else if (IS_ALT_MODIFIER)
    {
   
   
        emit changeEditinput("Alt+V");
        if(!isoperating)
            isoperating=true;
        changeVolShadingOption();
    }
    else if (IS_CTRL_MODIFIER)
    {
   
   
        emit changeEditinput("Ctl+V");
        if(!isoperating)
            isoperating=true;
        updateImageData();
    }
    else
    {
   
   
        callLoadNewStack(
### Vaa3D在神经科学领域的应用 Vaa3D 是一种强大的可视化工具,在神经科学研究领域具有广泛的应用价值。它主要用于处理和分析高分辨率显微图像数据,特别是在三维空间中的神经元结构重建方面表现出卓越性能[^1]。 #### 主要功能与应用场景 - **神经元追踪** Vaa3D 提供了一系列插件用于自动或半自动化地追踪神经元形态。这些插件能够识别并描绘复杂的树突和轴突分支结构,从而帮助研究者理解单个细胞乃至整个神经系统的工作机制。 - **多模态数据分析** 支持多种文件格式(如 `.tif`),可以将不同来源的数据整合到统一平台中进行比较分析。例如通过特定函数实现二维 TIFF 图像转换为三维模型的功能。 - **交互式探索** 借助 Qt 库开发图形界面使得用户能够在直观环境下操作复杂数据集[^2]。这种特性极大地提高了科研效率同时也降低了学习成本对于初学者而言更加友好易上手[^3]。 #### 技术细节说明 为了更好地利用该软件完成上述任务可能涉及到一些关键技术环节如下: ##### 安装配置环境 根据操作系统差异有不同的部署方式可以选择预编译版本快速启动或者自定义源码编译满足个性化需求: ```bash sudo apt-get update && sudo apt-get install qtbase5-dev libqt5opengl5-dev cmake g++ git -y git clone https://github.com/Vaa3D/vaa3d_tools.git vaa3d_src cd vaa3d_src/ mkdir build; cd build cmake .. make -j$(nproc) ./vaa3d ``` 以上脚本适用于基于 Debian 的 Linux 发行版比如 Ubuntu 来设置运行时所需的依赖项以及构建流程。 ##### 自定义算法扩展 如果标准内置模块无法完全覆盖特殊场景下的业务逻辑,则可以通过编写 Python 脚本或者其他支持的语言对接 API 接口来增强其功能性。下面展示了一个简单的阈值分割示例代码片段作为参考: ```python def ada_threshold(image_data, block_size=11, offset=2): """ Apply adaptive thresholding to the input image data. Parameters: image_data (numpy.ndarray): Input grayscale image. block_size (int): Size of a pixel neighborhood that is used to calculate a threshold value. offset (float): Constant subtracted from the mean or weighted mean. Returns: numpy.ndarray: Binary image after applying adaptive threshold. """ import cv2 binary_image = cv2.adaptiveThreshold( src=image_data, maxValue=255, adaptiveMethod=cv2.ADAPTIVE_THRESH_MEAN_C, thresholdType=cv2.THRESH_BINARY, blockSize=block_size, C=offset ) return binary_image ``` 此方法可用于初步提取感兴趣区域(ROI),为进一步精细建模奠定基础。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值