如何使用memcpy将U8/U16原始数据复制到LabVIEW Vision图像缓冲区
16位灰度原始数据转LabVIEW Vision图像问题
我有一块帧采集卡可采集16位灰度图像数据,输出为unsigned short类型的一维数组,需要将该U16原始数据转换为LabVIEW Vision图像用于后续处理。
在正式开展采集工作前,我先基于NI Vision的C支持库做测试:修改NI官方示例代码,使用memcpy()复制Grayscale U8图像,运行正常。
初始测试时,我在LabVIEW中创建了Grayscale U8类型的图像缓冲区,并传入图像句柄的指针:[LabVIEW代码]
参数配置如下:[函数参数配置]
C函数代码:
#include "nivision.h" // 对应LabVIEW簇的图像数据结构 typedef struct { char* name; Image* address; }IMAQ_Image; int __stdcall copyImageToLV(IMAQ_Image *LVImage) { Image* myImage, *lvImage; PixelValue pixel; ImageInfo myImageInfo, lvImageInfo; int y, LVHeight, LVWidth; lvImage = LVImage->address; pixel.grayscale = 128; // 创建新图像缓冲区并填充灰度值 myImage = imaqCreateImage(IMAQ_IMAGE_U8, 3); imaqGetImageSize(lvImage, &LVWidth, &LVHeight); imaqSetImageSize(myImage, LVWidth, LVHeight); imaqFillImage(myImage,pixel, NULL); imaqGetImageInfo(myImage, &myImageInfo); imaqGetImageInfo(lvImage, &lvImageInfo); // 将填充好的图像复制到LabVIEW创建的缓冲区 for (y = 0; y < LVHeight; ++y) { memcpy((char *)lvImageInfo.imageStart + y * lvImageInfo.pixelsPerLine, (char *)myImageInfo.imageStart + y * myImageInfo.pixelsPerLine, LVWidth); } imaqDispose(myImage); return 0; }
运行结果:[U8图像运行结果]
之后我尝试修改代码适配Grayscale U16,修改内容如下:
pixel.grayscale = 32000; myImage = imaqCreateImage(IMAQ_IMAGE_U16, 3); . . . memcpy((unsigned short*)lvImageInfo.imageStart + y * lvImageInfo.pixelsPerLine, (unsigned short*)myImageInfo.imageStart + y * myImageInfo.pixelsPerLine, LVWidth);
运行后输出空白图像:[U16图像输出结果]
更新1
空白图像是因为未设置正确的显示映射选项,右键点击图像指示器→16-Bit Display Mapping→Most Significant Bits,设置后即可正常显示实际灰度值。
现在我尝试直接读取帧采集卡输出的.raw格式像素文件,将数据复制到LabVIEW图像缓冲区,代码如下:
#include <iostream> #include "nivision.h" #include<fstream> using namespace std; int readRawFile(unsigned short* pSrc, int Width, int Height) { ifstream file; file.open("test.raw", ios::in | ios::binary); if (file.is_open() == true) { file.read(reinterpret_cast<char*>(pSrc), Width * Height * 2); file.close(); return 1; } else return 0; } int main() { Image* myImage; PixelValue pixel; ImageInfo myImageInfo; int LVHeight, LVWidth, i,handle,y; LVHeight = 3052; LVWidth = 2500; pixel.grayscale = 65535; unsigned short* pSrc = new unsigned short(LVHeight * LVWidth); readRawFile(pSrc, LVWidth, LVHeight); myImage = imaqCreateImage(IMAQ_IMAGE_U16, 3); imaqSetImageSize(myImage, LVWidth, LVHeight); imaqFillImage(myImage, pixel, NULL); imaqGetImageInfo(myImage, &myImageInfo); // 显示填充后的图像 imaqGetWindowHandle(&handle); imaqDisplayImage(myImage, handle, 1); std::cin >>i; std::cout << "Height = " <<LVHeight << "\tWidth = " << LVWidth <<std::endl; std::cout << "PixelsPerLine = " << myImageInfo.pixelsPerLine<<std::endl; for (y = 0; y < LVHeight; ++y) { memcpy((unsigned short*)myImageInfo.imageStart + y * myImageInfo.pixelsPerLine, pSrc + LVWidth * y, LVWidth *sizeof(unsigned short)); } imaqDisplayImage(myImage, handle, 1); std::cin >>i; imaqDispose(myImage); delete pSrc; }
运行代码后抛出如下访问违规错误:
Exception thrown at 0x00007FFF9D821470 (vcruntime140.dll) in TestApplication.exe: 0xC0000005: Access violation reading location 0x0000019EA712F320.
错误在循环运行约180次后出现,从错误描述看是pSrc指针相关问题,我暂时没有解决思路。另外我观察到Vision DLL会根据图像宽度分配额外内存,比如图像宽度为2500像素时,pixelsPerLine返回2560像素,未使用的像素会被填充为0。
更新2
我已排查到访问违规错误是内存分配错误导致,将代码修改为数组分配后:
unsigned short* pSrc = new unsigned short[LVHeight * LVWidth]; readRawFile(pSrc, LVWidth, LVHeight);
C++本地测试已可正常显示图像,但我的最终需求是在LabVIEW VI中获取图像(参考第一个LabVIEW代码片段),现在我将逻辑封装为DLL导出函数,在LabVIEW中调用后返回的图像像素值全为0,导出函数代码如下:
int __stdcall copyImageToLV(IMAQ_Image *LVImage) { Image *lvImage; ImageInfo lvImageInfo; int y, LVHeight, LVWidth,handle; lvImage = LVImage->address; imaqGetImageSize(lvImage, &LVWidth, &LVHeight); char* pSrc = new char[LVHeight * LVWidth * 2]; readRawFile(pSrc, LVWidth, LVHeight); imaqGetImageInfo(lvImage, &lvImageInfo); for (y = 0; y < LVHeight; ++y) { memcpy((unsigned short*)lvImageInfo.imageStart + y * lvImageInfo.pixelsPerLine,pSrc + y*LVWidth*2, LVWidth*2); } delete[] pSrc; return 0; }
烦请各位帮忙排查解决该问题,谢谢!
解决方案
你遇到的LabVIEW调用DLL后U16图像全为0的问题,可按以下优先级排查修复:
- LabVIEW端图像缓冲区类型不匹配
首先确认LabVIEW中创建的图像对象类型为Grayscale U16,而非之前测试用的Grayscale U8。位宽不匹配会导致像素解析完全错误,直接返回全0值。 - DLL调用参数配置错误
检查LabVIEW的调用库函数节点配置:- 函数调用约定必须选择
stdcall (WINAPI),和你导出函数的__stdcall修饰符匹配 - 传入的
IMAQ_Image簇必须配置为指针传递,如果配置为值传递,DLL拿到的只是无效的图像地址副本,写入操作不会生效
- 函数调用约定必须选择
- 代码内类型不匹配隐患
你当前封装的DLL代码中,readRawFile函数形参为unsigned short*,但你传入的是char*类型的pSrc,虽然强制转换不会直接报错,但建议统一类型避免隐式内存访问错误,同时增加读取成功判断,避免文件不存在时写入空数据:// 修改pSrc的类型和分配逻辑 unsigned short* pSrc = new unsigned short[LVHeight * LVWidth]; int readRes = readRawFile(pSrc, LVWidth, LVHeight); if (!readRes) { delete[] pSrc; return -1; } - 字节序不匹配问题
帧采集卡输出的.raw文件可能为大端序,而NI Vision默认使用小端序存储U16像素,字节序不匹配会导致像素值解析错误,看起来像全0。写入前可先做字节交换:for (int i = 0; i < LVWidth * LVHeight; i++) { pSrc[i] = _byteswap_ushort(pSrc[i]); } - 显示配置二次确认
即使像素写入正确,LabVIEW U16图像的默认显示映射也可能导致图像看起来全黑,确认你已经右键点击图像指示器,将16-Bit Display Mapping设置为Full Dynamic Range或者Most Significant Bits。
内容的提问来源于stack exchange,提问作者Achuthaperumal RK
相关产品推荐
相关产品推荐

