Skip to content

C#

C# 2

The N (starting at 1) and role (Range/Intensity order) in AccessChannel(imagesTuple, out img, N) depend on your camera settings (Stream Setup / GenICam node). First, check the size and type with imagesTuple.CountChannels() or GetImagePointer1 to see which is the Range.

This assumes a 1Grab = 1 line (height 1px) setting. If your configuration already includes multiple lines with 1Grab, you don't need tile_images and you can get a 2D "height map" as is.

```csharp

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
using System;
using HalconDotNet;

class GrabRangeIntensity_StackLines
{
    static void Main()
    {
        // --- ★ Points to adjust according to your environment ★ ---
        const string IFace    = "GigEVision2";            // e.g., "GigEVision2"
        const string Device   = "RULER3000_01";           // DeviceUserID or "169.254.x.x"
        const int    NumLines = 100;                      // Number of lines to acquire (assumes height is 1px per grab)
        const string SaveDir  = @"C:\temp\ruler_out";     // Destination folder for saving
        const string PixelFmt = "Mono16";                 // 12/16bit recommended for micrometer-level precision (must match camera settings)

        HTuple acq = null;
        HObject[] rangeLines = new HObject[NumLines];
        HObject[] intenLines = new HObject[NumLines];
        HObject rangeConcat = null, intenConcat = null;
        HObject rangeStack = null,  intenStack = null;

        try
        {
            System.IO.Directory.CreateDirectory(SaveDir);

            // 1) Open framegrabber
            HOperatorSet.OpenFramegrabber(
                IFace,
                0, 0, 0, 0, 0, 0,
                "default",
                -1, "default", -1, "false",
                "default",
                Device,
                0, -1,
                out acq);

            // 2) Recommended basic settings
            //    - Enable chunks (for simultaneous payload of Range+Intensity)
            //    - Pixel format (must match camera settings)
            try { HOperatorSet.SetFramegrabberParam(acq, "ChunkModeActive", "true"); } catch {}
            try { HOperatorSet.SetFramegrabberParam(acq, "PixelFormat", PixelFmt); } catch {}
            //    - Free run is OK for a start (see notes below if you need a software trigger)
            HOperatorSet.SetFramegrabberParam(acq, "TriggerMode", "Off");

            // 3) Acquisition loop (synchronous: grab_data)
            //    This part only performs the acquisition; concatenation and tiling are done in a later step.
            for (int i = 0; i < NumLines; i++)
            {
                // grab_data returns a "tuple of images" and "metadata"
                HObject imagesTuple;    // Contains (Range, Intensity, ...)
                HTuple  metaTuple;      // Contains metadata like 'mark' (can be discarded if not used)

                HOperatorSet.GrabData(out imagesTuple, out metaTuple, acq);

                // Extract Range / Intensity from the image tuple
                // * The order may vary depending on the camera/settings.
                //   Typically, imagesTuple[0]=Range and imagesTuple[1]=Intensity.
                HObject rangeImg, intenImg;
                HOperatorSet.AccessChannel(imagesTuple, out rangeImg, 1);  // 1st channel (=index 0)
                HOperatorSet.AccessChannel(imagesTuple, out intenImg, 2);  // 2nd channel (=index 1)

                // This assumes 1 Grab = 1 line (height = 1px)
                rangeLines[i] = rangeImg;
                intenLines[i] = intenImg;

                imagesTuple.Dispose(); // Release the tuple itself
                // metaTuple is HTuple (managed) so explicit release is not needed
            }

            // 4) Concatenate -> tile in one go (vertical stack: Rows=NumLines, Cols=1)
            HOperatorSet.GenEmptyObj(out rangeConcat);
            HOperatorSet.GenEmptyObj(out intenConcat);

            for (int i = 0; i < NumLines; i++)
            {
                HObject tmp;
                HOperatorSet.ConcatObj(rangeConcat, rangeLines[i], out tmp);
                rangeConcat.Dispose(); rangeConcat = tmp;
                rangeLines[i].Dispose();

                HOperatorSet.ConcatObj(intenConcat, intenLines[i], out tmp);
                intenConcat.Dispose(); intenConcat = tmp;
                intenLines[i].Dispose();
            }

            HOperatorSet.TileImages(rangeConcat, out rangeStack, NumLines, 1);
            HOperatorSet.TileImages(intenConcat, out intenStack, NumLines, 1);

            // 5) Save (16bit TIFF is recommended. PNG is also an option, but TIFF is safer for 16bit data)
            string pathRange = System.IO.Path.Combine(SaveDir, "range_stack.tiff");
            string pathInten = System.IO.Path.Combine(SaveDir, "intensity_stack.tiff");

            HOperatorSet.WriteImage(rangeStack, "tiff", 0, pathRange);
            HOperatorSet.WriteImage(intenStack, "tiff", 0, pathInten);

            Console.WriteLine($"saved:\n  {pathRange}\n  {pathInten}");
        }
        finally
        {
            // Cleanup
            if (rangeStack    != null) rangeStack.Dispose();
            if (intenStack    != null) intenStack.Dispose();
            if (rangeConcat   != null) rangeConcat.Dispose();
            if (intenConcat   != null) intenConcat.Dispose();
            if (acq           != null) HOperatorSet.CloseFramegrabber(acq);
        }
    }
}