1

I have a frame grabber that acquires 16bit grayscale image data as a 1d array of unsigned short. Now I need to convert this raw U16 data to a LabVIEW Vision Image for further processing.

Before proceeding with the actual acquisition, I thought of experimenting with the NI Vision Library Support for C++. I have slightly modified example code from NI to copy a Grayscale U8 image using memcpy(). When I run, It works!

For the initial test, I created a Grayscale U8 type Image Buffer in LabVIEW and passed the pointer to the Image Handle: LV Code

The Parameter Configuration Function Parameter Configuration

C++ Function:

#include "nivision.h"

//Image Data as LabVIEW Cluster
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;
  //Create a New Image buffer and fill it with grey.
  myImage = imaqCreateImage(IMAQ_IMAGE_U8, 3);
  imaqGetImageSize(lvImage, &LVWidth, &LVHeight);
  imaqSetImageSize(myImage, LVWidth, LVHeight);
  imaqFillImage(myImage,pixel, NULL);

  imaqGetImageInfo(myImage, &myImageInfo);
  imaqGetImageInfo(lvImage, &lvImageInfo);
  //LVImage->address = myImage;
  //Copy filled Image to Buffer created from 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;
}

The Output:

U8 Result

Then I tried to implement the same for Grayscale U16 by changing the following lines:

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);

I get the same Blank Image as output! U16 Image Output


UPDATE 1:

The Blank Image is due to not setting the appropriate Display mapping option. Right-click on the Image Indicator --> 16-Bit Display Mapping --> Most Significant Bits. After this, Actual gray values can be seen.

Now, I'm trying to actually acquire the images from Framegrabber. It writes the pixel data into a .raw file. I need to read the pixel data from the file and then copy that to LabVIEW's Image Buffer.

#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);

  //Display Filled Image.
  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;
} 

When I run this code, I get the following Access Violation Error:

Exception thrown at 0x00007FFF9D821470 (vcruntime140.dll) in
TestApplication.exe: 0xC0000005: Access violation reading location 
0x0000019EA712F320.

After about 180 Iterations, I'm getting this error! Of course, from the error description, the problem is with the pSrc pointer... But I have no idea how to resolve this!

I Observed that Vision DLL allocates some extra memory based on the given image width. If the image width is 2500 pixels, The pixelsPerLine returns 2560 pixels. When I tried printing, The unused pixels were filled with 0.

UPDATE 2:

Turns out the access violation error is due to invalid memory allocation.

unsigned short* pSrc = new unsigned short[LVHeight * LVWidth];
readRawFile(pSrc, LVWidth, LVHeight);

I just changed this and now I'm able to see the image in the image window. But my original requirement is to get the image in LabVIEW VI (Refer to the first VI snippet above). Now when I export the function as dll, and call it from LabVIEW, I get the blank image with pixel values of zeros. Here is my exported function,

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;
  }

Kindly help me to resolve this issue... Thanks!

1 Answers1

1

Please see this self-contained Q&A which describes how to use LabVIEW to create an IMAQ image from a file/buffer. I believe this more accurately meets the needs of users in a similar position to the question author and was created in response to the second update to the question.

The original answer is retained as it might be useful for those working with IMAQ images in C/C++.

Original Answer

There are a few issues that might be causing what you are seeing.

Firstly, memcpy requires the number of bytes to copy to be specified thus you need to account for the fact that a U16 pixel is two bytes instead of a single byte as with the U8 image. The sizeof operator could be used as follows;

memcpy((unsigned short*)lvImageInfo.imageStart + y * lvImageInfo.pixelsPerLine,
            (unsigned short*)myImageInfo.imageStart + y * myImageInfo.pixelsPerLine, LVWidth * sizeof(unsigned short));

Secondly, you need to make sure that your LabVIEW code is passing in a U16 type IMAQ image reference.

IMAQ Create Image with type U16

Finally, there is one more thing to watch out for - the IMAQ image indicator maximizes the displayed dynamic range for U16 greyscale images. So an image of the same pixel value appears to be black!

You can verify the pixel values by looking at the image information

IMAQ Image information showing pixel values of 32000 as expected

As a second test, if you use the IMAQ functions to set some arbitrary pixels to black (0) and white (65535) then you should see what you are expecting.

IMAQ image with first two pixels set to show greyscale pixels correctly

In Response to Question Update 1

You had one main issue:

unsigned short() does not dynamically create a buffer to read the file into, just a single unsigned short.

In addition:

Lots of pointer casting can make it difficult to keep track of what is happening. Given that file reading and memcpy is all char based, it makes more sense (to me at least) to keep pSrc as a char* and multiply the byte read position of each memcpy call by the number of bytes per pixel.

I have also added in the imaqSetWindowThreadPolicy(IMAQ_SEPARATE_THREAD) to stop your display windows from appearing to be unresponsive.

#include <iostream>
#include "nivision.h"
#include <fstream>
using namespace std;

int readRawFile(char *buffer, size_t length)
{
    ifstream file;
    file.open("test.raw", ios::in | ios::binary);
    if (file.is_open() == true)
    {
        file.read(buffer, length);
        file.close();
        return 1;
    }
    else
        return 0;
}

int main()
{
    // set the window thread policy
    // use IMAQ_SEPARATE_THREAD since we do not have a message handling loop in our main application/thread
    // this will stop the display windows from hanging or appearing non-responsive
    imaqSetWindowThreadPolicy(IMAQ_SEPARATE_THREAD);

    Image *myImage;
    PixelValue pixel;
    ImageInfo myImageInfo;
    int LVHeight, LVWidth, i, handle, y;
    LVHeight = 3052;
    LVWidth = 2500;
    pixel.grayscale = 65535;

    size_t bytesPerPixel = sizeof(uint16_t);
    size_t bufferLength = LVHeight * LVWidth * bytesPerPixel;

    // create a char buffer to read the file into
    char *pSrc;
    pSrc = new char[bufferLength];

    readRawFile(pSrc, bufferLength);

    myImage = imaqCreateImage(IMAQ_IMAGE_U16, 3);
    imaqSetImageSize(myImage, LVWidth, LVHeight);
    imaqFillImage(myImage, pixel, NULL);
    imaqGetImageInfo(myImage, &myImageInfo);

    //Display Filled Image.
    imaqGetWindowHandle(&handle);
    imaqDisplayImage(myImage, handle, 1);

    cin >> i;

    std::cout << "Height = " << LVHeight << "\tWidth = " << LVWidth
              << std::endl;
    std::cout << "PixelsPerLine = " << myImageInfo.pixelsPerLine << std::endl;

    for (y = 0; y < LVHeight; ++y)
    {
        memcpy(reinterpret_cast<uint16_t *>(myImageInfo.imageStart) + y * myImageInfo.pixelsPerLine, pSrc + LVWidth * y * bytesPerPixel, LVWidth * bytesPerPixel);
    }

    imaqDisplayImage(myImage, handle, 1);

    cin >> i;

    imaqDispose(myImage);
    delete pSrc;
}
John
  • 1,313
  • 9
  • 21
  • @Jhon, Thanks! I figured out about the `16-Bit Display Mapping` Option in the LabVIEW Image Indicator 2 days ago. I'll try the `memcpy` tip and let you know. Also, I have updated the question... kindly take a look...And yes, I'm passing `Grayscale U16` as my Image Buffer. – Achuthaperumal RK Sep 26 '21 at 17:45
  • @Jhon, I tried as you suggested. I'm getting an access violation from reading a location! – Achuthaperumal RK Sep 26 '21 at 18:50
  • 1
    I think the update of my answer to your question should help @Achuthaperumal – John Sep 28 '21 at 14:18
  • @Jhon, Thanks a lot! This works properly in visual studio. But my requirement is to get the image in LabVIEW VI (Refer the first VI Snippet in the question). When I export the same function as the dll and call it from LabVIEW, I get the blank image with pixel values of 0. Any Idea why it happens? – Achuthaperumal RK Sep 29 '21 at 04:20
  • @Jhon, I have updated the question. kindly take a look at the UPDATE 2 section and let me know your thoughts... – Achuthaperumal RK Sep 29 '21 at 04:48
  • @Jhon, I was looking at this thread: [link](https://stackoverflow.com/questions/67442043/passing-data-buffers-from-c-to-labview/67559513#67559513), You have suggested the use of LabVIEW memory manager functions such as `DSNewPtr()` will it help in my case? – Achuthaperumal RK Sep 29 '21 at 05:31
  • @AchuthaperumalRK - Repeated updating of your question makes it difficult for other users to understand the answers in the context of any problem they may have. I feel like we have gone as far as we should on this question. I would ask you to look at this Q&A I created for this topic https://stackoverflow.com/questions/69380393/create-a-labview-imaq-image-from-a-binary-buffer-file-with-and-without-ni-vision/. If that doesn't help then please open a new question instead of updating this one. – John Sep 29 '21 at 17:11