You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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的调用库函数节点配置:
    1. 函数调用约定必须选择stdcall (WINAPI),和你导出函数的__stdcall修饰符匹配
    2. 传入的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 11:18:03