VTK知识学习(26)- 图像基本操作(一)
1、前言
图像处理离不开一些基本的图像数据操作,例如获取和修改图像的基本信息、访问和修改图像像素值、图像显示、图像类型转换等。熟练掌握这些基本操作有助于使用 VTK进行图像处理应用程序的快速开发。
2、图像信息的访问与修改
1)利用vtkIamgeData方法
vtkImageData中提供了多个函数用于访问或者获取图像的基本信息,这些函数通常以 Set或者 Get加上相应的信息名的形式进行命名,例如获取图像维数的方法定义为 GetDimensions()。
private void TestGetImageInfo()
{vtkBMPReader reader = vtkBMPReader.New();reader.SetFileName("F:\\code\\VTK\\TestActiViz\\bin\\Debug\\data\\lena.bmp");reader.Update();int[] dims = reader.GetOutput().GetDimensions();Console.WriteLine($"图像维数:{dims[0]} {dims[1]} {dims[2]}");double[] orgin = reader.GetOutput().GetOrigin();Console.WriteLine($"图像原点:{orgin[0]} {orgin[1]} {orgin[2]}");double[] spaceing = reader.GetOutput().GetSpacing();Console.WriteLine($"像素间隔:{spaceing[0]} {spaceing[1]} {spaceing[2]}");vtkImageActor actor = vtkImageActor.New();actor.SetInputData(reader.GetOutput());vtkRenderer renderer = vtkRenderer.New();renderer.AddActor(actor);renderer.ResetCamera();renderer.SetBackground(1, 1, 1);vtkRenderWindow renderWindow = renderWindowControl.RenderWindow;renderWindow.AddRenderer(renderer);renderWindow.Render();
}

示例主要获取了图像的三个信息,即图像维数、图像原点和像素间隔。
VTK 中无论是二维图像还是三维图像,都用 vtkImageData表示,因此程序中定义图像维数为 dims[3],然后利用 GetDimensions()函数获取图像的维数;图像的原点和像素间隔都是物理空间数值,其数值类型为 double。从显示结果中可以看到,图像维数为512x512x1,其中Z方向的维数为1,说明该图像为二维图像:而图像的原点为(0,0,0)点,像素间隔为(1,1,1)。
2)利用类vtkChangeImageInformation
vtkImageData 中提供了多个 Set 函数用于设置图像的基本信息。在对一个图像 Filter的输出图像信息进行修改后,如果 Filter 重新 Update,图像信息又会恢复回原来的值。而vtkChangeImageInformation 类可以作为管线中的一个 Filter 来修改图像信息。利用这个Filter 可以修改图像的原点、像素间隔以及范围,另外还可以实现图像平移缩放等操作。
private void TestChangeImageInfo()
{vtkBMPReader reader = vtkBMPReader.New();reader.SetFileName("F:\\code\\VTK\\TestActiViz\\bin\\Debug\\data\\lena.bmp");reader.Update();int[] dims = reader.GetOutput().GetDimensions();Console.WriteLine($"原图像维数:{dims[0]} {dims[1]} {dims[2]}");double[] orgin = reader.GetOutput().GetOrigin();Console.WriteLine($"原图像原点:{orgin[0]} {orgin[1]} {orgin[2]}");double[] spaceing = reader.GetOutput().GetSpacing();Console.WriteLine($"原像素间隔:{spaceing[0]} {spaceing[1]} {spaceing[2]}");vtkImageChangeInformation changer = vtkImageChangeInformation.New();changer.SetInputData(reader.GetOutput());changer.SetOutputOrigin(100, 100, 0);changer.SetOutputSpacing(5, 5, 1);changer.SetCenterImage(1);changer.Update();dims = changer.GetOutput().GetDimensions();Console.WriteLine($"修改后图像维数:{dims[0]} {dims[1]} {dims[2]}");orgin = changer.GetOutput().GetOrigin();Console.WriteLine($"修改后图像原点:{orgin[0]} {orgin[1]} {orgin[2]}");spaceing = changer.GetOutput().GetSpacing();Console.WriteLine($"修改后像素间隔:{spaceing[0]} {spaceing[1]} {spaceing[2]}");vtkImageActor actor = vtkImageActor.New();actor.SetInputData(changer.GetOutput());vtkRenderer renderer = vtkRenderer.New();renderer.AddActor(actor);renderer.ResetCamera();renderer.SetBackground(1, 1, 1);vtkRenderWindow renderWindow = renderWindowControl.RenderWindow;renderWindow.AddRenderer(renderer);renderWindow.Render();
}

示例先读入图像,由 vtklmageData提供的函数接口获取图像的维数、图像原点和像素间隔。然后定义 vtkImageChangeInformation 对象,并设置输出图像原点为(100,100,0),输出图像像素间隔为(5,5,1),然后调用 CenterImage()函数将图像的原点置于图像的中心。
操作后的结果使得图像的原点位于(-1277.5,-1275.5,0)。
SetOutputOrigin(100,100,0)并没有起作用。查看 CenterImage()函数的注释,可以发现该函数的作用是将(0,0,0)点置于图像的中心。当Centerlmage()函数执行时会重新调用 SetOutputOrigin(),所以开始的 SetOutputOrigin()函数设置的原点将会被覆盖。
(-1277.5,-1275.5,0)是如何计算出来的呢?
根据图像的维数和像素间隔计算得到新的图像的宽度和高度为(512-1)x5,初始图像的原点位于(0,0,0),现在将图像的中心平移至原点,平移量为(-(512-1)x5/2,(512-1)x5/2,0)=(-1277.5,-1275.5,0)。

3、图像像素值的访问与修改
图像像素值的访问与修改是最常用的一种操作。VTK 中提供了两种访问图像像素值的方法第一种方法是直接访问 vtkImageData 的数据数组,这种方法最直接。前面创建图像赋值时也是采用这种方法。vtkImageData中提供了GetScalarPointer()函数获取数据数组指针,该函数有三种形式:
virtual void *GetScalarPointer(int coordinates[3]);
virtual void *GetScalarPointer(int x, int y, int z);
virtual void *GetScalarPointer();
前两种形式根据给定的像素索引得到指定的像素值,注意返回的是第(x,y,z)个像素值的地址。而第三种形式是返回图像数据数组的头指针,然后根据头指针可以依次访问索引像素。
1)数据数组
一个遍历图像像素的示例:
private void TestVisitPixel()
{vtkBMPReader reader = vtkBMPReader.New();reader.SetFileName("F:\\code\\VTK\\TestActiViz\\bin\\Debug\\data\\lena.bmp");reader.Update();//获取图像的大小int[] dims = reader.GetOutput().GetDimensions();//将图像的100*100大小的区域设置为黑色for (int k = 0; k < dims[2]; k++){for (int j = 0; j < dims[1]; j++){for (int i = 0; i < dims[0]; i++){if (i < 100 && j < 100){//VTK彩色及矢量图像的像素存储格式示意: …R G B R G B…byte[] infos = new byte[] { 0, 0, 0 };var pixel = reader.GetOutput().GetScalarPointer(i, j, k);Marshal.Copy(infos, 0, pixel, 3);}}}}vtkImageActor actor = vtkImageActor.New();actor.SetInputData(reader.GetOutput());vtkRenderer renderer = vtkRenderer.New();renderer.AddActor(actor);renderer.ResetCamera();renderer.SetBackground(1, 1, 1);vtkRenderWindow renderWindow = renderWindowControl.RenderWindow;renderWindow.AddRenderer(renderer);renderWindow.Render();
}

实现了将图像的 100x100大小的区域设置为黑色。先定义一个 reader 读取一幅 BMP 图像,通过 vtkImageData的 GetDimensionsO)函数获取图像的大小。然后建立三重循环,通过 GetScalarPointer(i,j,k)函数获取访问图像像素值。
需要注意的是,GetScalarPointer0函数返回的是 void*类型,因此需要根据图像的实际类型进行强制转换。如上面代码中将像素值数组的头指针类型转换为unsignedchar*。如果对数据类型不确定,还可以将图像数据类型强制转换为特定的数据类型,再进行遍历。
当然这个使用是指在c++中,在我们c#中你也看到是使用Marshal对指针进行复制操作了。
VTK 彩色以及矢量图像采用的是类似图 所示的像素存储格式。

因此在修改 RGB 图像以及矢量图像像素时,需要根据像素的元组的组分数目来访问。例中,需要修改每个像素的 RGB 值时,先获得第(i,i,k)个像素的地址也就是R值的地址,然后将地址加1来访问后续G值以及B值。如果对于像素的元组组分不确定,可以通过函数GetNumberOfScalarComponents()来获取,代码如下所示:
int nbOfComp = reader->GetOutput()->GetNumberOfScalarComponents();
2)迭代器方法访问图像像素
用 vtkImagelterator 类实现迭代器方法访问图像像素。该类是一个模板类使用时,需要提供迭代的图像像素类型以及迭代的区域大小。
这个是c++的方式,c#没有找到对应的实现。
void TestVisitImagePixelIteratively(){vtkSmartPointer<vtkBMPReader> reader =vtkSmartPointer<vtkBMPReader>::New();reader->SetFileName("F:\\code\\VTK\\TestActiViz\\bin\\Debug\\data\\lena.bmp");reader->Update();int numComponents = reader->GetOutput()->GetNumberOfScalarComponents();int subRegion[6] = { 0,300, 0, 300, 0, 0 };vtkImageIterator<unsigned char> it(reader->GetOutput(), subRegion);while (!it.IsAtEnd()){unsigned char* inSI = it.BeginSpan();unsigned char* inSIEnd = it.EndSpan();while (inSI!=inSIEnd){*inSI = 255 - *inSI;++inSI;}it.NextSpan();}vtkSmartPointer<vtkImageViewer2> imageViewer =vtkSmartPointer<vtkImageViewer2>::New();imageViewer->SetInputConnection(reader->GetOutputPort());vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =vtkSmartPointer<vtkRenderWindowInteractor>::New();imageViewer->SetupInteractor(renderWindowInteractor);imageViewer->Render();imageViewer->GetRenderer()->ResetCamera();imageViewer->Render();imageViewer->GetRenderer()->SetBackground(1.0, 1.0, 1.0);imageViewer->SetSize(640, 480);imageViewer->GetRenderWindow()->SetWindowName("VisitImagePixelIterativelyExample");renderWindowInteractor->Start();}

相关文章:
VTK知识学习(26)- 图像基本操作(一)
1、前言 图像处理离不开一些基本的图像数据操作,例如获取和修改图像的基本信息、访问和修改图像像素值、图像显示、图像类型转换等。熟练掌握这些基本操作有助于使用 VTK进行图像处理应用程序的快速开发。 2、图像信息的访问与修改 1)利用vtkIamgeData…...
2024年9月AI头条新闻:创新与挑战并存
2024年9月AI头条新闻:创新与挑战并存 9月,人工智能领域继续高速发展,重大产品发布、伦理争议和技术突破交织在一起。让我们回顾一下本月最重要的AI新闻: OpenAI的o1:更强大的语言模型 OpenAI推出了o1,一个…...
[Xshell] Xshell的下载安装使用、连接linux、 上传文件到linux系统-详解(附下载链接)
前言 xshell 链接:https://pan.quark.cn/s/57062561e81a 提取码:TK4K 链接失效(可能被官方和谐)可评论或私信我重发 安装 下载后解压得到文件 安装路径不要有中文 打开文件 注意!360等软件会拦截创建注册表的行为&a…...
count(1)、count(_)与count(列名)的区别?
大家好,我是锋哥。今天分享关于【count(1)、count(_)与count(列名)的区别?】面试题。希望对大家有帮助; count(1)、count(_)与count(列名)的区别? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在 SQL 中,…...
代码随想录训练营第二十七天| 贪心理论基础 455.分发饼干 376. 摆动序列 53. 最大子序和
贪心没有套路,说白了就是常识性推导加上举反例 今天的内容比较简单 简单了解贪心是通过局部最优解反推全局最优解(有经验成分) 455.分发饼干 题目链接:455. 分发饼干 - 力扣(LeetCode) 讲解链接ÿ…...
List直接使用removeAll报错
List直接使用removeAll报错 需要先将list转换才能使用 原因是: removeAll 方法在 Java 中用于从当前列表中删除另一个列表中存在的所有元素。如果直接对 List 接口的一个实现使用 removeAll 方法抛出异常,可能的原因有: 不同的List实现&am…...
Debian环境安装Docker Engine
Debian环境安装Docker Engine 卸载旧版本使用APT工具安装Docker设置存储库安装Docker设置权限 docker compose命令卸载Docker 卸载旧版本 要卸载的非官方软件包是: docker.iodocker-composedocker-docpodman-docker 此外,Docker Engine 依赖 containe…...
Python常用内置函数总结
目录 1. abs() 2. complex() 3. divmod() 4. eval() 5. float() 6. hash() 7. input() 8. int() 9. len() 10. list() 11. oct() 12. open() 13. pow() 14. print() 15. range() 16. reversed() 17. round() 18. sorted()…...
深入了解蓝牙Profile类型与设备的对应关系
在现代技术中,蓝牙作为一种无线通信技术,广泛应用于各种设备之间的短距离通信。不同的设备在连接时使用不同的蓝牙Profile(配置文件),每种Profile都为特定的设备功能提供支持,例如音频流传输、语音通话、文件传输等。在本文中,我们将详细介绍蓝牙Profile的常见类型及其对…...
[bug]java导出csv用Microsoft Office Excel打开乱码解决
[bug]java导出csv用Microsoft Office Excel打开乱码 现象 首先这个csv文件用macbook自带的 "Numbers表格" 软件打开是不乱码的, 但是使用者是Windows系统,他的电脑没有"Numbers表格"工具, 他用Microsoft Office Excel打开之后出现乱码,如下图…...
2023年区块链职业技能大赛——区块链应用技术(一)模块一
模块一:区块链产品方案设计及系统运维: 任务1-1:区块链产品需求分析与方案设计 1.依据给定区块链食品溯源系统的业务架构图,对考题进行业务分析,可能多的去考虑一个业务系统所需要的模块,使用Visio或思维导图工具展现本系统的基本设计概念和…...
4 软件工程——总体设计
一、设计过程 1.两个主要阶段 系统设计阶段:确定系统的具体实现方案结构设计阶段:确定软件结构 2.九个步骤 设想供选择的方案选取合理的方案推荐最佳方案功能分解设计软件结构设计数据库制定测试计划书写文档审查和复审 二、设计原理 1.模块化 模块…...
Elasticsearch:确保业务规则与语义搜索无缝协作
作者:来自 Elastic Kathleen DeRusso 利用查询规则与语义搜索和重新排序相结合的强大功能。 更多阅读: Elasticsearch 8.10 中引入查询规则 - query rules Elasticsearch 查询规则现已正式发布 - query rules 你是否知道查询规则(query ru…...
【大语言模型】ACL2024论文-33 Johnny 如何说服大型语言模型越狱:通过人性化 LLMs 重新思考挑战 AI 安全性的说服技巧
【大语言模型】ACL2024论文-33 Johnny 如何说服大型语言模型越狱:通过人性化 LLMs 重新思考挑战 AI 安全性的说服技巧 目录 文章目录 【大语言模型】ACL2024论文-33 Johnny 如何说服大型语言模型越狱:通过人性化 LLMs 重新思考挑战 AI 安全性的说服技巧目…...
【LuaFramework】LuaFramework_UGUI_V2框架学习
GitHub - jarjin/LuaFramework_UGUI_V2: 基于tolua的热更新框架V2 旧版本是Unity 5.0,这个是新版本支持更高版本的 导入工程后先清除wrap 然后重新生成wrap,你会发现有个报空null,框架的问题总结下所有的框架wrap相关报错问题和修复方法&…...
使用 AOP 在 Spring Boot 中实现跟踪和日志记录
在现代应用程序中,尤其是使用微服务构建的应用程序,跟踪和日志记录在跟踪流经各种服务的请求方面起着至关重要的作用。跟踪可帮助开发人员诊断问题、监控性能并了解用户在多个系统中的旅程。 在此博客中,我们将介绍如何使用traceId从前端生成…...
如何永久解决Apache Struts文件上传漏洞
Apache Struts又双叒叕爆文件上传漏洞了。 自Apache Struts框架发布以来,就存在多个版本的漏洞,其中一些漏洞涉及到文件上传功能。这些漏洞可能允许攻击者通过构造特定的请求来绕过安全限制,从而上传恶意文件。虽然每次官方都发布补丁进行修…...
FPGA远程升级 -- FLASH控制
简介 前文讲到如何实现XILINX芯片程序跳转,但升级程序是事先通过VIVADO工具将两个程序合成一个BIN文件实现升级的,并不能在线更新升级。要实现远程升级的能力需要对FPGA的FLASH进行在线写入升级程序。 FLASH介绍 本次设计FLASH选用的是S25FL128芯片&…...
企业内训|高智能数据构建、Agent研发及AI测评技术内训-吉林省某汽车厂商
吉林省某汽车厂商为提升员工在AI大模型技术方面的知识和实践能力,举办本次为期8天的综合培训课程。本课程分为两大部分:面向全体团队成员的AI大模型技术结构与行业应用,以及针对技术团队的高智能数据构建与Agent研发。课程内容涵盖非结构化数…...
ARM异常处理 M33
1. ARMv8-M异常类型及其详细解释 ARMv8-M Exception分为两类:预定义系统异常(015)和外部中断(1616N)。 各种异常的状态可以通过Status bit查看,获取更信息的异常原因: CFSR是由UFSR、BFSR和MMFSR组成: 下面列举HFSR、MMFSR、…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...
从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...
恶补电源:1.电桥
一、元器件的选择 搜索并选择电桥,再multisim中选择FWB,就有各种型号的电桥: 电桥是用来干嘛的呢? 它是一个由四个二极管搭成的“桥梁”形状的电路,用来把交流电(AC)变成直流电(DC)。…...
TJCTF 2025
还以为是天津的。这个比较容易,虽然绕了点弯,可还是把CP AK了,不过我会的别人也会,还是没啥名次。记录一下吧。 Crypto bacon-bits with open(flag.txt) as f: flag f.read().strip() with open(text.txt) as t: text t.read…...
