Writing Pixel Values to an Array

I want to get the pixel greyscale values (0-255) from the whole ROI (around 4000*200 px) to an array.
I’ve tried to do this with the GetPixel function, but then program does not react anymore because of an overload.

With access to the VPAT it should work more quickly, according to the helpfile.
I’ve tried to do it like it is in the CSVPAT tutorial but that hasn’t braught me further yet.

The red underlined variables are commented “Pointers and buffers can only be opened in an uncertain context”?

Is this the quickest way to get the whole pixel values from an image (axCVimage)?
How can they be written in an array?

Hi mave,

According to your screenshot and the fact that you had a look into the CSVPAT tutorial, I assume you are working with C# and windows forms.
The compiler errors you get are, because you have to make your method an unsafe method in order to be able to use pointer arithmetic under .NET.
You also need to allow unsafe code in your solution settings.

Another way to access the pixel values is to use Linear Access (Search for GetLinearAccess() in the documentation).
If you need a code snipped, that explains how to access image data via linear access, just let me know, but it shouldnt be to hard to get something running with the documentation :wink:

Cheers
Chris

Hi @mave!

As @Chris pointed out, unsafe is one of the key issues here. The keyword unsafe allows coders to use pointers very much like they did in C or C++ in C#. unsafe blocks are scoped - either by means of curly brackets {} or through function scope. In your case I think function scope fits better: You’ll need to modify the function signature to

private unsafe void vpatbutton_Click(object sender, EventArgs e)
{
  ...
}

Note that you’ll need to enable unsafe code generation in your project (off by default…):
image
Without checking the highlighted box the use of the keyword unsafe will result in a compiler error.

Please note that there is a conflict between the lines 147 and 167 in your code: pVPAT has been declared as a Cvb.Image.VPAEntry, but it should be a pointer to a VPAT entry for the code further down to make sense. Please also have a look at line 174 - I think what you intended to write was something like

  pixelLabel.Text += (*(pImageLine + pVPAT[x].XEntry.ToInt64())).ToString() + " ";

I’d also want to warn about line 157 and 158. The plan here seems to be disabling the grab while the pixel value array is being copied and restart grab once that has been done (I guess that is what the variable grab is going to be used for). Situations where such an approach is necessary certainly exist, but nevertheless I would like to point out two things:

  • In :cvb: a lot of effort has been dedicated to making asynchronous acquisition available and easily accessible - Terminating image acquisition during processing will, however, eliminate the possibility of working on your image data while the next frame is being acquired and reduce the overall frame rate to 1/t where t is the time taken by image processing plus the time taken for acquiring an image.
    To make things worse, with some video interface drivers repetitively starting and stopping image acquisition incurs another performance hit: The GenICam.vin for example has a lot of things to kick off when starting image acquisition and several resources to free and threads to stop once acquisition stops. As a result, repetitively starting and stopping image acquisition will reduce the overall frame rate of your application significantly because the (non-neglegible) time taken for starting and stopping acquisition will have to be added to t.
  • In your scenario, terminating acquisition to preserve the integrity of the currently processed image/VPAT is most likely unnecessary: Assuming that you are calling your routine from within an ImageSnapped event handler you can rely on the Image (and VPAT) to remain unaltered until the event handler has been exited (Image and VPAT internally are updated by the Image Control calling G2Wait - which will not happen while the ImageSnapped event handler is being processed, unless the (nowadays hidden) Global Async Acquisition Enabled flag has been set).
    And if you’re worried about the processing times: If the processing cannot keep up with the speed of image acquisition, the allocated number of buffers (default: 3) will be filled and once they are full any newly arriving images will simply be discarded (a fact that can be verified by invoking G2GetGrabStatus with the G2INFO_NumBuffersLost).

As for your question about processing speed:

  1. You are absolutely right in that GetPixel is terribly slow and unsuitable to retrieving the grey values of more than just a handful of pixels.
  2. VPAT access is a lot faster than that and by and large the most versatile way of achieving what you want because it’ll work regardless of the actual VPAT configuration of your image(s).
  3. The linear access that @Chris mentioned is the fastest approach but is limited to images where you can rely on the pixel data to be linearly arranged in memory (which isn’t always the case - e.g. if you set the RotateImage flag in your driver’s *.ini file to any value other than 0 the pixel data will no longer be linearly arranged). For code snippets with linear access see for example this entry.
3 Likes

@Chris & @illusive Thank you very much for your detailed response. That helped me a lot! Now, it works and much faster than before. It just takes around 4 seconds to export the whole Image (Mono 8Bit) to a txt file.
How much faster could it be with the use of linear access? I think the requirements are fulfilled, anyway the image isn’t rotated.
To which points has the code to be changed for that?

Hi Mave,

Right, its Milliseconds :muscle:

Cheers
Chris

1 Like

Nice work @Chris :hugs: - however I’d prefer copy&pasted code over a screenshot here :wink:. And remember we’re in the C-Style API section here - can you translate the CVB.Net based code accordingly? :sunglasses:
(@mave: You’ll have to take the milliseconds with a pinch of salt because the output to strings/text file will also take some time for a reasonably big image)

@illusive: Homework done :sunglasses::

    using Cvb;
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Threading.Tasks;

    namespace ArrayTestThatActuallyWritesToAList
    {
      class Program
      {
        static void Main(string[] args)
        {
          Image.IMG image;
          Image.CreateGenericImage(1, 4000, 200, false, out image);
          List<int> values = new List<int>();
          Stopwatch executionWatch = new Stopwatch();
          executionWatch.Start();
          CopyPixelsWithValue(image, values);
          executionWatch.Stop();
          Image.ReleaseObj(image);
          Console.WriteLine(executionWatch.ElapsedMilliseconds.ToString());
          Console.Read();
        }

        static unsafe void CopyPixelsWithValue(Image.IMG source, List<int> values)
        {
          int width = Image.ImageWidth(source);
          int height = Image.ImageHeight(source);
          IntPtr sourceBase;
          int sourceYInc;
          int sourceXInc;

          Cvb.Utilities.GetLinearAccess(source, 0, out sourceBase, out sourceXInc, out sourceYInc);

          Parallel.For(0, height, y =>
          {
            byte* pSrcLine = (byte*)sourceBase + y * sourceYInc;

            for (int x = 0; x < width; x++)
            {
              var srcVal = *(pSrcLine + x * sourceXInc);
              values.Add(srcVal);
            }
          });
        }
      }
    }

Didn’t feel very satisfying however :no_mouth:

@mave This should be pretty much copy-pasteable right now :wink:

Cheers
Chris

1 Like

@Chris: There is a memory leak left: either use SharedImg or ReleaseObj to clean-up the created IMG.

2 Likes

Well done!

I feel obliged to point out that the sequence of the pixel values in the values array does not reflect the sequence of the pixels in the area of interest due to the commendable use of Parallel.For. If the matter of the sequence in the array is of no significance for the follow-up processing then this is not a problem at all. If the sequence should be preserved, simply replace the Parallel.Forwith a regular for loop, @mave.

2 Likes

@parsd Thank you for pointing that out, I feel quite bad about that mistake as it is such a simple thing to take care of.
@mave This is the point where @illusive’s hint to use copy pasteable code takes effect as I’m not able to correct this mistake in my screenshot.
However, I have edited the code example to avoid someone copying the faulty version.

1 Like

Perfect! Thank you @Chris :blush:
it takes 211 milliseconds to save it to a textfile. Thats considerably more than i needed.
Thats really a great support here. Keep up with this great work :wink:

3 Likes