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 */ }
}
}
|