LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

imaqImageToArray() HOW TO USE IT?

Hello,
 
I'm doing image processing with LW CVI 8.5 and NI VISION. I tried to use arrays to make the processing faster, yet I didn't manage to use imaqImageToArray() function. I've tried a lot of things, here's what my code looks like now:
 
/////////////////////////////////////////////////////////////////////////////////////////////////////////
int success;
 unsigned int frame;
 AVIInfo aviSrcInfo;
 Image* imageSrc;
 Rect rect;
 int cols = 0;
 int rows = 0;
 double** arraySrc;
 double** arrayTmp;
 double** arraySum;
 if ( imaqGetAVIInfo (aviSrc, &aviSrcInfo) == 0 ) return -1;
 numFrames = min(aviSrcInfo.numFrames,numFrames);
 cols = aviSrcInfo.width;
 rows = aviSrcInfo.height;
 imagesrc=imaqCreateImage (aviSrcInfo.imageType, borderSize);
 imaqReadAVIFrame (imageSrc, aviSrc, 1, data, &datasize);
 imaqDuplicate (imageDest, imageSrc);
 IVA_SetPixelValue (&pixelValue,
        aviSrcInfo.imageType,
        grayscaleConstant,
        redConstant,
        greenConstant,
        blueConstant);
 imaqFillImage (imageDest, pixelValue, NULL);
 Image2Rect (imageSrc, &rect);
 arraySum = (double**)imaqImageToArray (imageDest, rect, NULL, NULL);
 arrayTmp = (double**)imaqImageToArray (imageDest, rect, NULL, NULL);
 LinEv2D (arrayTmp, cols, rows, 1., 0., arraySum);
 for(frame=1;frame<numFrames;frame++){
  success = imaqReadAVIFrame(imageSrc, aviSrc, frame, NULL, &datasize);
    arraysrc=imaqImageToArray (imageSrc, rect, &cols, &rows); 
  Add2D (arrayTmp, arraySrc, rows, cols, arraySum);
  LinEv2D (arraySum, rows, cols, 1., 0., arrayTmp);  
 }
 LinEv2D (arrayTmp, rows, cols, 1./numFrames, 0., arrayTmp);
 imaqArrayToImage (imageDest, arrayTmp, cols, rows);
 imaqDispose (imageSrc);
 imaqDispose (arraySrc);
 imaqDispose (arraySum);
 imaqDispose (arrayTmp);
return success;
/////////////////////////////////////////////////////////////////////////////////////////////////////////
 
I tried (double*) arrays but I thought since it's RGB values it's a 3D array that is apparently coded in 1 less dimensions since in the example I found, 2D arrays are coded in (double*) with cols and rows mixed.
If anyone can give me any piece of advice, please be sure I'll be glad to hear it.
Thanks
 
 
Julien Sellos
 
 
0 Kudos
Message 1 of 7
(6,431 Views)
i am sorry to digress a bit, but...

if you need to make your processing faster DO NOT EVER USE imaqImageToArray !

as you may know, NI Vision (and about any other image processing library) stores image in memory in a single long row of pixel values. this is called an image buffer. next to the image buffer is a set of attributes which tells the real width and height of the image, as well as its pixel format, etc... in order to be efficient when processing an image, a row of pixel in the buffer is often longer than the real width of the image: this is called padding, and allows to make memory transfert or processing by blocks of multiple bytes. also, images may contain a border around to allow for some processing (convolutions...) to execute properly. so a buffer contains a lot more informations than just image pixels.

then, when you use imaqImageToArray, it will physically copy all the relevant pixel of the image into a new buffer, removing any superfluous byte, and this copy takes a lot of time. generally, after calling imaqImageToArray and performing some processing, you have to call imaqArrayToImage to put the pixels back into the image buffer, which takes a lot of time again.

if you really want to process your buffer directly (which is completely normal: everybody does just that), use the imaqGetImageInfo function. it returns a structure describing the layout of the pixels in memory:
- imageStart is the address in memory of the first pixel of your image (coord 0,0)
- xRes is the width of your image (same as what is returned by imaqGetImageSize)
- pixelsPerLine is the real number of pixels for each line into the buffer.
(it is still missing a field describing the number of bytes per pixels)

now let's try:
{
    Image *img;
    ImageInfo info;

0 Kudos
Message 2 of 7
(6,412 Views)
(sorry, screwed up the previous post and its edit... here it is complete)

i am sorry to digress a bit, but...

if you need to make your processing faster DO NOT EVER USE imaqImageToArray !

as you may know, NI Vision (and about any other image processing library) stores image in memory in a single long row of pixel values. this is called an image buffer. next to the image buffer is a set of attributes which tells the real width and height of the image, as well as its pixel format, etc... in order to be efficient when processing an image, a row of pixel in the buffer is often longer than the real width of the image: this is called padding, and allows to make memory transfert or processing by blocks of multiple bytes. also, images may contain a border around to allow for some processing (convolutions...) to execute properly. so a buffer contains a lot more informations than just image pixels.

then, when you use imaqImageToArray, it will physically copy all the relevant pixel of the image into a new buffer, removing any superfluous byte, and this copy takes a lot of time. generally, after calling imaqImageToArray and performing some processing, you have to call imaqArrayToImage to put the pixels back into the image buffer, which takes a lot of time again. this is clearly not efficient and should be avoided if performance is your concern. for real fast home-made processing, write directly into image buffer...

to process your buffer directly (which is completely normal: everybody does just that... well, not quite everybody...), use the imaqGetImageInfo function. it returns a structure describing the layout of the pixels in memory:
- imageStart is the address in memory of the first pixel of your image (coord 0,0)
- xRes is the width of your image (same as what is returned by imaqGetImageSize)
- pixelsPerLine is the real number of pixels for each line into the buffer.
(it is still missing a field describing the number of bytes per pixels)

let's try:

    Image *img; // let's say this variable contains a valid image grayscale image
    ImageInfo info;
   
    imaqGetImageInfo( img, &info );
   
now you can access any pixel into the buffer:

    unsigned char *pixel_address = (unsigned char *)info.imageStart + y * info.pixelsPerLine + x;
    unsigned char pixel_value = *pixel_address; // or combine this with the previous line to get direclty the pixel value

you can also go through your entire image:

    for ( int y = 0; y < info.yRes; y++ )
    {
        for ( int x = 0; x < info.xRes; x++ )
        {
            pixel_address = (unsigned char *)info.imageStart + y * info.pixelsPerLine + x;

            // proces your pixel straight into the buffer here...
        }
    }

more efficient:

    unsigned char *pixel = info.imageStart;
    for ( int y = 0; y < info.yRes; y++ )
    {
        for ( int x = 0; x < info.xRes; x++ )
        {
            // proces your pixel straight into the buffer here...

            pixel += 1;
        }
        pixel += info.pixelsPerLine - info.xRes; // jump over all padding and border
    }

it can be made as efficient as you want (beware: CVI is NOT an optimizing compiler, optimize it yourself)
of course, if your image is not grayscale 8bit per pixel, you have to adjust the data type used for the pointer arithmetic, or add a factor to the pixel address computation.

THIS will make your computing faster. and you will not have to understand the call to imaqImageToArray which is badly documented and has no working example.
(next post, i show you how to write your own image processing library... :p)


Message 3 of 7
(6,407 Views)

Thanx for the code example below, helpfull!

I now can read the pixel values, but how to write the pixel values?

Can you (or anyone) give me an code example how to set (proces) the pixel value in the code below?  e.g. for instance set all pixel values to 128?   

  

Image *img; // let's say this variable contains a valid image grayscale image
    ImageInfo info;
   
    imaqGetImageInfo( img, &info );
   
now you can access any pixel into the buffer:

    unsigned char *pixel_address = (unsigned char *)info.imageStart + y * info.pixelsPerLine + x;
    unsigned char pixel_value = *pixel_address; // or combine this with the previous line to get direclty the pixel value

you can also go through your entire image:

    for ( int y = 0; y < info.yRes; y++ )
    {
        for ( int x = 0; x < info.xRes; x++ )
        {
            pixel_address = (unsigned char *)info.imageStart + y * info.pixelsPerLine + x;

            // proces your pixel straight into the buffer here...

 

// can you give me an code example how to set (proces) the pixel value here?  for instance set all pixel values to 128?


        }
    }

0 Kudos
Message 4 of 7
(6,193 Views)

the method i described gives you direct access to the image buffer. this means that you can read pixel values but also write values.

 

taking from my previous example:

 

    for ( int y = 0; y < info.yRes; y++ )
    {
        for ( int x = 0; x < info.xRes; x++ )
        {
            pixel_address = (unsigned char *)info.imageStart + y * info.pixelsPerLine + x;

            *pixel_address = 128;
        }
    }

more efficient:

    unsigned char *pixel = info.imageStart;
    for ( int y = 0; y < info.yRes; y++ )
    {
        for ( int x = 0; x < info.xRes; x++ )
        {

            *pixel = 128;
            pixel += 1;
        }
        pixel += info.pixelsPerLine - info.xRes; // jump over all padding and border
    }

 

since the values are written directly to the image buffer, the image does not need any further processing before using it in any other ni vision function.

0 Kudos
Message 5 of 7
(6,189 Views)

Thanx. Now it works!

...I saw that I wrote the local var (pixel_value) in stead of directly writing into the buffer 🙂

(my C is a little rusty it's been a while)

 

If my image is not grayscale 8bit per pixel, how can I adjust the data type (used for the pointer arithmetic), or add a factor to the pixel address computation? code example?

Can you direct me how to write your own image sharpen function or some convolution filter (kernel)? or image processing library...(info links?)

For filtering here you need to access the correct neighbour pixels (up, down, left, right etc.) of the single long row of pixel values (image buffer) and process the current pixel? Code Example?

0 Kudos
Message 6 of 7
(6,165 Views)

there are two ways of adjusting the bit-depth of the image:

- you can change the data type. for a 16 bit grayscale image, declare pixel address as a pointer to an "unsigned short".

- stay with an unsigned char pointer and adjust the offset in the pixel arithmetic: for 16 bit grayscale image, you get:

pixel_address = (unsigned char *)info.imageStart + (y * info.pixelsPerLine + x)*sizeof( unsigned short );

or better:

unsigned int pixel_size = sizeof( unsigned short );

pixel_address = (unsigned char *)info.imageStart + (y * info.pixelsPerLine + x)*pixel_size;

don't forget to adjust any pixel address computation.

 

the two are perfectly valid, but i'd rather use the second solution: the first solution will force you to repeat your function over and over again until you covered all possible data types. with the second option, the pixel size becomes a parameter, and the functions works for any data type: not just 8bit or 16bit grayscale, but also for floating point pixels, RGBA color pixels or even structures (for example, a 2 band image: one 16 bits color index associated with one 8 bit transparency value)... (if you were using C++, the data type could become a template, and this would be even more powerful).

 

writing your own convolution is a little bit more tricky. generally, your image processing library will do better than you. of course you can still do it yourself, but this would be overkill. there are some rare cases where what you want to perform is not available in the library: that's why i ended up writing my own contour walking function to compute the oriented bounding box of binary particles... there are plenty of places on the internet to find informations about image processing algorithm (the first one is googling for "image processing algorithm"). you have to read lots and lots of pages to find the real useful information, which is how to perform the function efficiently.

 

one last thing: CVI IS NOT AN OPTIMIZING COMPILER. you have to be very careful with the code you write, or it can be slower than using the NI vision library. also, do never think you found the fastest way to perform any given image processing task: generally, writing it in simple C statements and compiling it with an optimizing compiler will give you far better results. (Visual C++ is one such compiler if you don't forget to turn on all optimizations). i hope one day CVI will make some optimizations available.

 

Message 7 of 7
(6,142 Views)