alvin.chen
2019-11-29 edf127edc771f715c49f86e91e768633f0a0ed93
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
using System;
using HalconDotNet;
 
namespace HalconTools
{
    /// <summary>
    /// This model data class holds general information about a measure
    /// object. To define more specialized measure data objects the
    /// classes MeasurementEdge and MeasurementPair are derived from this
    /// class to be able to distinguish the measure
    /// types and results for simple edges and for edge pairs. The base
    /// class contains the methods to create measure handles and to create
    /// the display results of a measure action. The derived classes
    /// implement the specialized methods to perform a measure operation
    /// and generate the measure results.
    /// </summary>
    public class Measurement
    {
        /// <summary>HALCON measure object (handle).</summary>
        protected HMeasure    mHandle;
 
        /// <summary>
        /// Flag to distinguish between a linear and circular measure object.
        /// </summary>
        protected   int       mROIType;
 
        /// <summary>
        /// Model coordinates obtained from the ROI instance.
        /// This coordinates are the base to calculate the measuring field.
        /// </summary>
        protected   HTuple    mROICoord;
 
        /// <summary>Reference to the ROI.</summary>
        protected   ROI       mRoi;
 
        /// <summary>
        /// Auxiliary information about the
        /// measuring field calculated using the ROI coordinates.
        /// </summary>
        protected   HTuple    mMeasROI;
 
        /// <summary>Iconic object to display measure edge results.</summary>
        protected   HXLDCont  mEdgeXLD;
 
        /// <summary>Iconic object to display mMeasROI.</summary>
        protected   HRegion   mMeasureRegion;
 
        /// <summary>
        /// Reference to controller class to obtain information about
        /// the context of the measurement, e.g. image, parameter setups etc.
        /// </summary>
        protected MeasureAssistant mMeasAssist;
 
        /// <summary>
        /// Creates and initializes a measure object based on information the ROI object
        /// about the ROI.
        /// </summary>
        /// <param name="roi">ROI instance</param>
        /// <param name="mAssist">Reference to controller class</param>
        public Measurement(ROI roi, MeasureAssistant mAssist)
        {
            mRoi = roi;
            mMeasAssist = mAssist;
            mROICoord = mRoi.getModelData();
            mEdgeXLD = new HXLDCont();
            mMeasureRegion = new HRegion();
 
            if (mRoi is ROICircularArc)
                mROIType = ROI.ROI_TYPE_CIRCLEARC;
            else
                mROIType = ROI.ROI_TYPE_LINE;
        }
 
        /// <summary>
        /// Triggers an update of the measure object for all 
        /// changes concerning the shape of the ROI or the measurement
        /// parameters.
        /// </summary>
        public void UpdateROI()
        {
            mROICoord = mRoi.getModelData();
            UpdateMeasure();
        }
 
        /// <summary>
        /// Creates a measure object based on the model data
        /// defined by the ROI instance and the parameters
        /// describing the measure context.
        /// </summary>
        protected void UpdateMeasure()
        {
            double extent, sPhi, radius;
 
            if (mHandle != null)
                mHandle.Dispose();
 
            mMeasAssist.exceptionText = "";
 
            try
            {
                switch (mROIType)
                {
                    case ROI.ROI_TYPE_CIRCLEARC:
 
                        radius = mROICoord[2].D;
                        sPhi = mROICoord[3].D;
                        extent = mROICoord[4].D;
 
 
                        mMeasROI = GenSurCircle(mROICoord, mMeasAssist.mRoiWidth);
                        mHandle = new HMeasure(mROICoord[0].D, mROICoord[1].D, radius,
                                                sPhi, extent,
                                                mMeasAssist.mRoiWidth,
                                                mMeasAssist.mWidth, mMeasAssist.mHeight,
                                                mMeasAssist.mInterpolation);
 
                        break;
                    case ROI.ROI_TYPE_LINE:
 
                        mMeasROI = GenSurRect2(mROICoord, mMeasAssist.mRoiWidth);
                        mHandle = new HMeasure(mMeasROI[0].D, mMeasROI[1].D,
                                               mMeasROI[2].D, mMeasROI[3].D, mMeasROI[4].D,
                                               mMeasAssist.mWidth, mMeasAssist.mHeight,
                                               mMeasAssist.mInterpolation);
                        break;
                    //case ROI.ROI_TYPE_LINE:
 
                    //    mMeasROI = GenSurRect2(mROICoord,mMeasAssist.mRoiHeight, mMeasAssist.mRoiWidth);
                    //    mHandle = new HMeasure(mMeasROI[0].D, mMeasROI[1].D,
                    //                           mMeasROI[2].D, mMeasROI[3].D, mMeasROI[4].D,
                    //                           mMeasAssist.mWidth, mMeasAssist.mHeight,
                    //                           mMeasAssist.mInterpolation);
                        break;
                }
            }
            catch (HOperatorException e)
            {
                mEdgeXLD.Dispose();
                mMeasureRegion.Dispose();
                mMeasAssist.exceptionText = e.Message;
                ClearResultData();
                return;
            }
            UpdateResults();
            UpdateMeasureRegion();
        }
 
        /// <summary>
        /// Defines the measuring field of a linear ROI.
        /// </summary>
        /// <param name="line">
        /// Model data for a linear interactive ROI
        /// </param>
        /// <param name="width">Half width of (rectangular) measure ROI</param>
        /// <returns>Model data describing a linear measuring field</returns>
        private HTuple GenSurRect2(HTuple line, double width)
        {
            double row1 = line[0];
            double col1 = line[1];
            double row2 = line[2];
            double col2 = line[3];
 
            double phi     = HMisc.AngleLx(row1, col1, row2, col2);
            double length1 = (HMisc.DistancePp(row1, col1, row2, col2)) / 2.0;
            double length2 = width;
            double rowM    = (row1 + row2) / 2;
            double colM    = (col1 + col2) / 2;
 
            return new HTuple(new double[] { rowM, colM, phi, length1, length2 });
        }
 
        private HTuple GenSurRect2(HTuple line,double height, double width)
        {
            double row1 = line[0];
            double col1 = line[1];
            double row2 = line[2];
            double col2 = line[3];
 
            double phi = HMisc.AngleLx(row1, col1, row2, col2);
            double length1 = height / 2.0;
            double length2 = width;
            double rowM = (row1 + row2) / 2;
            double colM = (col1 + col2) / 2;
 
            return new HTuple(new double[] { rowM, colM, phi, length1, length2 });
        }
 
        /// <summary>
        /// Defines the measuring field of a circular ROI.
        /// </summary>
        /// <param name="circle">
        /// Model data for a circular ROI
        /// </param>
        /// <param name="width">Radius (half width) of the circular (ring-shaped) ROI</param>
        /// <returns>Model data describing circular measuring field</returns>
        private HTuple GenSurCircle(HTuple circle, double width)
        {
            double row1   = circle[0];
            double col1   = circle[1];
            double radius = circle[2];
            double startPhi  = circle[3];
            double extent    = circle[4];
 
            if (radius <= width)
                return new HTuple(new double[] { row1, col1, startPhi, extent, (radius + width), 0.0 });
            else
                return new HTuple(new double[] { row1, col1, startPhi, extent, (radius + width), (radius - width) });
        }
 
        /// <summary>Releases resources used for this measure object.</summary>
        public void ClearMeasurement()
        {
            if (mHandle != null)
            {
                mHandle.Dispose();
                mEdgeXLD.Dispose();
                mMeasureRegion.Dispose();
            }
        }
 
        /// <summary>
        /// Creates the iconic object for displaying a measured
        /// edge in a linear ROI.
        /// </summary>
        /// <returns>Contour depicting the measured edge</returns>
        public static HXLDCont DetermineEdgeLine(double row, double col, double phi, double width)
        {
            double row1, row2, col1, col2;
 
            row1 = row - width * Math.Sin(phi + 0.5 * Math.PI);
            col1 = col + width * Math.Cos(phi + 0.5 * Math.PI);
            row2 = row - width * Math.Sin(phi + 1.5 * Math.PI);
            col2 = col + width * Math.Cos(phi + 1.5 * Math.PI);
            return DetermineLine(row1, col1, row2, col2);
        }
 
        /// <summary>
        /// Creates the iconic object for displaying a measured
        /// edge from the coordinates of the end points.
        /// </summary>
        /// <returns>Contour depicting the measured edge</returns>
        public static HXLDCont DetermineLine(double row1, double col1, double row2, double col2)
        {
            HXLDCont edge = new HXLDCont();
            HTuple rows, cols;
 
            rows = new HTuple(new double[] { row1, row2 });
            cols = new HTuple(new double[] { col1, col2 });
 
            edge.GenContourPolygonXld(rows, cols);
            return edge;
        }
 
        /// <summary>
        /// Creates the iconic object for displaying a measured
        /// edge in a circular ROI.
        /// </summary>
        /// <returns>Edge contour depicting a measured edge</returns>
        public static HXLDCont DetermineEdgeCircularArc(double row, double col, double center_row, double center_col, double radius, double width)
        {
            double row1, row2, col1, col2;
 
            row1 = row + (center_row - row) / radius * width;
            col1 = col + (center_col - col) / radius * width;
            row2 = row - (center_row - row) / radius * width;
            col2 = col - (center_col - col) / radius * width;
 
            return DetermineLine(row1, col1, row2, col2);
        }
 
        /// <summary>
        /// Creates the iconic object for displaying
        /// the arc between edges of a pair.
        /// </summary>
        /// <returns>Edge contour depicting the arc between edges of a pair</returns>
        public static HXLDCont DeterminePairCircularArc(double row1, double col1, double row2, double col2, double cRow, double cCol, double radius, double width, bool positive)
        {
            HXLDCont arc = new HXLDCont();
            double startPhi, endPhi;
 
            startPhi = Math.Atan2(cRow - row1, col1 - cCol);
            endPhi = Math.Atan2(cRow - row2, col2 - cCol);
 
            arc.GenEllipseContourXld(cRow, cCol, 0.0, radius, radius, startPhi, endPhi, (positive ? "positive" : "negative"), 1.5);
            return arc;
        }
 
        /// <summary>
        /// Creates an iconic object depicting the 
        /// measuring field.
        /// </summary>
        public void UpdateMeasureRegion()
        {
 
            mMeasureRegion.Dispose();
            mMeasureRegion.GenEmptyObj();
 
            if (mROIType == ROI.ROI_TYPE_CIRCLEARC)
            {
                double sPhi, extent, innerRad, outerRad;
                HTuple innerR, outerR, innerC, outerC;
                HXLDCont outCont, innerCont, contour;
 
                outCont = new HXLDCont();
                innerCont = new HXLDCont();
 
                sPhi = mMeasROI[2].D;
                extent = mMeasROI[3].D;
                outerRad = mMeasROI[4].D;
                innerRad = mMeasROI[5].D;
 
 
                innerCont.GenCircleContourXld(mMeasROI[0].D, mMeasROI[1].D, innerRad, sPhi, (sPhi + extent), (extent > 0) ? "positive" : "negative", 1.0);
                outCont.GenCircleContourXld(mMeasROI[0].D, mMeasROI[1].D, outerRad, (sPhi + extent), sPhi, (extent > 0) ? "negative" : "positive", 1.0);
 
                innerCont.GetContourXld(out innerR, out innerC);
                outCont.GetContourXld(out outerR, out outerC);
                innerR = innerR.TupleConcat(outerR);
                innerC = innerC.TupleConcat(outerC);
 
                contour = new HXLDCont(innerR, innerC);
                contour = contour.CloseContoursXld();
 
                mMeasureRegion = contour.GenRegionContourXld("margin");
 
                contour.Dispose();
                innerCont.Dispose();
                outCont.Dispose();
            }
            else
            {
                mMeasureRegion.GenRectangle2(mMeasROI[0].D, mMeasROI[1].D,
                                             mMeasROI[2].D, mMeasROI[3].D,
                                             mMeasROI[4].D);
            }
        }
 
        /// <summary>
        /// If calibration data is available and valid, then rectify 
        /// measure result coordinates, otherwise leave them the same
        /// </summary>
        public void Rectify(HTuple row, HTuple col, out HTuple rowRect, out HTuple colRect)
        {
            double unitScale=0.0;
 
            if (mMeasAssist.mIsCalibValid)
            {
                switch (mMeasAssist.mUnit)
                {
                    case "µm":
                        unitScale = 0.000001;
                        break;
                    case "mm":
                        unitScale = 0.001;
                        break;
                    case "cm":
                        unitScale = 0.01;
                        break;
                    case "m":
                        unitScale = 1.0;
                        break;
                    default:
                        break;
                }
 
                HOperatorSet.ImagePointsToWorldPlane(mMeasAssist.mCamParameter,
                                                     mMeasAssist.mCamPose,
                                                     row, col, new HTuple(unitScale),
                                                     out colRect, out rowRect);
            }
            else
            {
                rowRect = row;
                colRect = col;
            }
 
        }
 
        /// <summary>
        /// If calibration data is available and valid, then transform the
        /// distance between measure result edges into world coordinates,
        /// else leave them the same.
        /// </summary>
        public HTuple Distance(HTuple row1, HTuple col1, HTuple row2, HTuple col2, int shift)
        {
            HTuple rows, cols, rowRect, colRect;
            HTuple distance = new HTuple();
            HXLDCont contour;
 
 
            if (shift == 0)
            {
                if (mROIType == ROI.ROI_TYPE_CIRCLEARC)
                {
                    double cRow, cCol, radius, extent, phi1, phi2, phi, res, length, tmp;
 
                    cRow = mROICoord[0].D;
                    cCol = mROICoord[1].D;
                    radius = mROICoord[2].D;
                    extent = mROICoord[4].D;
 
                    HOperatorSet.TupleGenConst(new HTuple(row1.Length), 0.0, out distance);
 
                    for (int i=0; i < distance.Length; i++)
                    {
                        phi1 = HMisc.AngleLx(cRow, cCol, row1[i].D, col1[i].D);
                        phi2 = HMisc.AngleLx(cRow, cCol, row2[i].D, col2[i].D);
 
                        if (extent < 0)
                        {
                            tmp = phi1;
                            phi1 = phi2;
                            phi2 = tmp;
                        }
 
                        phi = phi2 - phi1;
 
                        if (phi < 0)
                            phi += 2 * Math.PI;
 
                        res = 0.05 * 24.0 / (radius * phi);
 
                        contour = new HXLDCont();
                        contour.GenEllipseContourXld(cRow, cCol, 0, radius, radius, phi1, phi2, "positive", res);
                        contour.GetContourXld(out rows, out cols);
                        Rectify(rows, cols, out rowRect, out colRect);
                        contour.Dispose();
                        contour.GenContourPolygonXld(rowRect, colRect);
                        length = contour.LengthXld();
                        distance[i].D = length;
                        contour.Dispose();
                    }
 
                }
                else if (mROIType == ROI.ROI_TYPE_LINE)
                {
                    HTuple rRect1, cRect1, rRect2, cRect2;
                    Rectify(row1, col1, out rRect1, out cRect1);
                    Rectify(row2, col2, out rRect2, out cRect2);
                    distance = HMisc.DistancePp(rRect1, cRect1, rRect2, cRect2);
                }
                return distance;
 
            }
            else
            {
                HTuple rClip1, cClip1, rShift2, cShift2;
 
                rClip1 = row1.TupleSelectRange(new HTuple(0), new HTuple(row1.Length - shift - 1));
                cClip1 = col1.TupleSelectRange(new HTuple(0), new HTuple(col1.Length - shift - 1));
                rShift2 = row2.TupleSelectRange(new HTuple(shift), new HTuple(row2.Length - 1));
                cShift2 = col2.TupleSelectRange(new HTuple(shift), new HTuple(col2.Length - 1));
 
                return this.Distance(rClip1, cClip1, rShift2, cShift2, 0);
            }
        }
 
        /// <summary>
        /// Returns the iconic object describing the measuring field
        /// of the measure object.
        /// </summary>
        public HRegion getMeasureRegion()
        {
            return mMeasureRegion;
        }
 
        /// <summary>
        /// Returns the iconic object depicting the measured edges.
        /// </summary>
        public HXLDCont getMeasureResults()
        {
            return mEdgeXLD;
        }
 
        /// <summary>
        /// Returns the gray-value profile obtained by the measure projection
        /// operation.
        /// </summary>
        /// <returns>Gray-value profile</returns>
        public double[] getGrayValueProj()
        {
            HTuple grayVal;
 
            if (mHandle == null || (int)mHandle.Handle < 0)
                return null;
 
            grayVal = mHandle.MeasureProjection(mMeasAssist.mImage);
            return grayVal.ToDArr();
        }
 
        /********************************************************/
        /********************************************************/
        /// <summary>
        /// Virtual method to be implemented by the derived classes.
        /// </summary>
        public virtual void UpdateResults() { }
        /// <summary>
        /// Virtual method to be implemented by the derived classes.
        /// </summary>
        public virtual void UpdateXLD() { }
        /// <summary>
        /// Virtual method to be implemented by the derived classes.
        /// </summary>
        public virtual void ClearResultData() { }
        /// <summary>
        /// Virtual method to be implemented by the derived classes.
        /// </summary>
        public virtual MeasureResult getMeasureResultData()
        {
            return null;
        }
 
    }//end of class
}//end of namespace