Skip to content

C#

C# 4

Usage & Notes

Assuming 1 Trigger = 1 line (height = 1px, width = W). If your setup uses "Multiple Lines (2D) per 1 Trigger," tile_images is not necessary.

The payload order (Range/Intensity) may change depending on your setup. The above code assumes #1 = Range, #2 = Intensity. First, check the log (GetImagePointer1) and replace SelectObj(..., 1/2) if necessary. (Since this is a multi-object image, use SelectObj instead of AccessChannel.)

Timeout: Adjust TimeoutMs (this will vary depending on the line rate and exposure).

PixelFormat/BitDepth: Mono12/16 is recommended for µm-level resolution. Save as 16-bit TIFF.

The standard procedure is to convert to mm and remove surface tilt at a later stage: Z_mm = scale * DN + offset, and then create a small step by fitting the reference surface to a plane.

```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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
using System;
using HalconDotNet;

class GrabRI_SoftwareTrigger_Async
{
    static void Main()
    {
        // ====== Adjust to your environment ======
        const string IFace    = "GigEVision2";            // Usually "GigEVision2"
        const string Device   = "RULER3000_01";           // DeviceUserID or "169.254.x.x"
        const int    NumLines = 100;                      // Number of lines to acquire (assumes 1 trigger = 1 line)
        const string SaveDir  = @"C:\temp\ruler_swtrg";   // Save destination
        const string PixelFmt = "Mono16";                 // 12/16bit recommended for micron-level precision (must match camera)
        const int    TimeoutMs = 2000;                    // Timeout for GrabDataAsync (ms)
        const bool   Verbose  = true;                     // Log for checking channel order

        HTuple acq = null;
        HObject[] rangeLines = new HObject[NumLines];
        HObject[] intenLines = new HObject[NumLines];
        HObject imagesMO = null; // Temporary: container for multi-object
        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) Parameters (software trigger & simultaneous chunk acquisition)
            TrySet(acq, "ChunkModeActive", "true");        // Range+Intensity as simultaneous payload
            TrySet(acq, "PixelFormat", PixelFmt);         // Must match camera settings (e.g., Mono16)
            TrySet(acq, "AcquisitionMode", "SingleFrame"); // Assumes 1 Trigger = 1 Frame (ignore if not available)
            TrySet(acq, "TriggerSelector", "FrameStart");   // Select FrameStart trigger (ignore if not available)
            TrySet(acq, "TriggerMode", "On");
            TrySet(acq, "TriggerSource", "Software");       // Use software trigger

            // 3) Start asynchronous acquisition (buffer rotation)
            HOperatorSet.GrabDataStart(acq, -1);

            // 4) Loop: Trigger -> Asynchronous acquisition (only acquire. Concatenate & tile later)
            for (int i = 0; i < NumLines; i++)
            {
                // 4-1) Issue a software trigger
                HOperatorSet.SetFramegrabberParam(acq, "TriggerSoftware", "execute");

                // 4-2) Receive (Range+Intensity simultaneously)
                HObject imagesTuple; // multi-object (a tuple of multiple objects)
                HTuple  metaTuple;   // Metadata like 'mark' (can be ignored if not used)

                HOperatorSet.GrabDataAsync(out imagesTuple, out metaTuple, acq, TimeoutMs);

                // 4-3) Extraction: Extract Range / Intensity from the multi-object
                //      ★IMPORTANT★ grab_data returns a "multi-object of images".
                //      -> The correct way to extract is using SelectObj, NOT AccessChannel.
                //      (The channel order is environment-dependent. Check with the initial log.)
                int nObj;
                HOperatorSet.CountObj(imagesTuple, out nObj);

                if (nObj < 2)
                    throw new Exception($"Expected at least 2 payloads (Range & Intensity), but got {nObj}");

                // Check: Log size and type only on the first run
                if (Verbose && i == 0)
                {
                    for (int k = 1; k <= nObj; k++)
                    {
                        HObject ch; HOperatorSet.SelectObj(imagesTuple, out ch, k);
                        HTuple type, w, h;
                        HOperatorSet.GetImagePointer1(ch, out _, out type, out w, out h);
                        Console.WriteLine($"[DBG] payload #{k}: {type.S} {w}x{h}");
                        ch.Dispose();
                    }
                }

                // Here we assume #1 is Range and #2 is Intensity (*swap if necessary)
                HOperatorSet.SelectObj(imagesTuple, out HObject rangeImg, 1);
                HOperatorSet.SelectObj(imagesTuple, out HObject intenImg, 2);

                // Assuming 1 Trigger = 1 line (height = 1px)
                rangeLines[i] = rangeImg;
                intenLines[i] = intenImg;

                imagesTuple.Dispose(); // Dispose of the main multi-object when no longer needed
            }

            // 5) Concatenate -> tile in one go (vertical stack)
            HOperatorSet.GenEmptyObj(out rangeConcat);
            HOperatorSet.GenEmptyObj(out intenConcat);

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

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

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

            // 6) Save as 16bit TIFF
            string pathRange = System.IO.Path.Combine(SaveDir, "range_stack_swtrg_async.tiff");
            string pathInten = System.IO.Path.Combine(SaveDir, "intensity_stack_swtrg_async.tiff");

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

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

    static void TrySet(HTuple acq, string name, string val)
    {
        try { HOperatorSet.SetFramegrabberParam(acq, name, val); }
        catch { /* Ignore if the I/F doesn't support it */ }
    }
}