summaryrefslogtreecommitdiff
path: root/scanin
diff options
context:
space:
mode:
Diffstat (limited to 'scanin')
-rw-r--r--scanin/CMP_DT_003.cht336
-rw-r--r--scanin/CMP_Digital_Target-3.cht1210
-rw-r--r--scanin/CMP_Digital_Target-3.cie591
-rw-r--r--scanin/CMP_Digital_Target-3.ti2601
-rw-r--r--scanin/ColorChecker.cht59
-rw-r--r--scanin/ColorChecker.cie39
-rw-r--r--scanin/ColorCheckerDC.cht318
-rw-r--r--scanin/ColorCheckerPassport.cht122
-rw-r--r--scanin/ColorCheckerPassport.cie64
-rw-r--r--scanin/ColorCheckerSG.cht203
-rw-r--r--scanin/Hutchcolor.cht597
-rw-r--r--scanin/Jamfile33
-rw-r--r--scanin/LaserSoftDCPro.cht202
-rw-r--r--scanin/License.txt662
-rw-r--r--scanin/Makefile.am19
-rw-r--r--scanin/QPcard_201.cht76
-rw-r--r--scanin/QPcard_201.cie42
-rw-r--r--scanin/QPcard_202.cht80
-rw-r--r--scanin/QPcard_202.cie48
-rw-r--r--scanin/Readme.txt8
-rw-r--r--scanin/SpyderChecker.cht115
-rw-r--r--scanin/SpyderChecker.cie62
-rw-r--r--scanin/afiles28
-rw-r--r--scanin/i1_RGB_Scan_1.4.cht341
-rw-r--r--scanin/it8.cht338
-rw-r--r--scanin/scanin.c1436
-rw-r--r--scanin/scanrd.c4659
-rw-r--r--scanin/scanrd.h125
-rw-r--r--scanin/scanrd_.h321
29 files changed, 12735 insertions, 0 deletions
diff --git a/scanin/CMP_DT_003.cht b/scanin/CMP_DT_003.cht
new file mode 100644
index 0000000..37411af
--- /dev/null
+++ b/scanin/CMP_DT_003.cht
@@ -0,0 +1,336 @@
+
+BOXES 286
+ F _ _ 22 14 522 14 522 402 22 402
+ D ALL ALL _ _ 500 388 22 14 0 0
+ X A S 1 15 24.21052632 22.4 44.5 43.5 24.21052632 22.4
+
+BOX_SHRINK 3.0
+
+REF_ROTATION 0.0
+
+XLIST 20
+ 44.500000 1.0 1.0
+ 68.710526 1.0 1.0
+ 92.921053 1.0 1.0
+ 117.131579 1.0 1.0
+ 141.342105 1.0 1.0
+ 165.552632 1.0 1.0
+ 189.763158 1.0 1.0
+ 213.973684 1.0 1.0
+ 238.184211 1.0 1.0
+ 262.394737 1.0 1.0
+ 286.605263 1.0 1.0
+ 310.815790 1.0 1.0
+ 335.026316 1.0 1.0
+ 359.236842 1.0 1.0
+ 383.447368 1.0 1.0
+ 407.657895 1.0 1.0
+ 431.868421 1.0 1.0
+ 456.078947 1.0 1.0
+ 480.289474 1.0 1.0
+ 504.500000 1.0 1.0
+
+YLIST 16
+ 43.5000000 1.0 1.0
+ 65.9000000 1.0 1.0
+ 88.3000000 1.0 1.0
+ 110.7000000 1.0 1.0
+ 133.1000000 1.0 1.0
+ 155.5000000 1.0 1.0
+ 177.9000000 1.0 1.0
+ 200.3000000 1.0 1.0
+ 222.7000000 1.0 1.0
+ 245.1000000 1.0 1.0
+ 267.5000000 1.0 1.0
+ 289.9000000 1.0 1.0
+ 312.3000000 1.0 1.0
+ 334.7000000 1.0 1.0
+ 357.1000000 1.0 1.0
+ 379.5000000 1.0 1.0
+
+EXPECTED XYZ 285
+ A1 9.46 8.62 29.26
+ A2 75.93 84.68 17.08
+ A3 44.11 49.20 9.06
+ A4 74.02 82.54 15.47
+ A5 37.88 42.42 7.93
+ A6 70.26 78.24 13.90
+ A7 33.59 37.74 7.04
+ A8 64.25 71.82 12.27
+ A9 30.32 34.10 6.32
+ A10 57.13 63.83 11.22
+ A11 26.62 30.10 5.61
+ A12 51.96 58.23 10.31
+ A13 24.04 26.72 5.25
+ A14 47.16 52.59 9.77
+ A15 37.64 22.85 4.57
+ B1 8.18 6.87 23.20
+ B2 3.61 3.38 6.33
+ B3 20.88 23.43 4.87
+ B4 6.33 6.95 2.79
+ B5 16.96 19.17 4.20
+ B6 5.33 5.76 2.62
+ B7 13.34 14.75 3.69
+ B8 4.19 4.42 2.51
+ B9 11.50 12.44 3.41
+ B10 3.35 3.48 2.29
+ B11 9.89 10.67 3.24
+ B12 2.58 2.69 2.00
+ B13 8.23 9.01 3.11
+ B14 6.56 5.16 3.23
+ B15 24.38 14.64 3.70
+ C1 68.17 69.38 68.07
+ C2 36.78 42.46 59.62
+ C3 75.58 84.30 16.75
+ C4 75.91 84.25 23.05
+ C5 74.70 83.33 16.21
+ C6 75.40 83.27 27.19
+ C7 72.95 81.42 15.76
+ C8 76.66 83.97 33.57
+ C9 71.33 79.64 15.34
+ C10 76.21 82.14 39.39
+ C11 70.81 79.26 15.13
+ C12 78.61 84.06 47.87
+ C13 73.95 82.35 17.71
+ C14 60.78 52.94 41.53
+ C15 36.37 21.94 4.40
+ D1 7.77 6.43 21.76
+ D2 3.42 3.25 4.79
+ D3 10.85 14.71 3.95
+ D4 45.74 31.43 7.66
+ D5 64.97 58.40 40.57
+ D6 19.20 20.77 4.47
+ D7 75.67 77.88 68.77
+ D8 46.56 58.50 11.12
+ D9 63.18 64.06 65.19
+ D10 81.64 85.77 61.30
+ D11 65.37 58.65 46.40
+ D12 61.29 55.60 29.97
+ D13 39.33 30.42 13.58
+ D14 5.41 4.44 3.00
+ D15 21.48 13.05 3.51
+ E1 51.91 51.52 63.01
+ E2 33.50 39.48 59.55
+ E3 19.80 26.35 5.19
+ E4 40.01 32.11 15.48
+ E5 37.52 50.89 23.01
+ E6 12.96 19.59 4.87
+ E7 40.98 31.63 14.39
+ E8 82.08 84.89 69.04
+ E9 42.04 30.96 10.93
+ E10 22.65 16.11 3.94
+ E11 16.97 31.21 9.80
+ E12 17.85 13.86 4.54
+ E13 37.65 30.47 11.22
+ E14 59.56 52.44 35.78
+ E15 37.21 22.38 4.99
+ F1 7.29 5.92 19.97
+ F2 2.90 2.84 3.47
+ F3 23.09 24.88 26.94
+ F4 12.29 23.07 7.35
+ F5 3.78 3.96 3.02
+ F6 35.90 39.27 26.05
+ F7 31.18 36.48 44.00
+ F8 14.33 10.91 3.47
+ F9 51.06 46.54 12.01
+ F10 62.15 71.68 13.67
+ F11 45.60 30.43 40.49
+ F12 27.89 15.62 6.31
+ F13 39.47 27.31 5.08
+ F14 4.49 3.88 2.77
+ F15 19.73 12.17 3.49
+ G1 40.25 39.39 58.62
+ G2 19.66 22.70 47.78
+ G3 14.05 21.04 21.63
+ G4 25.20 38.40 38.99
+ G5 2.23 2.29 1.82
+ G6 32.34 44.38 25.97
+ G7 39.08 40.35 45.16
+ G8 21.91 25.22 7.66
+ G9 51.20 40.45 19.47
+ G10 53.69 54.84 12.82
+ G11 43.69 29.70 5.63
+ G12 14.51 14.92 41.81
+ G13 48.91 38.08 7.33
+ G14 57.75 51.93 23.98
+ G15 40.27 24.75 7.03
+ H1 6.40 5.14 17.21
+ H2 2.50 2.51 2.41
+ H3 29.45 31.10 6.27
+ H4 8.45 13.05 6.25
+ H5 86.92 89.85 72.02
+ H6 55.25 57.23 44.58
+ H7 61.04 63.45 50.22
+ H8 66.13 68.64 54.49
+ H9 72.30 74.84 59.86
+ H10 77.76 80.54 64.40
+ H11 51.45 33.36 33.30
+ H12 34.29 19.71 5.84
+ H13 58.12 53.49 9.49
+ H14 3.19 3.01 2.34
+ H15 17.14 10.82 3.44
+ I1 28.84 27.18 51.31
+ I2 26.22 30.11 55.57
+ I3 8.61 12.78 13.10
+ I4 33.19 47.31 44.96
+ I5 2.15 2.20 1.76
+ I6 49.64 51.25 39.42
+ I7 5.60 5.83 4.56
+ I8 7.15 7.43 5.74
+ I9 8.78 9.28 7.44
+ I10 10.72 11.21 8.69
+ I11 40.19 25.72 5.17
+ I12 24.06 30.11 17.13
+ I13 37.28 22.42 4.86
+ I14 55.52 50.69 14.17
+ I15 43.35 28.37 10.26
+ J1 5.89 4.70 15.90
+ J2 2.28 2.31 1.96
+ J3 27.11 24.41 4.88
+ J4 7.74 9.84 6.03
+ J5 89.34 92.52 73.92
+ J6 43.85 45.42 35.57
+ J7 4.30 4.50 3.43
+ J8 2.15 2.19 1.76
+ J9 2.09 2.14 1.72
+ J10 13.73 14.17 11.04
+ J11 48.54 31.76 20.77
+ J12 32.08 17.94 10.34
+ J13 40.10 26.64 5.08
+ J14 2.38 2.39 1.88
+ J15 14.87 9.67 3.40
+ K1 20.70 19.17 45.35
+ K2 50.77 57.51 66.01
+ K3 5.13 6.59 5.77
+ K4 39.68 52.97 51.16
+ K5 2.10 2.15 1.70
+ K6 38.94 40.21 31.90
+ K7 3.21 3.34 2.69
+ K8 2.41 2.47 1.98
+ K9 2.13 2.17 1.74
+ K10 15.60 16.15 12.73
+ K11 33.32 21.81 4.35
+ K12 27.52 31.82 32.36
+ K13 56.60 50.73 8.89
+ K14 45.02 29.35 9.98
+ K15 33.62 20.13 4.15
+ L1 5.46 4.40 14.88
+ L2 2.11 2.16 1.70
+ L3 25.77 20.56 4.52
+ L4 7.71 9.80 5.98
+ L5 84.08 86.79 69.98
+ L6 33.50 34.70 27.85
+ L7 29.61 30.66 23.86
+ L8 24.73 25.62 19.83
+ L9 21.29 22.01 17.44
+ L10 18.36 19.06 15.25
+ L11 46.52 30.74 11.47
+ L12 24.42 16.78 15.94
+ L13 52.38 41.58 7.87
+ L14 2.21 2.25 1.78
+ L15 13.22 8.72 3.42
+ M1 14.98 13.99 39.67
+ M2 11.85 13.25 28.34
+ M3 12.36 12.97 3.50
+ M4 61.64 70.63 61.46
+ M5 2.71 2.82 2.20
+ M6 19.68 32.16 26.16
+ M7 22.82 21.24 9.05
+ M8 18.52 24.40 39.35
+ M9 12.91 11.12 3.85
+ M10 30.16 32.59 40.27
+ M11 19.93 15.24 35.28
+ M12 23.31 32.50 35.69
+ M13 40.57 29.47 5.44
+ M14 48.37 32.78 13.33
+ M15 33.33 19.90 4.09
+ N1 4.94 4.06 12.92
+ N2 2.11 2.17 1.71
+ N3 24.26 17.39 4.08
+ N4 7.07 6.36 6.03
+ N5 4.99 5.20 4.01
+ N6 16.13 29.37 14.18
+ N7 26.00 22.77 21.27
+ N8 19.90 28.01 35.06
+ N9 13.59 11.19 8.53
+ N10 25.06 17.40 4.36
+ N11 45.92 30.60 7.35
+ N12 23.35 22.39 17.67
+ N13 32.06 32.06 6.36
+ N14 2.19 2.22 1.79
+ N15 11.33 7.77 3.42
+ O1 10.02 9.28 31.17
+ O2 10.76 11.90 24.53
+ O3 42.31 54.55 53.67
+ O4 19.88 26.90 37.20
+ O5 13.56 15.96 37.55
+ O6 4.92 5.13 7.76
+ O7 5.99 4.89 16.71
+ O8 86.01 88.32 70.07
+ O9 41.28 54.49 46.60
+ O10 54.51 66.41 49.82
+ O11 6.39 4.76 7.70
+ O12 7.47 7.24 24.02
+ O13 10.20 9.57 23.31
+ O14 53.00 38.17 18.52
+ O15 32.29 19.09 3.94
+ P1 4.39 3.79 10.44
+ P2 23.77 36.35 37.44
+ P3 9.32 10.17 16.69
+ P4 21.20 34.66 29.30
+ P5 13.42 11.68 33.90
+ P6 85.83 89.93 70.83
+ P7 10.92 10.20 33.11
+ P8 68.39 77.72 54.98
+ P9 74.31 69.95 50.96
+ P10 76.65 83.76 61.45
+ P11 22.36 24.83 36.36
+ P12 53.63 58.46 57.53
+ P13 5.08 4.20 5.62
+ P14 56.95 44.27 24.67
+ P15 9.35 6.75 3.46
+ Q1 8.80 8.07 28.22
+ Q2 23.52 36.04 37.29
+ Q3 20.50 35.70 11.97
+ Q4 30.51 46.22 20.80
+ Q5 20.45 35.69 11.99
+ Q6 36.10 51.62 25.45
+ Q7 20.44 35.71 12.27
+ Q8 41.63 56.54 30.57
+ Q9 21.38 36.76 13.48
+ Q10 51.17 64.25 39.66
+ Q11 23.24 38.80 15.22
+ Q12 62.32 72.88 49.66
+ Q13 26.49 42.23 17.36
+ Q14 60.98 51.36 33.35
+ Q15 29.32 17.44 3.82
+ R1 3.95 3.59 8.36
+ R2 20.13 30.86 31.56
+ R3 10.16 18.47 6.04
+ R4 4.78 6.56 3.40
+ R5 9.21 15.96 5.65
+ R6 4.38 5.64 3.17
+ R7 8.46 14.35 5.50
+ R8 3.97 4.84 3.01
+ R9 7.21 11.73 4.98
+ R10 3.21 3.65 2.53
+ R11 6.23 9.66 4.43
+ R12 2.54 2.69 2.07
+ R13 5.50 7.95 3.98
+ R14 65.19 58.07 40.38
+ R15 7.60 5.78 3.43
+ S1 8.92 8.17 28.21
+ S2 20.24 35.38 11.78
+ S3 17.56 32.25 10.31
+ S4 19.54 34.67 11.34
+ S5 17.29 31.99 10.02
+ S6 18.80 33.75 11.17
+ S7 16.23 30.73 9.38
+ S8 16.69 31.26 9.79
+ S9 14.30 27.57 7.99
+ S10 19.72 34.91 12.50
+ S11 12.91 24.49 7.31
+ S12 26.55 41.95 17.28
+ S13 11.56 21.36 6.62
+ S14 33.15 44.32 24.54
+ S15 26.38 15.91 3.70
diff --git a/scanin/CMP_Digital_Target-3.cht b/scanin/CMP_Digital_Target-3.cht
new file mode 100644
index 0000000..eb95ed8
--- /dev/null
+++ b/scanin/CMP_Digital_Target-3.cht
@@ -0,0 +1,1210 @@
+
+BOXES 571
+ F _ _ 100 200 3100 200 3100 2100 100 2100
+ D ALL ALL _ _ 3200 200 0 0 0 0
+ X A Z 1 19 100 100 100 200 100 100
+ X 2A 2D 1 19 100 100 2700 200 100 100
+
+BOX_SHRINK 12
+
+REF_ROTATION 0.0
+
+XLIST 31
+ 100 1 1
+ 200 1 1
+ 300 1 1
+ 400 1 1
+ 500 1 1
+ 600 1 1
+ 700 1 1
+ 800 1 1
+ 900 1 1
+ 1000 1 1
+ 1100 1 1
+ 1200 1 1
+ 1300 1 1
+ 1400 1 1
+ 1500 1 1
+ 1600 1 1
+ 1700 1 1
+ 1800 1 1
+ 1900 1 1
+ 2000 1 1
+ 2100 1 1
+ 2200 1 1
+ 2300 1 1
+ 2400 1 1
+ 2500 1 1
+ 2600 1 1
+ 2700 1 1
+ 2800 1 1
+ 2900 1 1
+ 3000 1 1
+ 3100 1 1
+
+YLIST 21
+ 200 1 1
+ 300 1 1
+ 400 1 1
+ 500 1 1
+ 600 1 1
+ 700 1 1
+ 800 1 1
+ 900 1 1
+ 1000 1 1
+ 1100 1 1
+ 1200 1 1
+ 1300 1 1
+ 1400 1 1
+ 1500 1 1
+ 1500 1 1
+ 1600 1 1
+ 1700 1 1
+ 1800 1 1
+ 1900 1 1
+ 2000 1 1
+ 2100 1 1
+
+EXPECTED XYZ 570
+A1 19.991 14.120 27.886
+B1 19.926 34.054 14.005
+C1 6.2319 5.6339 19.078
+D1 32.785 35.198 16.672
+E1 38.996 22.658 25.805
+F1 16.011 17.612 14.561
+G1 39.496 24.997 12.003
+H1 7.4905 6.2468 18.358
+I1 11.987 23.286 6.5928
+J1 16.591 12.444 27.331
+K1 63.038 64.179 58.403
+L1 4.3513 3.8393 2.7750
+M1 27.386 30.873 42.527
+N1 31.789 44.281 40.439
+O1 59.453 61.985 40.335
+P1 26.783 15.713 3.5833
+Q1 3.0238 3.6843 3.0876
+R1 15.462 28.885 12.776
+S1 23.694 16.421 29.352
+T1 65.900 73.381 11.377
+U1 8.3333 15.036 13.749
+V1 72.416 77.985 33.423
+W1 16.393 25.254 38.122
+X1 2.3144 2.3581 1.9303
+Y1 27.102 23.276 16.813
+Z1 2.6994 2.8850 2.2332
+2A1 10.757 20.588 4.8216
+2B1 40.765 45.919 58.857
+2C1 22.864 16.381 33.839
+2D1 28.289 21.404 20.483
+A2 10.412 10.567 30.978
+B2 24.139 14.085 3.5857
+C2 19.473 21.752 38.376
+D2 26.280 16.991 28.035
+E2 42.573 39.671 9.6556
+F2 17.113 18.705 43.690
+G2 35.850 22.548 3.8554
+H2 13.037 10.392 26.425
+I2 14.946 28.262 8.1825
+J2 3.1780 3.3914 2.3047
+K2 35.880 35.178 26.683
+L2 36.434 22.147 28.386
+M2 17.689 17.022 15.072
+N2 36.396 23.022 13.623
+O2 19.884 26.988 39.544
+P2 10.294 7.4271 3.4839
+Q2 24.415 37.008 36.288
+R2 36.078 21.682 26.898
+S2 12.232 12.247 33.412
+T2 12.486 23.920 5.4357
+U2 38.913 47.091 40.794
+V2 8.3779 6.8387 3.7119
+W2 2.3401 2.3804 1.9157
+X2 17.501 20.678 9.7225
+Y2 60.324 56.490 9.7029
+Z2 18.340 14.257 6.9452
+2A2 39.051 50.700 6.0515
+2B2 5.3134 8.7205 3.7883
+2C2 24.040 16.842 14.641
+2D2 10.963 22.720 8.1563
+A3 3.2215 2.8069 3.1344
+B3 35.703 49.122 24.276
+C3 9.6710 9.2674 28.141
+D3 39.368 42.329 16.693
+E3 9.8448 9.8037 29.501
+F3 50.040 60.997 38.371
+G3 9.8731 9.2375 28.216
+H3 31.926 43.409 5.2962
+I3 18.889 24.530 52.235
+J3 36.271 22.167 11.952
+K3 34.281 37.060 50.573
+L3 2.4511 2.5353 2.0398
+M3 34.120 37.136 30.510
+N3 28.309 16.461 3.7229
+O3 12.851 10.508 27.052
+P3 24.532 27.910 7.4170
+Q3 29.800 23.719 40.761
+R3 11.420 17.249 4.2060
+S3 30.098 17.923 3.6411
+T3 15.102 16.455 41.548
+U3 16.816 29.880 6.2892
+V3 19.054 17.886 40.635
+W3 21.160 13.146 3.6418
+X3 56.827 44.647 36.033
+Y3 7.3224 13.350 4.3645
+Z3 34.447 20.515 11.595
+2A3 10.795 11.448 32.464
+2B3 20.617 25.691 5.5795
+2C3 11.126 10.174 28.914
+2D3 68.724 66.102 56.410
+A4 19.671 15.554 33.919
+B4 17.501 19.753 3.8498
+C4 21.509 25.734 51.674
+D4 51.958 42.451 9.1213
+E4 14.112 17.806 42.697
+F4 61.487 65.884 27.427
+G4 38.420 23.239 29.457
+H4 9.8759 9.7311 29.126
+I4 75.793 79.718 46.695
+J4 10.595 15.480 24.653
+K4 32.187 21.331 32.814
+L4 13.777 17.753 16.663
+M4 27.339 16.027 3.5603
+N4 16.922 28.995 29.336
+O4 6.3066 4.7610 6.9921
+P4 22.390 29.210 15.592
+Q4 30.657 20.369 32.268
+R4 19.152 19.807 15.592
+S4 13.948 25.671 5.2122
+T4 14.991 27.898 19.745
+U4 11.486 7.6381 12.484
+V4 10.749 11.899 3.2945
+W4 77.699 80.782 67.995
+X4 24.430 21.610 7.1800
+Y4 3.2104 3.1999 4.2513
+Z4 14.450 27.763 8.5197
+2A4 21.679 16.235 32.574
+2B4 15.737 29.267 10.078
+2C4 3.9226 4.4418 7.5674
+2D4 24.022 34.375 15.022
+A5 16.920 10.945 3.6233
+B5 32.876 32.373 51.547
+C5 23.454 37.144 7.5202
+D5 24.762 25.266 23.702
+E5 29.192 16.958 3.5359
+F5 23.640 37.186 26.447
+G5 20.387 28.842 4.7028
+H5 3.3636 4.0809 2.6833
+I5 23.272 35.820 35.198
+J5 30.642 19.573 30.103
+K5 22.753 35.416 35.064
+L5 11.209 7.8786 3.5144
+M5 17.513 29.721 30.234
+N5 2.9935 2.9559 2.2837
+O5 44.139 49.035 35.423
+P5 14.200 13.959 3.6761
+Q5 59.021 56.831 48.262
+R5 6.3401 9.2262 3.4989
+S5 45.700 49.818 40.124
+T5 9.3142 14.437 4.0636
+U5 2.3535 2.3975 1.9290
+V5 27.766 28.925 20.091
+W5 3.0887 2.9300 2.3596
+X5 8.7239 16.803 5.0276
+Y5 70.161 75.882 63.247
+Z5 11.191 11.244 16.654
+2A5 62.394 68.771 47.096
+2B5 12.640 10.459 27.333
+2C5 11.160 21.761 5.5099
+2D5 77.413 80.592 53.936
+A6 37.703 22.453 27.499
+B6 14.943 18.301 44.058
+C6 44.282 30.956 7.9185
+D6 10.440 18.144 6.5076
+E6 32.892 20.486 29.274
+F6 17.819 31.263 19.184
+G6 3.7468 3.6058 6.3752
+H6 45.907 41.132 28.818
+I6 4.4472 4.1158 9.1204
+J6 20.388 31.549 44.358
+K6 2.3970 2.4515 1.9961
+L6 14.323 24.343 14.536
+M6 2.3623 2.4293 1.9541
+N6 41.190 36.767 50.714
+O6 62.357 69.847 6.3069
+P6 10.124 10.811 31.915
+Q6 67.127 65.762 49.352
+R6 39.128 23.959 27.822
+S6 10.413 15.222 6.7952
+T6 41.195 30.074 26.292
+U6 8.4099 9.5591 6.5834
+V6 43.942 26.976 32.439
+W6 33.397 44.263 15.513
+X6 56.629 45.672 50.137
+Y6 10.198 10.583 6.9283
+Z6 42.782 29.822 40.386
+2A6 61.567 65.013 61.274
+2B6 21.335 15.115 28.916
+2C6 3.2203 3.7212 2.6005
+2D6 35.799 34.854 29.422
+A7 19.123 33.078 10.667
+B7 12.915 10.831 28.225
+C7 77.518 80.587 57.622
+D7 11.348 10.154 28.703
+E7 12.442 24.254 5.6475
+F7 15.345 13.969 34.910
+G7 52.703 61.988 5.9513
+H7 9.9056 10.888 32.881
+I7 67.471 66.483 44.705
+J7 8.8426 9.6268 8.1730
+K7 61.925 56.855 24.968
+L7 9.6349 10.726 32.427
+M7 32.435 45.157 6.1480
+N7 30.427 18.146 3.6270
+O7 16.928 17.695 11.911
+P7 56.094 48.894 4.9467
+Q7 11.934 11.778 20.573
+R7 15.285 28.434 6.3346
+S7 3.5054 4.3615 2.7830
+T7 35.099 44.589 58.771
+U7 26.155 17.005 28.079
+V7 8.9086 14.924 14.071
+W7 30.611 33.536 8.6858
+X7 79.113 81.784 66.503
+Y7 10.240 10.764 31.229
+Z7 11.976 23.218 5.4228
+2A7 9.9976 9.7574 28.929
+2B7 25.572 39.297 14.262
+2C7 66.257 69.908 5.8960
+2D7 14.893 16.603 41.626
+A8 12.100 24.359 8.4071
+B8 42.436 39.900 4.7260
+C8 20.574 15.216 20.136
+D8 12.701 25.255 8.1803
+E8 18.324 13.039 26.409
+F8 16.724 30.035 13.212
+G8 3.3200 3.1831 3.0291
+H8 20.304 33.570 24.443
+I8 2.9169 2.9009 3.0168
+J8 48.038 46.099 39.891
+K8 5.2241 8.0427 3.5985
+L8 38.619 50.368 44.407
+M8 2.3428 2.3959 1.9273
+N8 3.1956 3.3116 2.6415
+O8 10.494 10.847 8.8081
+P8 22.233 23.005 18.548
+Q8 39.317 40.778 33.056
+R8 63.147 65.480 53.862
+S8 28.252 23.514 24.400
+T8 14.520 26.909 5.4756
+U8 18.057 24.054 51.817
+V8 2.3402 2.3911 1.9209
+W8 77.838 79.255 64.883
+X8 14.232 12.342 3.5677
+Y8 31.451 43.594 39.472
+Z8 3.8429 5.0881 3.0661
+2A8 27.844 41.491 21.140
+2B8 2.4921 2.5648 2.0613
+2C8 34.635 36.777 50.964
+2D8 2.3436 2.3905 1.9440
+A9 46.811 59.109 25.406
+B9 35.695 26.905 33.928
+C9 8.0578 15.313 4.8058
+D9 70.562 68.540 46.575
+E9 10.633 11.656 33.787
+F9 54.113 43.360 23.933
+G9 7.1200 11.358 10.328
+H9 31.237 20.786 32.756
+I9 14.469 27.174 19.553
+J9 11.813 8.1727 3.4750
+K9 51.613 51.265 59.093
+L9 11.232 21.889 5.3482
+M9 2.3389 2.3932 1.9205
+N9 4.6543 4.8363 3.9004
+O9 11.973 12.444 10.082
+P9 24.548 25.410 20.720
+Q9 43.270 44.842 36.215
+R9 68.113 71.034 59.864
+S9 31.468 18.903 5.4734
+T9 3.2297 3.6435 2.9170
+U9 31.779 25.042 15.321
+V9 30.098 43.673 10.856
+W9 65.360 57.273 55.175
+X9 7.8261 15.682 5.0934
+Y9 30.151 17.603 3.5424
+Z9 14.169 10.872 25.545
+2A9 49.740 51.961 35.295
+2B9 19.293 14.167 28.533
+2C9 23.305 29.469 10.465
+2D9 22.420 16.254 30.792
+A10 7.4766 14.316 6.7672
+B10 30.693 34.185 4.8132
+C10 11.568 23.235 13.773
+D10 2.4307 2.4999 2.0171
+E10 30.140 19.949 31.751
+F10 15.810 25.353 37.521
+G10 2.4272 2.4936 1.9887
+H10 12.549 25.096 11.562
+I10 2.3402 2.3788 1.9223
+J10 13.387 24.235 23.958
+K10 25.478 22.853 4.3681
+L10 5.0366 4.2550 2.8902
+M10 2.3374 2.3915 1.9194
+N10 5.8942 6.1054 4.9143
+O10 13.462 14.013 11.428
+P10 27.055 28.014 23.007
+Q10 47.406 49.169 39.386
+R10 73.974 77.043 65.729
+S10 7.8358 8.0794 7.9176
+T10 54.395 42.563 48.165
+U10 11.113 22.841 7.7084
+V10 2.9394 3.4387 2.9247
+W10 13.284 25.973 11.867
+X10 2.4768 2.5120 2.1182
+Y10 27.102 26.481 19.829
+Z10 23.978 16.698 3.6586
+2A10 24.659 30.178 27.108
+2B10 13.618 8.1445 8.4106
+2C10 24.412 26.496 22.533
+2D10 5.2897 4.2252 10.803
+A11 15.662 9.9371 16.757
+B11 6.3528 11.756 4.1276
+C11 70.247 65.054 58.834
+D11 42.904 30.283 4.3741
+E11 33.675 37.292 27.259
+F11 7.3260 5.3398 8.4321
+G11 56.748 56.026 41.441
+H11 8.1585 8.7147 3.0001
+I11 54.512 56.195 42.466
+J11 4.9736 7.2275 6.1574
+K11 50.413 50.352 29.675
+L11 11.783 8.3671 18.993
+M11 2.4004 2.4632 1.9865
+N11 7.0502 7.2979 5.9348
+O11 15.157 15.663 12.643
+P11 30.809 31.935 26.037
+Q11 51.029 52.827 42.179
+R11 78.892 82.077 69.152
+S11 9.0391 9.3614 7.5795
+T11 54.688 46.067 27.270
+U11 7.0672 5.5203 3.1542
+V11 19.018 30.251 40.808
+W11 2.6438 2.7813 2.1741
+X11 20.294 33.254 28.954
+Y11 4.7917 4.3367 10.461
+Z11 39.678 51.335 44.730
+2A11 11.397 22.075 5.4606
+2B11 43.396 54.386 47.692
+2C11 9.0264 8.0855 4.5632
+2D11 29.263 17.239 3.6542
+A12 42.435 55.238 11.960
+B12 10.244 9.5241 28.550
+C12 25.557 39.490 11.397
+D12 9.5983 9.1448 8.3283
+E12 71.285 77.444 27.463
+F12 32.592 33.399 30.058
+G12 5.1430 4.3541 13.182
+H12 18.517 32.087 22.562
+I12 6.9977 5.1440 3.8014
+J12 69.246 76.042 17.844
+K12 10.642 7.6407 17.593
+L12 21.927 35.477 19.681
+M12 2.4287 2.4921 1.9949
+N12 7.9937 8.3172 6.7360
+O12 17.120 17.761 14.285
+P12 33.242 34.486 28.221
+Q12 55.136 57.000 45.303
+R12 84.047 87.070 71.496
+S12 46.799 45.618 34.284
+T12 6.6921 6.0185 19.773
+U12 31.583 45.365 15.646
+V12 35.975 22.808 7.0470
+W12 15.632 16.004 15.333
+X12 32.188 19.569 3.7849
+Y12 9.9981 9.6287 28.171
+Z12 14.429 27.348 6.4901
+2A12 13.822 12.446 29.936
+2B12 68.261 74.431 52.067
+2C12 2.4685 2.5621 2.0287
+2D12 28.798 30.974 45.549
+A13 14.129 10.884 26.286
+B13 21.280 29.662 4.6515
+C13 32.064 17.966 13.604
+D13 9.9611 10.733 32.277
+E13 51.845 54.927 28.181
+F13 6.3441 8.5421 15.068
+G13 68.699 71.452 46.387
+H13 10.374 10.902 29.169
+I13 26.203 23.997 4.3858
+J13 25.753 36.554 54.373
+K13 5.9068 4.9157 15.287
+L13 35.348 27.270 4.1933
+M13 2.6154 2.7053 2.1626
+N13 9.3637 9.6843 7.8391
+O13 19.359 20.106 16.147
+P13 36.624 37.957 30.671
+Q13 59.859 61.861 49.702
+R13 89.957 93.025 75.604
+S13 17.613 12.665 26.369
+T13 22.969 25.114 7.7873
+U13 13.623 8.3610 7.6135
+V13 59.661 54.433 33.140
+W13 15.045 10.609 3.4373
+X13 26.676 39.190 37.659
+Y13 16.770 18.831 3.7976
+Z13 16.243 13.004 15.620
+2A13 62.079 69.145 59.254
+2B13 9.8300 11.508 34.315
+2C13 13.378 8.4024 4.1065
+2D13 11.706 16.228 24.708
+A14 27.260 17.709 28.446
+B14 10.007 11.116 33.309
+C14 55.170 63.826 56.969
+D14 22.474 12.791 7.8220
+E14 17.833 19.340 7.6068
+F14 25.508 15.387 3.5019
+G14 28.674 42.244 25.294
+H14 20.013 14.167 27.201
+I14 2.4079 2.4331 1.9867
+J14 23.527 21.066 41.374
+K14 23.203 12.509 8.0121
+L14 48.486 42.511 43.112
+M14 8.6031 15.926 4.6200
+N14 53.107 53.884 48.977
+O14 3.9836 3.7404 6.5852
+P14 35.517 35.068 43.001
+Q14 37.782 22.314 19.042
+R14 17.467 17.105 13.104
+S14 22.370 35.508 5.9931
+T14 23.886 13.985 16.868
+U14 19.591 19.252 12.172
+V14 63.345 63.812 5.6645
+W14 12.028 10.481 3.3856
+X14 42.635 43.420 39.912
+Y14 19.856 32.192 5.1431
+Z14 15.570 17.641 12.711
+2A14 29.733 19.495 31.105
+2B14 81.209 84.285 67.959
+2C14 16.972 13.897 32.504
+2D14 20.262 27.906 7.7177
+A15 24.922 14.319 17.652
+B15 20.394 32.266 39.177
+C15 28.847 42.042 6.3345
+D15 27.579 26.553 23.681
+E15 38.874 23.290 19.357
+F15 36.703 46.352 27.027
+G15 34.061 22.803 34.531
+H15 10.113 11.921 31.879
+I15 64.423 58.008 39.281
+J15 9.4542 9.1717 6.7120
+K15 48.073 52.062 16.154
+L15 22.225 20.657 33.169
+M15 32.410 43.840 8.6168
+N15 19.932 29.141 53.896
+O15 8.2354 6.1612 3.2448
+P15 21.186 33.689 34.860
+Q15 2.8839 2.9372 2.7235
+R15 16.647 10.677 18.389
+S15 35.874 22.454 3.7685
+T15 18.024 31.495 21.231
+U15 26.027 17.635 30.172
+V15 5.7623 9.6302 3.8733
+W15 53.522 58.991 41.376
+X15 23.991 17.693 34.060
+Y15 3.5595 3.8688 2.3978
+Z15 34.055 35.265 28.241
+2A15 29.379 16.826 7.3609
+2B15 9.2107 8.5579 25.691
+2C15 25.707 18.129 3.6532
+2D15 13.875 19.568 19.948
+A16 32.441 19.582 3.7464
+B16 43.738 40.362 16.554
+C16 4.9159 8.3065 7.2559
+D16 37.197 27.832 7.4910
+E16 36.691 35.876 51.695
+F16 38.836 30.036 4.3338
+G16 17.555 13.363 28.888
+H16 75.466 77.836 67.216
+I16 16.954 15.112 7.2992
+J16 47.072 52.200 10.669
+K16 55.962 60.759 50.287
+L16 10.165 9.7715 28.705
+M16 36.859 23.500 3.8297
+N16 29.735 43.076 29.229
+O16 7.3545 6.2503 2.9745
+P16 46.114 46.965 33.568
+Q16 6.2218 6.8107 2.7776
+R16 29.663 17.214 6.8286
+S16 8.0381 10.138 16.410
+T16 31.088 18.734 3.6263
+U16 18.911 32.215 23.899
+V16 26.145 16.883 27.363
+W16 33.705 20.761 3.7986
+X16 14.361 27.487 7.1744
+Y16 29.882 22.982 39.138
+Z16 2.4233 2.4412 1.9648
+2A16 42.519 39.188 21.270
+2B16 5.2349 4.4461 13.823
+2C16 32.492 19.465 7.0806
+2D16 33.454 46.863 22.481
+A17 18.355 31.722 19.048
+B17 4.8626 4.2833 10.526
+C17 39.631 36.092 16.781
+D17 3.8471 5.8319 4.8962
+E17 43.491 44.349 22.904
+F17 35.028 21.444 3.8305
+G17 23.826 20.302 40.464
+H17 44.211 56.679 16.435
+I17 6.5115 6.2807 3.6004
+J17 35.464 48.530 31.500
+K17 31.217 18.833 4.2081
+L17 6.1796 5.0138 3.0160
+M17 21.355 33.511 36.981
+N17 31.382 17.339 13.539
+O17 20.515 20.076 24.934
+P17 32.143 19.447 3.7592
+Q17 60.501 50.532 52.708
+R17 12.657 24.420 5.6873
+S17 3.4355 3.1639 2.5372
+T17 28.192 29.771 51.322
+U17 52.660 42.651 15.019
+V17 69.823 71.393 63.731
+W17 44.995 50.413 5.2421
+X17 37.401 38.967 26.702
+Y17 8.0339 5.5483 9.9052
+Z17 10.382 21.829 11.599
+2A17 34.325 21.131 7.2263
+2B17 9.1863 9.9839 3.1512
+2C17 75.534 73.388 61.922
+2D17 19.258 14.916 29.290
+A18 74.089 78.770 59.424
+B18 42.903 30.204 40.715
+C18 8.7363 10.837 6.0872
+D18 75.509 74.636 56.066
+E18 10.413 9.8011 28.727
+F18 11.836 22.861 5.4226
+G18 26.954 18.177 29.358
+H18 16.220 29.699 13.154
+I18 2.3647 2.3980 1.9090
+J18 77.786 81.283 65.463
+K18 14.363 11.898 6.7644
+L18 60.378 57.128 5.2726
+M18 30.554 20.123 31.743
+N18 24.748 27.645 20.451
+O18 44.929 31.003 12.389
+P18 11.881 10.425 27.990
+Q18 25.737 39.690 14.823
+R18 52.300 42.856 4.6545
+S18 7.2126 14.429 7.1944
+T18 77.679 78.425 65.193
+U18 47.084 32.209 22.319
+V18 6.6683 11.848 10.189
+W18 65.207 70.970 60.729
+X18 6.2806 11.330 4.1095
+Y18 60.571 56.044 16.070
+Z18 15.673 16.045 40.153
+2A18 41.178 25.901 19.845
+2B18 13.693 16.674 24.407
+2C18 74.223 78.952 41.733
+2D18 38.135 24.817 4.1395
+A19 39.239 29.094 15.479
+B19 4.7416 7.0454 3.4644
+C19 49.395 43.657 28.522
+D19 42.332 52.372 5.7052
+E19 24.242 22.424 14.026
+F19 2.4551 2.5226 2.0256
+G19 16.677 25.667 25.825
+H19 13.124 8.8453 3.5763
+I19 28.527 37.076 34.358
+J19 22.891 16.490 6.9749
+K19 22.666 35.279 35.631
+L19 5.4212 4.5484 2.9627
+M19 11.199 21.925 5.4425
+N19 22.708 12.897 3.6335
+O19 12.507 23.738 22.498
+P19 2.3442 2.3896 1.9267
+Q19 30.303 17.749 3.5777
+R19 10.104 9.7374 28.448
+S19 13.901 26.685 6.3873
+T19 2.4391 2.4780 2.0610
+U19 45.972 49.473 57.247
+V19 4.8275 8.3485 3.9886
+W19 63.154 70.855 7.5811
+X19 14.863 14.166 8.5770
+Y19 74.340 78.986 66.286
+Z19 49.996 33.344 36.894
+2A19 10.165 11.287 33.081
+2B19 62.642 69.515 6.2139
+2C19 38.429 24.741 7.3479
+2D19 12.944 12.283 32.608
+
+A1 106.60 73.896 140.50
+B1 101.22 157.76 91.298
+C1 56.474 54.712 116.20
+D1 142.23 143.96 106.67
+E1 161.58 92.303 143.82
+F1 90.405 96.497 94.153
+G1 163.39 98.124 88.178
+H1 63.278 55.370 115.21
+I1 71.385 133.64 49.363
+J1 97.757 75.383 143.28
+K1 194.49 193.52 200.39
+L1 43.729 37.037 36.612
+M1 128.36 143.53 178.51
+N1 136.71 176.12 173.90
+O1 190.25 190.03 173.26
+P1 139.89 69.074 45.054
+Q1 34.433 40.188 37.667
+R1 87.362 153.04 89.833
+S1 124.79 84.048 151.84
+T1 201.42 206.76 79.283
+U1 59.875 104.70 93.370
+V1 205.39 208.60 159.63
+W1 91.572 136.12 167.03
+X1 30.336 29.306 28.957
+Y1 132.60 110.17 107.91
+Z1 32.956 33.215 31.185
+2A1 70.282 129.32 46.946
+2B1 159.00 173.65 202.91
+2C1 121.19 88.150 161.84
+2D1 136.59 101.50 125.83
+A2 75.414 80.525 150.43
+B2 128.64 63.027 42.872
+C2 101.62 116.40 166.26
+D2 129.62 78.772 146.55
+E2 164.20 144.75 82.589
+F2 96.904 110.83 179.21
+G2 157.24 84.071 46.359
+H2 84.439 70.424 140.26
+I2 81.661 147.44 58.864
+J2 35.433 35.856 32.072
+K2 150.66 144.08 140.58
+L2 158.35 93.379 154.74
+M2 100.38 93.492 99.897
+N2 158.93 93.968 95.921
+O2 102.60 137.21 171.09
+P2 74.932 50.703 44.902
+Q2 115.12 165.78 166.70
+R2 157.97 92.663 150.58
+S2 83.913 88.787 161.27
+T2 72.772 133.20 48.154
+U2 155.64 177.45 175.23
+V2 65.311 50.797 44.712
+W2 30.304 29.389 28.630
+X2 98.383 112.57 76.501
+Y2 196.95 176.56 81.174
+Z2 106.87 76.461 64.020
+2A2 149.29 180.57 55.756
+2B2 46.850 72.093 42.887
+2C2 126.21 86.097 104.74
+2D2 68.443 138.05 59.668
+A3 40.703 34.668 43.170
+B3 140.80 177.61 130.47
+C3 71.422 72.467 144.63
+D3 157.83 159.05 109.87
+E3 74.511 78.412 148.91
+F3 172.35 192.98 167.60
+G3 72.505 72.322 146.66
+H3 132.85 167.63 50.020
+I3 99.822 132.27 193.48
+J3 158.78 88.817 87.144
+K3 146.32 156.61 192.38
+L3 30.978 30.641 29.826
+M3 146.27 153.30 150.57
+N3 144.16 70.919 46.534
+O3 85.091 72.947 144.27
+P3 122.91 131.12 67.881
+Q3 140.27 116.77 178.48
+R3 73.020 103.31 46.082
+S3 148.58 74.828 46.145
+T3 92.676 106.61 179.17
+U3 89.067 149.36 56.522
+V3 104.27 103.98 177.68
+W3 122.27 64.970 44.965
+X3 190.80 157.08 167.65
+Y3 55.033 94.759 44.077
+Z3 157.08 85.576 87.558
+2A3 79.250 88.578 159.52
+2B3 108.15 127.71 55.172
+2C3 78.102 75.847 150.05
+2D3 202.83 194.20 199.16
+A4 105.09 86.729 157.14
+B4 99.696 102.26 43.923
+C4 107.26 130.81 190.72
+D4 178.30 141.51 75.719
+E4 86.201 111.97 176.39
+F4 192.41 193.92 143.50
+G4 161.20 95.317 155.94
+H4 74.863 77.945 149.59
+I4 207.61 208.23 184.16
+J4 70.931 101.90 131.71
+K4 147.01 94.545 162.80
+L4 81.586 103.83 104.84
+M4 141.06 69.848 45.247
+N4 90.494 151.07 150.65
+O4 55.294 41.016 63.313
+P4 111.84 140.47 103.22
+Q4 144.45 93.249 162.31
+R4 105.31 104.82 101.31
+S4 79.325 137.68 48.973
+T4 84.189 150.82 120.01
+U4 81.413 52.379 93.432
+V4 76.459 77.531 42.048
+W4 211.28 212.44 213.00
+X4 125.79 102.06 67.469
+Y4 36.736 36.340 46.306
+Z4 83.114 150.29 61.891
+2A4 117.71 90.574 160.37
+2B4 89.463 153.87 72.494
+2C4 42.498 47.832 65.570
+2D4 115.91 156.34 101.73
+A5 100.43 57.576 43.875
+B5 139.57 140.52 191.37
+C5 106.24 157.82 63.346
+D5 120.63 119.68 128.56
+E5 144.32 70.530 44.643
+F5 112.12 164.88 139.23
+G5 103.37 136.87 47.413
+H5 36.251 41.780 35.395
+I5 111.24 162.80 164.26
+J5 143.56 87.562 156.55
+K5 109.54 163.06 164.19
+L5 79.627 52.133 45.354
+M5 92.456 152.33 153.19
+N5 34.714 33.013 32.281
+O5 166.35 175.94 163.99
+P5 89.669 79.549 47.144
+Q5 191.24 182.85 188.48
+R5 51.675 70.899 41.770
+S5 170.53 177.54 173.76
+T5 63.890 93.942 45.687
+U5 30.168 29.299 28.940
+V5 133.08 133.14 122.05
+W5 36.054 32.923 33.276
+X5 61.314 111.77 45.755
+Y5 203.39 209.40 208.06
+Z5 76.353 77.670 109.77
+2A5 193.86 202.18 186.73
+2B5 84.630 73.965 145.61
+2C5 68.392 129.05 45.336
+2D5 210.46 210.49 195.89
+A6 156.79 90.614 148.03
+B6 88.481 111.82 178.46
+C6 167.90 108.82 66.983
+D6 66.578 110.85 54.360
+E6 148.31 87.735 154.80
+F6 93.611 155.13 115.73
+G6 40.190 39.280 58.763
+H6 170.89 152.59 147.19
+I6 46.469 44.545 75.326
+J6 102.48 151.18 180.03
+K6 30.872 30.164 29.516
+L6 83.294 137.00 98.315
+M6 30.722 30.150 29.446
+N6 163.74 151.67 194.83
+O6 199.83 203.65 49.970
+P6 77.512 86.873 159.07
+Q6 200.99 194.31 190.45
+R6 165.39 100.99 154.68
+S6 69.496 98.026 61.986
+T6 167.68 124.93 146.98
+U6 62.706 68.483 60.211
+V6 174.63 108.06 166.14
+W6 138.60 174.02 106.27
+X6 192.12 161.59 197.05
+Y6 71.207 70.503 63.173
+Z6 171.42 121.85 182.26
+2A6 193.77 197.98 207.14
+2B6 117.80 82.965 151.94
+2C6 36.305 39.517 34.462
+2D6 152.48 145.44 148.89
+A7 93.637 153.35 73.710
+B7 83.277 73.755 144.52
+C7 206.85 206.70 197.33
+D7 77.454 73.349 146.72
+E7 70.809 132.64 47.023
+F7 91.049 88.345 163.10
+G7 177.87 190.87 53.018
+H7 75.960 87.720 159.98
+I7 199.48 192.77 181.55
+J7 64.713 68.090 67.610
+K7 193.08 175.48 139.54
+L7 75.430 87.876 159.79
+M7 133.24 173.17 56.742
+N7 148.49 75.098 46.216
+O7 97.134 97.177 86.559
+P7 191.20 160.30 54.685
+Q7 80.068 80.251 125.32
+R7 83.368 147.19 55.358
+S7 37.747 44.719 36.907
+T7 146.52 175.26 205.50
+U7 133.97 82.941 152.98
+V7 63.182 103.22 97.125
+W7 144.22 147.07 76.384
+X7 212.78 213.19 211.89
+Y7 77.896 86.298 158.37
+Z7 71.313 133.52 47.389
+2A7 75.926 78.650 151.98
+2B7 118.08 168.98 98.781
+2C7 207.34 198.34 43.623
+2D7 92.639 108.26 179.66
+A8 69.904 137.36 58.325
+B8 167.09 144.55 49.599
+C8 111.10 78.672 121.83
+D8 74.340 141.34 56.829
+E8 104.13 74.055 141.65
+F8 90.075 154.61 90.911
+G8 36.341 33.896 36.868
+H8 101.65 159.66 134.07
+I8 34.216 33.499 37.712
+J8 174.41 165.56 173.38
+K8 46.252 66.300 42.397
+L8 152.69 184.12 182.69
+M8 30.240 29.863 28.939
+N8 35.593 35.467 34.190
+O8 71.728 71.553 70.937
+P8 115.92 116.10 114.68
+Q8 159.85 160.38 159.13
+R8 196.60 197.21 197.12
+S8 137.57 112.53 140.33
+T8 80.940 142.13 50.952
+U8 99.365 133.93 196.43
+V8 30.957 30.566 29.813
+W8 212.09 210.69 210.55
+X8 90.920 71.799 46.751
+Y8 137.72 177.30 175.40
+Z8 39.524 49.911 38.685
+2A8 130.39 176.13 127.05
+2B8 31.904 31.437 30.690
+2C8 149.77 158.18 196.00
+2D8 30.719 29.824 29.600
+A9 160.64 188.72 135.75
+B9 151.09 113.48 161.82
+C9 56.378 101.12 44.028
+D9 200.86 192.61 183.06
+E9 77.639 89.149 161.20
+F9 183.43 148.52 132.23
+G9 53.868 83.869 77.703
+H9 144.68 93.693 163.06
+I9 80.625 147.05 117.46
+J9 82.612 53.093 45.342
+K9 179.05 178.11 205.03
+L9 68.256 128.21 45.397
+M9 30.122 29.559 28.786
+N9 44.132 44.030 43.113
+O9 77.603 77.496 77.212
+P9 123.38 123.35 123.35
+Q9 167.61 168.12 167.11
+R9 202.61 204.17 205.53
+S9 152.34 81.269 59.657
+T9 36.525 39.233 37.120
+U9 147.79 112.59 105.57
+V9 128.85 175.42 85.449
+W9 202.12 182.11 203.04
+X9 58.841 111.22 46.014
+Y9 150.27 74.574 46.780
+Z9 91.373 72.814 142.15
+2A9 178.42 178.87 165.68
+2B9 110.21 82.066 150.83
+2C9 115.57 141.00 82.227
+2D9 120.44 87.308 156.62
+A10 54.278 98.104 59.100
+B10 141.49 142.92 50.130
+C10 69.545 135.98 93.417
+D10 30.772 30.009 29.471
+E10 140.52 89.479 159.03
+F10 88.149 135.62 165.23
+G10 30.508 30.075 29.389
+H10 75.794 144.13 86.030
+I10 29.881 29.286 28.915
+J10 78.427 139.08 135.09
+K10 127.69 103.24 52.122
+L10 47.518 38.484 37.887
+M10 30.202 29.466 28.978
+N10 50.671 50.438 49.901
+O10 84.087 84.218 84.130
+P10 130.92 131.27 131.19
+Q10 174.24 174.94 173.31
+R10 208.46 209.75 212.17
+S10 61.440 61.759 68.112
+T10 189.59 154.63 195.61
+U10 70.065 139.76 58.155
+V10 34.382 38.877 37.374
+W10 81.185 148.91 88.683
+X10 31.337 30.706 30.647
+Y10 132.10 124.54 122.36
+Z10 128.86 77.259 44.848
+2A10 121.07 144.71 145.24
+2B10 91.404 54.498 76.524
+2C10 122.45 129.19 129.52
+2D10 52.091 43.712 85.349
+A11 95.121 56.945 109.13
+B11 49.247 84.978 41.595
+C11 201.38 188.28 201.57
+D11 165.99 106.71 48.883
+E11 142.45 151.87 140.77
+F11 59.895 43.373 70.686
+G11 185.60 179.80 175.52
+H11 62.810 62.001 39.464
+I11 182.38 181.99 177.01
+J11 44.613 62.579 56.523
+K11 177.26 171.26 154.02
+L11 81.706 58.921 119.57
+M11 30.648 29.769 29.464
+N11 56.534 56.324 55.689
+O11 89.911 89.936 88.980
+P11 140.77 141.41 140.88
+Q11 179.84 180.21 178.58
+R11 212.60 213.84 215.53
+S11 66.493 66.184 65.558
+T11 187.32 160.61 146.99
+U11 59.842 44.929 41.517
+V11 100.71 151.70 177.40
+W11 32.433 32.983 31.555
+X11 103.41 161.56 150.03
+Y11 49.930 46.756 84.028
+Z11 157.24 187.57 185.20
+2A11 69.840 130.51 46.639
+2B11 164.73 190.77 189.92
+2C11 67.660 57.565 50.639
+2D11 146.88 73.255 46.894
+A12 149.18 183.06 84.464
+B12 72.446 71.794 146.17
+C12 111.58 163.80 82.344
+D12 67.115 62.023 67.669
+E12 202.25 206.59 144.16
+F12 142.52 142.52 149.46
+G12 52.218 46.843 96.005
+H12 95.301 157.62 128.66
+I12 58.804 42.199 45.333
+J12 201.67 207.01 112.57
+K12 76.878 56.433 113.86
+L12 110.43 164.87 121.89
+M12 31.073 30.502 29.828
+N12 60.863 60.999 60.221
+O12 97.436 97.414 96.425
+P12 146.96 147.76 147.68
+Q12 186.04 186.49 184.52
+R12 217.03 217.69 217.80
+S12 173.81 166.67 164.48
+T12 61.998 59.133 126.43
+U12 133.08 178.53 107.01
+V12 159.59 92.307 67.569
+W12 92.823 93.091 102.67
+X12 153.31 79.910 48.385
+Y12 76.169 77.765 152.48
+Z12 80.867 145.82 55.209
+2A12 88.726 84.091 155.13
+2B12 201.37 208.14 195.53
+2C12 32.004 31.705 30.813
+2D12 135.53 145.08 187.78
+A13 86.415 69.301 138.07
+B13 104.58 136.42 46.559
+C13 147.82 79.114 100.32
+D13 75.334 85.157 157.26
+E13 178.81 179.96 148.24
+F13 55.482 72.327 100.16
+G13 200.09 199.75 184.43
+H13 76.688 84.710 151.41
+I13 129.73 107.35 52.022
+J13 119.03 160.66 198.42
+K13 56.473 50.032 106.16
+L13 153.18 106.56 46.936
+M13 32.264 31.978 31.289
+N13 66.470 66.520 66.246
+O13 105.28 105.86 104.76
+P13 153.75 154.63 154.12
+Q13 192.03 192.25 191.27
+R13 221.68 221.89 221.96
+S13 104.20 75.856 145.33
+T13 121.87 124.18 72.370
+U13 91.392 55.886 72.021
+V13 193.01 176.53 161.70
+W13 97.047 61.496 44.638
+X13 124.18 171.27 172.48
+Y13 100.95 103.55 45.932
+Z13 98.719 76.722 108.03
+2A13 193.99 204.68 205.71
+2B13 77.619 94.368 167.50
+2C13 91.512 53.191 50.701
+2D13 76.609 104.98 135.45
+A14 129.84 79.131 147.61
+B14 74.731 86.884 158.53
+C14 178.45 194.65 198.02
+D14 122.80 65.789 71.805
+E14 99.529 101.15 66.691
+F14 133.73 68.160 43.803
+G14 130.31 174.28 137.87
+H14 110.49 77.413 145.63
+I14 30.999 29.764 29.587
+J14 120.14 114.13 178.99
+K14 128.29 64.544 74.117
+L14 176.70 157.34 183.40
+M14 59.777 105.00 45.141
+N14 182.00 181.11 190.30
+O14 42.651 40.749 62.042
+P14 151.32 150.31 182.91
+Q14 162.51 94.932 125.82
+R14 99.745 94.301 93.467
+S14 107.25 159.78 58.090
+T14 128.01 72.369 117.84
+U14 107.76 101.18 90.766
+V14 204.14 190.16 48.644
+W14 82.597 65.626 44.970
+X14 166.39 166.20 175.82
+Y14 100.69 152.17 51.384
+Z14 91.650 100.20 90.746
+2A14 143.62 91.243 162.04
+2B14 214.71 215.34 214.31
+2C14 101.32 87.875 161.74
+2D14 105.18 138.75 68.497
+A15 124.84 68.758 114.78
+B15 99.929 150.22 167.53
+C15 121.25 165.78 57.133
+D15 129.49 121.04 130.58
+E15 161.27 95.254 123.97
+F15 148.20 174.03 142.22
+G15 150.41 98.923 167.42
+H15 75.180 91.615 158.04
+I15 196.22 179.25 172.32
+J15 66.558 62.105 60.696
+K15 174.25 177.65 112.40
+L15 116.33 112.29 162.02
+M15 133.68 171.46 73.038
+N15 102.47 146.49 198.15
+O15 64.715 46.574 43.032
+P15 105.91 159.08 164.78
+Q15 34.379 33.754 36.003
+R15 102.62 61.271 119.89
+S15 159.62 86.224 48.021
+T15 95.611 158.57 127.45
+U15 133.03 87.140 157.87
+V15 48.894 77.368 43.484
+W15 182.54 191.20 178.40
+X15 126.00 95.308 166.74
+Y15 38.376 39.986 33.590
+Z15 148.30 148.94 147.37
+2A15 146.55 75.055 68.429
+2B15 71.706 70.645 144.63
+2C15 133.02 80.816 44.452
+2D15 82.777 113.54 117.52
+A16 147.43 75.104 45.417
+B16 162.22 145.05 108.87
+C16 43.273 69.681 61.261
+D16 154.15 106.72 65.606
+E16 149.19 148.83 193.98
+F16 157.80 111.38 47.573
+G16 100.61 79.449 149.13
+H16 206.92 207.61 211.44
+I16 97.895 82.272 66.348
+J16 172.80 178.46 82.595
+K16 183.90 191.17 190.75
+L16 75.048 76.021 151.02
+M16 159.42 88.282 47.938
+N16 133.24 176.09 150.50
+O16 59.749 48.251 40.198
+P16 171.06 168.69 161.58
+Q16 53.260 54.841 37.700
+R16 147.03 76.735 66.424
+S16 63.206 78.809 107.93
+T16 150.22 77.352 47.226
+U16 97.824 159.49 134.64
+V16 133.39 81.567 151.23
+W16 155.32 82.024 48.477
+X16 81.301 147.86 56.013
+Y16 141.53 112.75 178.08
+Z16 31.556 30.196 30.075
+2A16 166.04 149.78 129.15
+2B16 53.939 48.402 100.70
+2C16 152.96 83.435 68.500
+2D16 141.97 181.00 131.63
+A17 93.002 152.57 112.99
+B17 48.562 44.595 81.595
+C17 155.24 137.12 108.13
+D17 37.723 54.820 48.270
+E17 163.04 158.68 133.73
+F17 155.10 81.151 47.477
+G17 119.76 108.26 175.73
+H17 155.83 188.69 108.40
+I17 54.125 49.417 43.252
+J17 146.89 182.65 155.76
+K17 148.71 77.345 50.119
+L17 53.925 41.854 39.291
+M17 105.46 156.83 167.45
+N17 149.16 79.222 103.08
+O17 109.98 107.89 138.78
+P17 151.99 78.765 47.620
+Q17 195.13 169.06 200.08
+R17 73.599 136.55 49.271
+S17 37.757 33.718 34.938
+T17 132.61 142.77 197.68
+U17 181.82 145.93 103.48
+V17 204.10 203.91 210.35
+W17 175.82 179.36 52.229
+X17 155.69 156.50 143.88
+Y17 65.731 44.495 80.654
+Z17 67.930 138.21 88.916
+2A17 156.40 87.866 68.881
+2B17 68.870 68.990 41.254
+2C17 209.60 202.66 207.60
+2D17 108.97 87.062 152.98
+A18 200.70 204.40 197.90
+B18 164.93 117.44 177.68
+C18 61.227 72.827 56.013
+D18 204.51 199.19 195.95
+E18 73.692 73.322 148.06
+F18 68.313 128.24 45.392
+G18 131.14 85.288 152.52
+H18 89.609 153.68 93.128
+I18 30.179 29.676 28.922
+J18 208.65 210.69 209.48
+K18 88.863 70.346 63.498
+L18 197.92 177.16 54.602
+M18 143.55 91.470 162.00
+N18 121.99 131.52 122.44
+O18 172.04 115.00 91.328
+P18 81.187 74.923 148.64
+Q18 117.91 169.48 101.12
+R18 183.93 145.62 52.297
+S18 55.452 105.38 65.438
+T18 211.11 208.92 210.90
+U18 177.41 124.39 134.00
+V18 53.506 90.891 78.889
+W18 197.65 205.13 206.81
+X18 51.396 86.860 43.674
+Y18 193.58 175.18 113.92
+Z18 96.313 103.94 178.60
+2A18 168.53 106.08 128.73
+2B18 85.496 104.15 136.89
+2C18 208.09 210.26 179.31
+2D18 161.94 92.492 50.116
+A19 156.71 112.54 101.12
+B19 42.513 58.501 40.536
+C19 172.17 152.13 143.40
+D19 159.73 183.13 54.703
+E19 119.21 105.48 95.518
+F19 30.235 29.764 29.269
+G19 88.689 134.99 136.69
+H19 86.392 53.901 45.566
+I19 126.88 158.15 160.78
+J19 120.42 79.125 64.241
+K19 108.34 160.80 164.56
+L19 49.327 39.952 38.592
+M19 67.917 128.27 44.976
+N19 127.84 62.080 45.945
+O19 74.380 138.45 130.11
+P19 30.060 29.337 28.817
+Q19 147.97 73.539 46.480
+R19 75.153 76.608 151.15
+S19 78.339 144.41 52.936
+T19 31.312 30.556 30.767
+U19 169.32 177.98 203.35
+V19 44.471 72.376 45.634
+W19 199.02 205.39 55.979
+X19 90.125 81.954 73.039
+Y19 207.33 211.44 212.22
+Z19 182.95 126.85 175.69
+2A19 77.857 90.598 163.13
+2B19 202.16 204.23 49.052
+2C19 162.94 96.440 68.740
+2D19 83.952 84.116 160.97
+
diff --git a/scanin/CMP_Digital_Target-3.cie b/scanin/CMP_Digital_Target-3.cie
new file mode 100644
index 0000000..6c8ff5d
--- /dev/null
+++ b/scanin/CMP_Digital_Target-3.cie
@@ -0,0 +1,591 @@
+CTI3
+
+DESCRIPTOR "Argyll Calibration Target chart information 3"
+ORIGINATOR "Argyll chartread"
+CREATED "Mon Sep 7 08:33:21 2009"
+KEYWORD "DEVICE_CLASS"
+DEVICE_CLASS "OUTPUT"
+KEYWORD "COLOR_REP"
+COLOR_REP "RGB_XYZ"
+KEYWORD "TARGET_INSTRUMENT"
+TARGET_INSTRUMENT "GretagMacbeth i1 Pro"
+
+KEYWORD "SAMPLE_LOC"
+NUMBER_OF_FIELDS 8
+BEGIN_DATA_FORMAT
+SAMPLE_ID SAMPLE_LOC RGB_R RGB_G RGB_B XYZ_X XYZ_Y XYZ_Z
+END_DATA_FORMAT
+
+NUMBER_OF_SETS 570
+BEGIN_DATA
+1 "A1" 106.60 73.896 140.50 19.991 14.120 27.886
+2 "B1" 101.22 157.76 91.298 19.926 34.054 14.005
+3 "C1" 56.474 54.712 116.20 6.2319 5.6339 19.078
+4 "D1" 142.23 143.96 106.67 32.785 35.198 16.672
+5 "E1" 161.58 92.303 143.82 38.996 22.658 25.805
+6 "F1" 90.405 96.497 94.153 16.011 17.612 14.561
+7 "G1" 163.39 98.124 88.178 39.496 24.997 12.003
+8 "H1" 63.278 55.370 115.21 7.4905 6.2468 18.358
+9 "I1" 71.385 133.64 49.363 11.987 23.286 6.5928
+10 "J1" 97.757 75.383 143.28 16.591 12.444 27.331
+11 "K1" 194.49 193.52 200.39 63.038 64.179 58.403
+12 "L1" 43.729 37.037 36.612 4.3513 3.8393 2.7750
+13 "M1" 128.36 143.53 178.51 27.386 30.873 42.527
+14 "N1" 136.71 176.12 173.90 31.789 44.281 40.439
+15 "O1" 190.25 190.03 173.26 59.453 61.985 40.335
+16 "P1" 139.89 69.074 45.054 26.783 15.713 3.5833
+17 "Q1" 34.433 40.188 37.667 3.0238 3.6843 3.0876
+18 "R1" 87.362 153.04 89.833 15.462 28.885 12.776
+19 "S1" 124.79 84.048 151.84 23.694 16.421 29.352
+20 "T1" 201.42 206.76 79.283 65.900 73.381 11.377
+21 "U1" 59.875 104.70 93.370 8.3333 15.036 13.749
+22 "V1" 205.39 208.60 159.63 72.416 77.985 33.423
+23 "W1" 91.572 136.12 167.03 16.393 25.254 38.122
+24 "X1" 30.336 29.306 28.957 2.3144 2.3581 1.9303
+25 "Y1" 132.60 110.17 107.91 27.102 23.276 16.813
+26 "Z1" 32.956 33.215 31.185 2.6994 2.8850 2.2332
+27 "2A1" 70.282 129.32 46.946 10.757 20.588 4.8216
+28 "2B1" 159.00 173.65 202.91 40.765 45.919 58.857
+29 "2C1" 121.19 88.150 161.84 22.864 16.381 33.839
+30 "2D1" 136.59 101.50 125.83 28.289 21.404 20.483
+31 "A2" 75.414 80.525 150.43 10.412 10.567 30.978
+32 "B2" 128.64 63.027 42.872 24.139 14.085 3.5857
+33 "C2" 101.62 116.40 166.26 19.473 21.752 38.376
+34 "D2" 129.62 78.772 146.55 26.280 16.991 28.035
+35 "E2" 164.20 144.75 82.589 42.573 39.671 9.6556
+36 "F2" 96.904 110.83 179.21 17.113 18.705 43.690
+37 "G2" 157.24 84.071 46.359 35.850 22.548 3.8554
+38 "H2" 84.439 70.424 140.26 13.037 10.392 26.425
+39 "I2" 81.661 147.44 58.864 14.946 28.262 8.1825
+40 "J2" 35.433 35.856 32.072 3.1780 3.3914 2.3047
+41 "K2" 150.66 144.08 140.58 35.880 35.178 26.683
+42 "L2" 158.35 93.379 154.74 36.434 22.147 28.386
+43 "M2" 100.38 93.492 99.897 17.689 17.022 15.072
+44 "N2" 158.93 93.968 95.921 36.396 23.022 13.623
+45 "O2" 102.60 137.21 171.09 19.884 26.988 39.544
+46 "P2" 74.932 50.703 44.902 10.294 7.4271 3.4839
+47 "Q2" 115.12 165.78 166.70 24.415 37.008 36.288
+48 "R2" 157.97 92.663 150.58 36.078 21.682 26.898
+49 "S2" 83.913 88.787 161.27 12.232 12.247 33.412
+50 "T2" 72.772 133.20 48.154 12.486 23.920 5.4357
+51 "U2" 155.64 177.45 175.23 38.913 47.091 40.794
+52 "V2" 65.311 50.797 44.712 8.3779 6.8387 3.7119
+53 "W2" 30.304 29.389 28.630 2.3401 2.3804 1.9157
+54 "X2" 98.383 112.57 76.501 17.501 20.678 9.7225
+55 "Y2" 196.95 176.56 81.174 60.324 56.490 9.7029
+56 "Z2" 106.87 76.461 64.020 18.340 14.257 6.9452
+57 "2A2" 149.29 180.57 55.756 39.051 50.700 6.0515
+58 "2B2" 46.850 72.093 42.887 5.3134 8.7205 3.7883
+59 "2C2" 126.21 86.097 104.74 24.040 16.842 14.641
+60 "2D2" 68.443 138.05 59.668 10.963 22.720 8.1563
+61 "A3" 40.703 34.668 43.170 3.2215 2.8069 3.1344
+62 "B3" 140.80 177.61 130.47 35.703 49.122 24.276
+63 "C3" 71.422 72.467 144.63 9.6710 9.2674 28.141
+64 "D3" 157.83 159.05 109.87 39.368 42.329 16.693
+65 "E3" 74.511 78.412 148.91 9.8448 9.8037 29.501
+66 "F3" 172.35 192.98 167.60 50.040 60.997 38.371
+67 "G3" 72.505 72.322 146.66 9.8731 9.2375 28.216
+68 "H3" 132.85 167.63 50.020 31.926 43.409 5.2962
+69 "I3" 99.822 132.27 193.48 18.889 24.530 52.235
+70 "J3" 158.78 88.817 87.144 36.271 22.167 11.952
+71 "K3" 146.32 156.61 192.38 34.281 37.060 50.573
+72 "L3" 30.978 30.641 29.826 2.4511 2.5353 2.0398
+73 "M3" 146.27 153.30 150.57 34.120 37.136 30.510
+74 "N3" 144.16 70.919 46.534 28.309 16.461 3.7229
+75 "O3" 85.091 72.947 144.27 12.851 10.508 27.052
+76 "P3" 122.91 131.12 67.881 24.532 27.910 7.4170
+77 "Q3" 140.27 116.77 178.48 29.800 23.719 40.761
+78 "R3" 73.020 103.31 46.082 11.420 17.249 4.2060
+79 "S3" 148.58 74.828 46.145 30.098 17.923 3.6411
+80 "T3" 92.676 106.61 179.17 15.102 16.455 41.548
+81 "U3" 89.067 149.36 56.522 16.816 29.880 6.2892
+82 "V3" 104.27 103.98 177.68 19.054 17.886 40.635
+83 "W3" 122.27 64.970 44.965 21.160 13.146 3.6418
+84 "X3" 190.80 157.08 167.65 56.827 44.647 36.033
+85 "Y3" 55.033 94.759 44.077 7.3224 13.350 4.3645
+86 "Z3" 157.08 85.576 87.558 34.447 20.515 11.595
+87 "2A3" 79.250 88.578 159.52 10.795 11.448 32.464
+88 "2B3" 108.15 127.71 55.172 20.617 25.691 5.5795
+89 "2C3" 78.102 75.847 150.05 11.126 10.174 28.914
+90 "2D3" 202.83 194.20 199.16 68.724 66.102 56.410
+91 "A4" 105.09 86.729 157.14 19.671 15.554 33.919
+92 "B4" 99.696 102.26 43.923 17.501 19.753 3.8498
+93 "C4" 107.26 130.81 190.72 21.509 25.734 51.674
+94 "D4" 178.30 141.51 75.719 51.958 42.451 9.1213
+95 "E4" 86.201 111.97 176.39 14.112 17.806 42.697
+96 "F4" 192.41 193.92 143.50 61.487 65.884 27.427
+97 "G4" 161.20 95.317 155.94 38.420 23.239 29.457
+98 "H4" 74.863 77.945 149.59 9.8759 9.7311 29.126
+99 "I4" 207.61 208.23 184.16 75.793 79.718 46.695
+100 "J4" 70.931 101.90 131.71 10.595 15.480 24.653
+101 "K4" 147.01 94.545 162.80 32.187 21.331 32.814
+102 "L4" 81.586 103.83 104.84 13.777 17.753 16.663
+103 "M4" 141.06 69.848 45.247 27.339 16.027 3.5603
+104 "N4" 90.494 151.07 150.65 16.922 28.995 29.336
+105 "O4" 55.294 41.016 63.313 6.3066 4.7610 6.9921
+106 "P4" 111.84 140.47 103.22 22.390 29.210 15.592
+107 "Q4" 144.45 93.249 162.31 30.657 20.369 32.268
+108 "R4" 105.31 104.82 101.31 19.152 19.807 15.592
+109 "S4" 79.325 137.68 48.973 13.948 25.671 5.2122
+110 "T4" 84.189 150.82 120.01 14.991 27.898 19.745
+111 "U4" 81.413 52.379 93.432 11.486 7.6381 12.484
+112 "V4" 76.459 77.531 42.048 10.749 11.899 3.2945
+113 "W4" 211.28 212.44 213.00 77.699 80.782 67.995
+114 "X4" 125.79 102.06 67.469 24.430 21.610 7.1800
+115 "Y4" 36.736 36.340 46.306 3.2104 3.1999 4.2513
+116 "Z4" 83.114 150.29 61.891 14.450 27.763 8.5197
+117 "2A4" 117.71 90.574 160.37 21.679 16.235 32.574
+118 "2B4" 89.463 153.87 72.494 15.737 29.267 10.078
+119 "2C4" 42.498 47.832 65.570 3.9226 4.4418 7.5674
+120 "2D4" 115.91 156.34 101.73 24.022 34.375 15.022
+121 "A5" 100.43 57.576 43.875 16.920 10.945 3.6233
+122 "B5" 139.57 140.52 191.37 32.876 32.373 51.547
+123 "C5" 106.24 157.82 63.346 23.454 37.144 7.5202
+124 "D5" 120.63 119.68 128.56 24.762 25.266 23.702
+125 "E5" 144.32 70.530 44.643 29.192 16.958 3.5359
+126 "F5" 112.12 164.88 139.23 23.640 37.186 26.447
+127 "G5" 103.37 136.87 47.413 20.387 28.842 4.7028
+128 "H5" 36.251 41.780 35.395 3.3636 4.0809 2.6833
+129 "I5" 111.24 162.80 164.26 23.272 35.820 35.198
+130 "J5" 143.56 87.562 156.55 30.642 19.573 30.103
+131 "K5" 109.54 163.06 164.19 22.753 35.416 35.064
+132 "L5" 79.627 52.133 45.354 11.209 7.8786 3.5144
+133 "M5" 92.456 152.33 153.19 17.513 29.721 30.234
+134 "N5" 34.714 33.013 32.281 2.9935 2.9559 2.2837
+135 "O5" 166.35 175.94 163.99 44.139 49.035 35.423
+136 "P5" 89.669 79.549 47.144 14.200 13.959 3.6761
+137 "Q5" 191.24 182.85 188.48 59.021 56.831 48.262
+138 "R5" 51.675 70.899 41.770 6.3401 9.2262 3.4989
+139 "S5" 170.53 177.54 173.76 45.700 49.818 40.124
+140 "T5" 63.890 93.942 45.687 9.3142 14.437 4.0636
+141 "U5" 30.168 29.299 28.940 2.3535 2.3975 1.9290
+142 "V5" 133.08 133.14 122.05 27.766 28.925 20.091
+143 "W5" 36.054 32.923 33.276 3.0887 2.9300 2.3596
+144 "X5" 61.314 111.77 45.755 8.7239 16.803 5.0276
+145 "Y5" 203.39 209.40 208.06 70.161 75.882 63.247
+146 "Z5" 76.353 77.670 109.77 11.191 11.244 16.654
+147 "2A5" 193.86 202.18 186.73 62.394 68.771 47.096
+148 "2B5" 84.630 73.965 145.61 12.640 10.459 27.333
+149 "2C5" 68.392 129.05 45.336 11.160 21.761 5.5099
+150 "2D5" 210.46 210.49 195.89 77.413 80.592 53.936
+151 "A6" 156.79 90.614 148.03 37.703 22.453 27.499
+152 "B6" 88.481 111.82 178.46 14.943 18.301 44.058
+153 "C6" 167.90 108.82 66.983 44.282 30.956 7.9185
+154 "D6" 66.578 110.85 54.360 10.440 18.144 6.5076
+155 "E6" 148.31 87.735 154.80 32.892 20.486 29.274
+156 "F6" 93.611 155.13 115.73 17.819 31.263 19.184
+157 "G6" 40.190 39.280 58.763 3.7468 3.6058 6.3752
+158 "H6" 170.89 152.59 147.19 45.907 41.132 28.818
+159 "I6" 46.469 44.545 75.326 4.4472 4.1158 9.1204
+160 "J6" 102.48 151.18 180.03 20.388 31.549 44.358
+161 "K6" 30.872 30.164 29.516 2.3970 2.4515 1.9961
+162 "L6" 83.294 137.00 98.315 14.323 24.343 14.536
+163 "M6" 30.722 30.150 29.446 2.3623 2.4293 1.9541
+164 "N6" 163.74 151.67 194.83 41.190 36.767 50.714
+165 "O6" 199.83 203.65 49.970 62.357 69.847 6.3069
+166 "P6" 77.512 86.873 159.07 10.124 10.811 31.915
+167 "Q6" 200.99 194.31 190.45 67.127 65.762 49.352
+168 "R6" 165.39 100.99 154.68 39.128 23.959 27.822
+169 "S6" 69.496 98.026 61.986 10.413 15.222 6.7952
+170 "T6" 167.68 124.93 146.98 41.195 30.074 26.292
+171 "U6" 62.706 68.483 60.211 8.4099 9.5591 6.5834
+172 "V6" 174.63 108.06 166.14 43.942 26.976 32.439
+173 "W6" 138.60 174.02 106.27 33.397 44.263 15.513
+174 "X6" 192.12 161.59 197.05 56.629 45.672 50.137
+175 "Y6" 71.207 70.503 63.173 10.198 10.583 6.9283
+176 "Z6" 171.42 121.85 182.26 42.782 29.822 40.386
+177 "2A6" 193.77 197.98 207.14 61.567 65.013 61.274
+178 "2B6" 117.80 82.965 151.94 21.335 15.115 28.916
+179 "2C6" 36.305 39.517 34.462 3.2203 3.7212 2.6005
+180 "2D6" 152.48 145.44 148.89 35.799 34.854 29.422
+181 "A7" 93.637 153.35 73.710 19.123 33.078 10.667
+182 "B7" 83.277 73.755 144.52 12.915 10.831 28.225
+183 "C7" 206.85 206.70 197.33 77.518 80.587 57.622
+184 "D7" 77.454 73.349 146.72 11.348 10.154 28.703
+185 "E7" 70.809 132.64 47.023 12.442 24.254 5.6475
+186 "F7" 91.049 88.345 163.10 15.345 13.969 34.910
+187 "G7" 177.87 190.87 53.018 52.703 61.988 5.9513
+188 "H7" 75.960 87.720 159.98 9.9056 10.888 32.881
+189 "I7" 199.48 192.77 181.55 67.471 66.483 44.705
+190 "J7" 64.713 68.090 67.610 8.8426 9.6268 8.1730
+191 "K7" 193.08 175.48 139.54 61.925 56.855 24.968
+192 "L7" 75.430 87.876 159.79 9.6349 10.726 32.427
+193 "M7" 133.24 173.17 56.742 32.435 45.157 6.1480
+194 "N7" 148.49 75.098 46.216 30.427 18.146 3.6270
+195 "O7" 97.134 97.177 86.559 16.928 17.695 11.911
+196 "P7" 191.20 160.30 54.685 56.094 48.894 4.9467
+197 "Q7" 80.068 80.251 125.32 11.934 11.778 20.573
+198 "R7" 83.368 147.19 55.358 15.285 28.434 6.3346
+199 "S7" 37.747 44.719 36.907 3.5054 4.3615 2.7830
+200 "T7" 146.52 175.26 205.50 35.099 44.589 58.771
+201 "U7" 133.97 82.941 152.98 26.155 17.005 28.079
+202 "V7" 63.182 103.22 97.125 8.9086 14.924 14.071
+203 "W7" 144.22 147.07 76.384 30.611 33.536 8.6858
+204 "X7" 212.78 213.19 211.89 79.113 81.784 66.503
+205 "Y7" 77.896 86.298 158.37 10.240 10.764 31.229
+206 "Z7" 71.313 133.52 47.389 11.976 23.218 5.4228
+207 "2A7" 75.926 78.650 151.98 9.9976 9.7574 28.929
+208 "2B7" 118.08 168.98 98.781 25.572 39.297 14.262
+209 "2C7" 207.34 198.34 43.623 66.257 69.908 5.8960
+210 "2D7" 92.639 108.26 179.66 14.893 16.603 41.626
+211 "A8" 69.904 137.36 58.325 12.100 24.359 8.4071
+212 "B8" 167.09 144.55 49.599 42.436 39.900 4.7260
+213 "C8" 111.10 78.672 121.83 20.574 15.216 20.136
+214 "D8" 74.340 141.34 56.829 12.701 25.255 8.1803
+215 "E8" 104.13 74.055 141.65 18.324 13.039 26.409
+216 "F8" 90.075 154.61 90.911 16.724 30.035 13.212
+217 "G8" 36.341 33.896 36.868 3.3200 3.1831 3.0291
+218 "H8" 101.65 159.66 134.07 20.304 33.570 24.443
+219 "I8" 34.216 33.499 37.712 2.9169 2.9009 3.0168
+220 "J8" 174.41 165.56 173.38 48.038 46.099 39.891
+221 "K8" 46.252 66.300 42.397 5.2241 8.0427 3.5985
+222 "L8" 152.69 184.12 182.69 38.619 50.368 44.407
+223 "M8" 30.240 29.863 28.939 2.3428 2.3959 1.9273
+224 "N8" 35.593 35.467 34.190 3.1956 3.3116 2.6415
+225 "O8" 71.728 71.553 70.937 10.494 10.847 8.8081
+226 "P8" 115.92 116.10 114.68 22.233 23.005 18.548
+227 "Q8" 159.85 160.38 159.13 39.317 40.778 33.056
+228 "R8" 196.60 197.21 197.12 63.147 65.480 53.862
+229 "S8" 137.57 112.53 140.33 28.252 23.514 24.400
+230 "T8" 80.940 142.13 50.952 14.520 26.909 5.4756
+231 "U8" 99.365 133.93 196.43 18.057 24.054 51.817
+232 "V8" 30.957 30.566 29.813 2.3402 2.3911 1.9209
+233 "W8" 212.09 210.69 210.55 77.838 79.255 64.883
+234 "X8" 90.920 71.799 46.751 14.232 12.342 3.5677
+235 "Y8" 137.72 177.30 175.40 31.451 43.594 39.472
+236 "Z8" 39.524 49.911 38.685 3.8429 5.0881 3.0661
+237 "2A8" 130.39 176.13 127.05 27.844 41.491 21.140
+238 "2B8" 31.904 31.437 30.690 2.4921 2.5648 2.0613
+239 "2C8" 149.77 158.18 196.00 34.635 36.777 50.964
+240 "2D8" 30.719 29.824 29.600 2.3436 2.3905 1.9440
+241 "A9" 160.64 188.72 135.75 46.811 59.109 25.406
+242 "B9" 151.09 113.48 161.82 35.695 26.905 33.928
+243 "C9" 56.378 101.12 44.028 8.0578 15.313 4.8058
+244 "D9" 200.86 192.61 183.06 70.562 68.540 46.575
+245 "E9" 77.639 89.149 161.20 10.633 11.656 33.787
+246 "F9" 183.43 148.52 132.23 54.113 43.360 23.933
+247 "G9" 53.868 83.869 77.703 7.1200 11.358 10.328
+248 "H9" 144.68 93.693 163.06 31.237 20.786 32.756
+249 "I9" 80.625 147.05 117.46 14.469 27.174 19.553
+250 "J9" 82.612 53.093 45.342 11.813 8.1727 3.4750
+251 "K9" 179.05 178.11 205.03 51.613 51.265 59.093
+252 "L9" 68.256 128.21 45.397 11.232 21.889 5.3482
+253 "M9" 30.122 29.559 28.786 2.3389 2.3932 1.9205
+254 "N9" 44.132 44.030 43.113 4.6543 4.8363 3.9004
+255 "O9" 77.603 77.496 77.212 11.973 12.444 10.082
+256 "P9" 123.38 123.35 123.35 24.548 25.410 20.720
+257 "Q9" 167.61 168.12 167.11 43.270 44.842 36.215
+258 "R9" 202.61 204.17 205.53 68.113 71.034 59.864
+259 "S9" 152.34 81.269 59.657 31.468 18.903 5.4734
+260 "T9" 36.525 39.233 37.120 3.2297 3.6435 2.9170
+261 "U9" 147.79 112.59 105.57 31.779 25.042 15.321
+262 "V9" 128.85 175.42 85.449 30.098 43.673 10.856
+263 "W9" 202.12 182.11 203.04 65.360 57.273 55.175
+264 "X9" 58.841 111.22 46.014 7.8261 15.682 5.0934
+265 "Y9" 150.27 74.574 46.780 30.151 17.603 3.5424
+266 "Z9" 91.373 72.814 142.15 14.169 10.872 25.545
+267 "2A9" 178.42 178.87 165.68 49.740 51.961 35.295
+268 "2B9" 110.21 82.066 150.83 19.293 14.167 28.533
+269 "2C9" 115.57 141.00 82.227 23.305 29.469 10.465
+270 "2D9" 120.44 87.308 156.62 22.420 16.254 30.792
+271 "A10" 54.278 98.104 59.100 7.4766 14.316 6.7672
+272 "B10" 141.49 142.92 50.130 30.693 34.185 4.8132
+273 "C10" 69.545 135.98 93.417 11.568 23.235 13.773
+274 "D10" 30.772 30.009 29.471 2.4307 2.4999 2.0171
+275 "E10" 140.52 89.479 159.03 30.140 19.949 31.751
+276 "F10" 88.149 135.62 165.23 15.810 25.353 37.521
+277 "G10" 30.508 30.075 29.389 2.4272 2.4936 1.9887
+278 "H10" 75.794 144.13 86.030 12.549 25.096 11.562
+279 "I10" 29.881 29.286 28.915 2.3402 2.3788 1.9223
+280 "J10" 78.427 139.08 135.09 13.387 24.235 23.958
+281 "K10" 127.69 103.24 52.122 25.478 22.853 4.3681
+282 "L10" 47.518 38.484 37.887 5.0366 4.2550 2.8902
+283 "M10" 30.202 29.466 28.978 2.3374 2.3915 1.9194
+284 "N10" 50.671 50.438 49.901 5.8942 6.1054 4.9143
+285 "O10" 84.087 84.218 84.130 13.462 14.013 11.428
+286 "P10" 130.92 131.27 131.19 27.055 28.014 23.007
+287 "Q10" 174.24 174.94 173.31 47.406 49.169 39.386
+288 "R10" 208.46 209.75 212.17 73.974 77.043 65.729
+289 "S10" 61.440 61.759 68.112 7.8358 8.0794 7.9176
+290 "T10" 189.59 154.63 195.61 54.395 42.563 48.165
+291 "U10" 70.065 139.76 58.155 11.113 22.841 7.7084
+292 "V10" 34.382 38.877 37.374 2.9394 3.4387 2.9247
+293 "W10" 81.185 148.91 88.683 13.284 25.973 11.867
+294 "X10" 31.337 30.706 30.647 2.4768 2.5120 2.1182
+295 "Y10" 132.10 124.54 122.36 27.102 26.481 19.829
+296 "Z10" 128.86 77.259 44.848 23.978 16.698 3.6586
+297 "2A10" 121.07 144.71 145.24 24.659 30.178 27.108
+298 "2B10" 91.404 54.498 76.524 13.618 8.1445 8.4106
+299 "2C10" 122.45 129.19 129.52 24.412 26.496 22.533
+300 "2D10" 52.091 43.712 85.349 5.2897 4.2252 10.803
+301 "A11" 95.121 56.945 109.13 15.662 9.9371 16.757
+302 "B11" 49.247 84.978 41.595 6.3528 11.756 4.1276
+303 "C11" 201.38 188.28 201.57 70.247 65.054 58.834
+304 "D11" 165.99 106.71 48.883 42.904 30.283 4.3741
+305 "E11" 142.45 151.87 140.77 33.675 37.292 27.259
+306 "F11" 59.895 43.373 70.686 7.3260 5.3398 8.4321
+307 "G11" 185.60 179.80 175.52 56.748 56.026 41.441
+308 "H11" 62.810 62.001 39.464 8.1585 8.7147 3.0001
+309 "I11" 182.38 181.99 177.01 54.512 56.195 42.466
+310 "J11" 44.613 62.579 56.523 4.9736 7.2275 6.1574
+311 "K11" 177.26 171.26 154.02 50.413 50.352 29.675
+312 "L11" 81.706 58.921 119.57 11.783 8.3671 18.993
+313 "M11" 30.648 29.769 29.464 2.4004 2.4632 1.9865
+314 "N11" 56.534 56.324 55.689 7.0502 7.2979 5.9348
+315 "O11" 89.911 89.936 88.980 15.157 15.663 12.643
+316 "P11" 140.77 141.41 140.88 30.809 31.935 26.037
+317 "Q11" 179.84 180.21 178.58 51.029 52.827 42.179
+318 "R11" 212.60 213.84 215.53 78.892 82.077 69.152
+319 "S11" 66.493 66.184 65.558 9.0391 9.3614 7.5795
+320 "T11" 187.32 160.61 146.99 54.688 46.067 27.270
+321 "U11" 59.842 44.929 41.517 7.0672 5.5203 3.1542
+322 "V11" 100.71 151.70 177.40 19.018 30.251 40.808
+323 "W11" 32.433 32.983 31.555 2.6438 2.7813 2.1741
+324 "X11" 103.41 161.56 150.03 20.294 33.254 28.954
+325 "Y11" 49.930 46.756 84.028 4.7917 4.3367 10.461
+326 "Z11" 157.24 187.57 185.20 39.678 51.335 44.730
+327 "2A11" 69.840 130.51 46.639 11.397 22.075 5.4606
+328 "2B11" 164.73 190.77 189.92 43.396 54.386 47.692
+329 "2C11" 67.660 57.565 50.639 9.0264 8.0855 4.5632
+330 "2D11" 146.88 73.255 46.894 29.263 17.239 3.6542
+331 "A12" 149.18 183.06 84.464 42.435 55.238 11.960
+332 "B12" 72.446 71.794 146.17 10.244 9.5241 28.550
+333 "C12" 111.58 163.80 82.344 25.557 39.490 11.397
+334 "D12" 67.115 62.023 67.669 9.5983 9.1448 8.3283
+335 "E12" 202.25 206.59 144.16 71.285 77.444 27.463
+336 "F12" 142.52 142.52 149.46 32.592 33.399 30.058
+337 "G12" 52.218 46.843 96.005 5.1430 4.3541 13.182
+338 "H12" 95.301 157.62 128.66 18.517 32.087 22.562
+339 "I12" 58.804 42.199 45.333 6.9977 5.1440 3.8014
+340 "J12" 201.67 207.01 112.57 69.246 76.042 17.844
+341 "K12" 76.878 56.433 113.86 10.642 7.6407 17.593
+342 "L12" 110.43 164.87 121.89 21.927 35.477 19.681
+343 "M12" 31.073 30.502 29.828 2.4287 2.4921 1.9949
+344 "N12" 60.863 60.999 60.221 7.9937 8.3172 6.7360
+345 "O12" 97.436 97.414 96.425 17.120 17.761 14.285
+346 "P12" 146.96 147.76 147.68 33.242 34.486 28.221
+347 "Q12" 186.04 186.49 184.52 55.136 57.000 45.303
+348 "R12" 217.03 217.69 217.80 84.047 87.070 71.496
+349 "S12" 173.81 166.67 164.48 46.799 45.618 34.284
+350 "T12" 61.998 59.133 126.43 6.6921 6.0185 19.773
+351 "U12" 133.08 178.53 107.01 31.583 45.365 15.646
+352 "V12" 159.59 92.307 67.569 35.975 22.808 7.0470
+353 "W12" 92.823 93.091 102.67 15.632 16.004 15.333
+354 "X12" 153.31 79.910 48.385 32.188 19.569 3.7849
+355 "Y12" 76.169 77.765 152.48 9.9981 9.6287 28.171
+356 "Z12" 80.867 145.82 55.209 14.429 27.348 6.4901
+357 "2A12" 88.726 84.091 155.13 13.822 12.446 29.936
+358 "2B12" 201.37 208.14 195.53 68.261 74.431 52.067
+359 "2C12" 32.004 31.705 30.813 2.4685 2.5621 2.0287
+360 "2D12" 135.53 145.08 187.78 28.798 30.974 45.549
+361 "A13" 86.415 69.301 138.07 14.129 10.884 26.286
+362 "B13" 104.58 136.42 46.559 21.280 29.662 4.6515
+363 "C13" 147.82 79.114 100.32 32.064 17.966 13.604
+364 "D13" 75.334 85.157 157.26 9.9611 10.733 32.277
+365 "E13" 178.81 179.96 148.24 51.845 54.927 28.181
+366 "F13" 55.482 72.327 100.16 6.3441 8.5421 15.068
+367 "G13" 200.09 199.75 184.43 68.699 71.452 46.387
+368 "H13" 76.688 84.710 151.41 10.374 10.902 29.169
+369 "I13" 129.73 107.35 52.022 26.203 23.997 4.3858
+370 "J13" 119.03 160.66 198.42 25.753 36.554 54.373
+371 "K13" 56.473 50.032 106.16 5.9068 4.9157 15.287
+372 "L13" 153.18 106.56 46.936 35.348 27.270 4.1933
+373 "M13" 32.264 31.978 31.289 2.6154 2.7053 2.1626
+374 "N13" 66.470 66.520 66.246 9.3637 9.6843 7.8391
+375 "O13" 105.28 105.86 104.76 19.359 20.106 16.147
+376 "P13" 153.75 154.63 154.12 36.624 37.957 30.671
+377 "Q13" 192.03 192.25 191.27 59.859 61.861 49.702
+378 "R13" 221.68 221.89 221.96 89.957 93.025 75.604
+379 "S13" 104.20 75.856 145.33 17.613 12.665 26.369
+380 "T13" 121.87 124.18 72.370 22.969 25.114 7.7873
+381 "U13" 91.392 55.886 72.021 13.623 8.3610 7.6135
+382 "V13" 193.01 176.53 161.70 59.661 54.433 33.140
+383 "W13" 97.047 61.496 44.638 15.045 10.609 3.4373
+384 "X13" 124.18 171.27 172.48 26.676 39.190 37.659
+385 "Y13" 100.95 103.55 45.932 16.770 18.831 3.7976
+386 "Z13" 98.719 76.722 108.03 16.243 13.004 15.620
+387 "2A13" 193.99 204.68 205.71 62.079 69.145 59.254
+388 "2B13" 77.619 94.368 167.50 9.8300 11.508 34.315
+389 "2C13" 91.512 53.191 50.701 13.378 8.4024 4.1065
+390 "2D13" 76.609 104.98 135.45 11.706 16.228 24.708
+391 "A14" 129.84 79.131 147.61 27.260 17.709 28.446
+392 "B14" 74.731 86.884 158.53 10.007 11.116 33.309
+393 "C14" 178.45 194.65 198.02 55.170 63.826 56.969
+394 "D14" 122.80 65.789 71.805 22.474 12.791 7.8220
+395 "E14" 99.529 101.15 66.691 17.833 19.340 7.6068
+396 "F14" 133.73 68.160 43.803 25.508 15.387 3.5019
+397 "G14" 130.31 174.28 137.87 28.674 42.244 25.294
+398 "H14" 110.49 77.413 145.63 20.013 14.167 27.201
+399 "I14" 30.999 29.764 29.587 2.4079 2.4331 1.9867
+400 "J14" 120.14 114.13 178.99 23.527 21.066 41.374
+401 "K14" 128.29 64.544 74.117 23.203 12.509 8.0121
+402 "L14" 176.70 157.34 183.40 48.486 42.511 43.112
+403 "M14" 59.777 105.00 45.141 8.6031 15.926 4.6200
+404 "N14" 182.00 181.11 190.30 53.107 53.884 48.977
+405 "O14" 42.651 40.749 62.042 3.9836 3.7404 6.5852
+406 "P14" 151.32 150.31 182.91 35.517 35.068 43.001
+407 "Q14" 162.51 94.932 125.82 37.782 22.314 19.042
+408 "R14" 99.745 94.301 93.467 17.467 17.105 13.104
+409 "S14" 107.25 159.78 58.090 22.370 35.508 5.9931
+410 "T14" 128.01 72.369 117.84 23.886 13.985 16.868
+411 "U14" 107.76 101.18 90.766 19.591 19.252 12.172
+412 "V14" 204.14 190.16 48.644 63.345 63.812 5.6645
+413 "W14" 82.597 65.626 44.970 12.028 10.481 3.3856
+414 "X14" 166.39 166.20 175.82 42.635 43.420 39.912
+415 "Y14" 100.69 152.17 51.384 19.856 32.192 5.1431
+416 "Z14" 91.650 100.20 90.746 15.570 17.641 12.711
+417 "2A14" 143.62 91.243 162.04 29.733 19.495 31.105
+418 "2B14" 214.71 215.34 214.31 81.209 84.285 67.959
+419 "2C14" 101.32 87.875 161.74 16.972 13.897 32.504
+420 "2D14" 105.18 138.75 68.497 20.262 27.906 7.7177
+421 "A15" 124.84 68.758 114.78 24.922 14.319 17.652
+422 "B15" 99.929 150.22 167.53 20.394 32.266 39.177
+423 "C15" 121.25 165.78 57.133 28.847 42.042 6.3345
+424 "D15" 129.49 121.04 130.58 27.579 26.553 23.681
+425 "E15" 161.27 95.254 123.97 38.874 23.290 19.357
+426 "F15" 148.20 174.03 142.22 36.703 46.352 27.027
+427 "G15" 150.41 98.923 167.42 34.061 22.803 34.531
+428 "H15" 75.180 91.615 158.04 10.113 11.921 31.879
+429 "I15" 196.22 179.25 172.32 64.423 58.008 39.281
+430 "J15" 66.558 62.105 60.696 9.4542 9.1717 6.7120
+431 "K15" 174.25 177.65 112.40 48.073 52.062 16.154
+432 "L15" 116.33 112.29 162.02 22.225 20.657 33.169
+433 "M15" 133.68 171.46 73.038 32.410 43.840 8.6168
+434 "N15" 102.47 146.49 198.15 19.932 29.141 53.896
+435 "O15" 64.715 46.574 43.032 8.2354 6.1612 3.2448
+436 "P15" 105.91 159.08 164.78 21.186 33.689 34.860
+437 "Q15" 34.379 33.754 36.003 2.8839 2.9372 2.7235
+438 "R15" 102.62 61.271 119.89 16.647 10.677 18.389
+439 "S15" 159.62 86.224 48.021 35.874 22.454 3.7685
+440 "T15" 95.611 158.57 127.45 18.024 31.495 21.231
+441 "U15" 133.03 87.140 157.87 26.027 17.635 30.172
+442 "V15" 48.894 77.368 43.484 5.7623 9.6302 3.8733
+443 "W15" 182.54 191.20 178.40 53.522 58.991 41.376
+444 "X15" 126.00 95.308 166.74 23.991 17.693 34.060
+445 "Y15" 38.376 39.986 33.590 3.5595 3.8688 2.3978
+446 "Z15" 148.30 148.94 147.37 34.055 35.265 28.241
+447 "2A15" 146.55 75.055 68.429 29.379 16.826 7.3609
+448 "2B15" 71.706 70.645 144.63 9.2107 8.5579 25.691
+449 "2C15" 133.02 80.816 44.452 25.707 18.129 3.6532
+450 "2D15" 82.777 113.54 117.52 13.875 19.568 19.948
+451 "A16" 147.43 75.104 45.417 32.441 19.582 3.7464
+452 "B16" 162.22 145.05 108.87 43.738 40.362 16.554
+453 "C16" 43.273 69.681 61.261 4.9159 8.3065 7.2559
+454 "D16" 154.15 106.72 65.606 37.197 27.832 7.4910
+455 "E16" 149.19 148.83 193.98 36.691 35.876 51.695
+456 "F16" 157.80 111.38 47.573 38.836 30.036 4.3338
+457 "G16" 100.61 79.449 149.13 17.555 13.363 28.888
+458 "H16" 206.92 207.61 211.44 75.466 77.836 67.216
+459 "I16" 97.895 82.272 66.348 16.954 15.112 7.2992
+460 "J16" 172.80 178.46 82.595 47.072 52.200 10.669
+461 "K16" 183.90 191.17 190.75 55.962 60.759 50.287
+462 "L16" 75.048 76.021 151.02 10.165 9.7715 28.705
+463 "M16" 159.42 88.282 47.938 36.859 23.500 3.8297
+464 "N16" 133.24 176.09 150.50 29.735 43.076 29.229
+465 "O16" 59.749 48.251 40.198 7.3545 6.2503 2.9745
+466 "P16" 171.06 168.69 161.58 46.114 46.965 33.568
+467 "Q16" 53.260 54.841 37.700 6.2218 6.8107 2.7776
+468 "R16" 147.03 76.735 66.424 29.663 17.214 6.8286
+469 "S16" 63.206 78.809 107.93 8.0381 10.138 16.410
+470 "T16" 150.22 77.352 47.226 31.088 18.734 3.6263
+471 "U16" 97.824 159.49 134.64 18.911 32.215 23.899
+472 "V16" 133.39 81.567 151.23 26.145 16.883 27.363
+473 "W16" 155.32 82.024 48.477 33.705 20.761 3.7986
+474 "X16" 81.301 147.86 56.013 14.361 27.487 7.1744
+475 "Y16" 141.53 112.75 178.08 29.882 22.982 39.138
+476 "Z16" 31.556 30.196 30.075 2.4233 2.4412 1.9648
+477 "2A16" 166.04 149.78 129.15 42.519 39.188 21.270
+478 "2B16" 53.939 48.402 100.70 5.2349 4.4461 13.823
+479 "2C16" 152.96 83.435 68.500 32.492 19.465 7.0806
+480 "2D16" 141.97 181.00 131.63 33.454 46.863 22.481
+481 "A17" 93.002 152.57 112.99 18.355 31.722 19.048
+482 "B17" 48.562 44.595 81.595 4.8626 4.2833 10.526
+483 "C17" 155.24 137.12 108.13 39.631 36.092 16.781
+484 "D17" 37.723 54.820 48.270 3.8471 5.8319 4.8962
+485 "E17" 163.04 158.68 133.73 43.491 44.349 22.904
+486 "F17" 155.10 81.151 47.477 35.028 21.444 3.8305
+487 "G17" 119.76 108.26 175.73 23.826 20.302 40.464
+488 "H17" 155.83 188.69 108.40 44.211 56.679 16.435
+489 "I17" 54.125 49.417 43.252 6.5115 6.2807 3.6004
+490 "J17" 146.89 182.65 155.76 35.464 48.530 31.500
+491 "K17" 148.71 77.345 50.119 31.217 18.833 4.2081
+492 "L17" 53.925 41.854 39.291 6.1796 5.0138 3.0160
+493 "M17" 105.46 156.83 167.45 21.355 33.511 36.981
+494 "N17" 149.16 79.222 103.08 31.382 17.339 13.539
+495 "O17" 109.98 107.89 138.78 20.515 20.076 24.934
+496 "P17" 151.99 78.765 47.620 32.143 19.447 3.7592
+497 "Q17" 195.13 169.06 200.08 60.501 50.532 52.708
+498 "R17" 73.599 136.55 49.271 12.657 24.420 5.6873
+499 "S17" 37.757 33.718 34.938 3.4355 3.1639 2.5372
+500 "T17" 132.61 142.77 197.68 28.192 29.771 51.322
+501 "U17" 181.82 145.93 103.48 52.660 42.651 15.019
+502 "V17" 204.10 203.91 210.35 69.823 71.393 63.731
+503 "W17" 175.82 179.36 52.229 44.995 50.413 5.2421
+504 "X17" 155.69 156.50 143.88 37.401 38.967 26.702
+505 "Y17" 65.731 44.495 80.654 8.0339 5.5483 9.9052
+506 "Z17" 67.930 138.21 88.916 10.382 21.829 11.599
+507 "2A17" 156.40 87.866 68.881 34.325 21.131 7.2263
+508 "2B17" 68.870 68.990 41.254 9.1863 9.9839 3.1512
+509 "2C17" 209.60 202.66 207.60 75.534 73.388 61.922
+510 "2D17" 108.97 87.062 152.98 19.258 14.916 29.290
+511 "A18" 200.70 204.40 197.90 74.089 78.770 59.424
+512 "B18" 164.93 117.44 177.68 42.903 30.204 40.715
+513 "C18" 61.227 72.827 56.013 8.7363 10.837 6.0872
+514 "D18" 204.51 199.19 195.95 75.509 74.636 56.066
+515 "E18" 73.692 73.322 148.06 10.413 9.8011 28.727
+516 "F18" 68.313 128.24 45.392 11.836 22.861 5.4226
+517 "G18" 131.14 85.288 152.52 26.954 18.177 29.358
+518 "H18" 89.609 153.68 93.128 16.220 29.699 13.154
+519 "I18" 30.179 29.676 28.922 2.3647 2.3980 1.9090
+520 "J18" 208.65 210.69 209.48 77.786 81.283 65.463
+521 "K18" 88.863 70.346 63.498 14.363 11.898 6.7644
+522 "L18" 197.92 177.16 54.602 60.378 57.128 5.2726
+523 "M18" 143.55 91.470 162.00 30.554 20.123 31.743
+524 "N18" 121.99 131.52 122.44 24.748 27.645 20.451
+525 "O18" 172.04 115.00 91.328 44.929 31.003 12.389
+526 "P18" 81.187 74.923 148.64 11.881 10.425 27.990
+527 "Q18" 117.91 169.48 101.12 25.737 39.690 14.823
+528 "R18" 183.93 145.62 52.297 52.300 42.856 4.6545
+529 "S18" 55.452 105.38 65.438 7.2126 14.429 7.1944
+530 "T18" 211.11 208.92 210.90 77.679 78.425 65.193
+531 "U18" 177.41 124.39 134.00 47.084 32.209 22.319
+532 "V18" 53.506 90.891 78.889 6.6683 11.848 10.189
+533 "W18" 197.65 205.13 206.81 65.207 70.970 60.729
+534 "X18" 51.396 86.860 43.674 6.2806 11.330 4.1095
+535 "Y18" 193.58 175.18 113.92 60.571 56.044 16.070
+536 "Z18" 96.313 103.94 178.60 15.673 16.045 40.153
+537 "2A18" 168.53 106.08 128.73 41.178 25.901 19.845
+538 "2B18" 85.496 104.15 136.89 13.693 16.674 24.407
+539 "2C18" 208.09 210.26 179.31 74.223 78.952 41.733
+540 "2D18" 161.94 92.492 50.116 38.135 24.817 4.1395
+541 "A19" 156.71 112.54 101.12 39.239 29.094 15.479
+542 "B19" 42.513 58.501 40.536 4.7416 7.0454 3.4644
+543 "C19" 172.17 152.13 143.40 49.395 43.657 28.522
+544 "D19" 159.73 183.13 54.703 42.332 52.372 5.7052
+545 "E19" 119.21 105.48 95.518 24.242 22.424 14.026
+546 "F19" 30.235 29.764 29.269 2.4551 2.5226 2.0256
+547 "G19" 88.689 134.99 136.69 16.677 25.667 25.825
+548 "H19" 86.392 53.901 45.566 13.124 8.8453 3.5763
+549 "I19" 126.88 158.15 160.78 28.527 37.076 34.358
+550 "J19" 120.42 79.125 64.241 22.891 16.490 6.9749
+551 "K19" 108.34 160.80 164.56 22.666 35.279 35.631
+552 "L19" 49.327 39.952 38.592 5.4212 4.5484 2.9627
+553 "M19" 67.917 128.27 44.976 11.199 21.925 5.4425
+554 "N19" 127.84 62.080 45.945 22.708 12.897 3.6335
+555 "O19" 74.380 138.45 130.11 12.507 23.738 22.498
+556 "P19" 30.060 29.337 28.817 2.3442 2.3896 1.9267
+557 "Q19" 147.97 73.539 46.480 30.303 17.749 3.5777
+558 "R19" 75.153 76.608 151.15 10.104 9.7374 28.448
+559 "S19" 78.339 144.41 52.936 13.901 26.685 6.3873
+560 "T19" 31.312 30.556 30.767 2.4391 2.4780 2.0610
+561 "U19" 169.32 177.98 203.35 45.972 49.473 57.247
+562 "V19" 44.471 72.376 45.634 4.8275 8.3485 3.9886
+563 "W19" 199.02 205.39 55.979 63.154 70.855 7.5811
+564 "X19" 90.125 81.954 73.039 14.863 14.166 8.5770
+565 "Y19" 207.33 211.44 212.22 74.340 78.986 66.286
+566 "Z19" 182.95 126.85 175.69 49.996 33.344 36.894
+567 "2A19" 77.857 90.598 163.13 10.165 11.287 33.081
+568 "2B19" 202.16 204.23 49.052 62.642 69.515 6.2139
+569 "2C19" 162.94 96.440 68.740 38.429 24.741 7.3479
+570 "2D19" 83.952 84.116 160.97 12.944 12.283 32.608
+END_DATA
diff --git a/scanin/CMP_Digital_Target-3.ti2 b/scanin/CMP_Digital_Target-3.ti2
new file mode 100644
index 0000000..e4b15e9
--- /dev/null
+++ b/scanin/CMP_Digital_Target-3.ti2
@@ -0,0 +1,601 @@
+CTI2
+
+DESCRIPTOR "Argyll Calibration Target chart information 2 for Christopher Metarie Digital Target 3"
+ORIGINATOR "Argyll printtarg"
+CREATED "Wed Sep 12 00:02:26 2007"
+KEYWORD "TARGET_INSTRUMENT"
+TARGET_INSTRUMENT "GretagMacbeth i1 Pro"
+KEYWORD "ACCURATE_EXPECTED_VALUES"
+ACCURATE_EXPECTED_VALUES "true"
+KEYWORD "COLOR_REP"
+COLOR_REP "RGB"
+KEYWORD "STEPS_IN_PASS"
+STEPS_IN_PASS "30"
+KEYWORD "PASSES_IN_STRIPS2"
+PASSES_IN_STRIPS2 "19"
+KEYWORD "STRIP_INDEX_PATTERN"
+STRIP_INDEX_PATTERN "0-9,@-9,@-9;1-999"
+KEYWORD "PATCH_INDEX_PATTERN"
+PATCH_INDEX_PATTERN "A-Z, 2-9"
+KEYWORD "INDEX_ORDER"
+INDEX_ORDER "PATCH_THEN_STRIP"
+
+KEYWORD "SAMPLE_LOC"
+NUMBER_OF_FIELDS 8
+BEGIN_DATA_FORMAT
+SAMPLE_ID SAMPLE_LOC RGB_R RGB_G RGB_B XYZ_X XYZ_Y XYZ_Z
+END_DATA_FORMAT
+
+NUMBER_OF_SETS 570
+BEGIN_DATA
+1 "A1" 106.60 73.896 140.50 19.991 14.120 27.886
+2 "B1" 101.22 157.76 91.298 19.926 34.054 14.005
+3 "C1" 56.474 54.712 116.20 6.2319 5.6339 19.078
+4 "D1" 142.23 143.96 106.67 32.785 35.198 16.672
+5 "E1" 161.58 92.303 143.82 38.996 22.658 25.805
+6 "F1" 90.405 96.497 94.153 16.011 17.612 14.561
+7 "G1" 163.39 98.124 88.178 39.496 24.997 12.003
+8 "H1" 63.278 55.370 115.21 7.4905 6.2468 18.358
+9 "I1" 71.385 133.64 49.363 11.987 23.286 6.5928
+10 "J1" 97.757 75.383 143.28 16.591 12.444 27.331
+11 "K1" 194.49 193.52 200.39 63.038 64.179 58.403
+12 "L1" 43.729 37.037 36.612 4.3513 3.8393 2.7750
+13 "M1" 128.36 143.53 178.51 27.386 30.873 42.527
+14 "N1" 136.71 176.12 173.90 31.789 44.281 40.439
+15 "O1" 190.25 190.03 173.26 59.453 61.985 40.335
+16 "P1" 139.89 69.074 45.054 26.783 15.713 3.5833
+17 "Q1" 34.433 40.188 37.667 3.0238 3.6843 3.0876
+18 "R1" 87.362 153.04 89.833 15.462 28.885 12.776
+19 "S1" 124.79 84.048 151.84 23.694 16.421 29.352
+20 "T1" 201.42 206.76 79.283 65.900 73.381 11.377
+21 "U1" 59.875 104.70 93.370 8.3333 15.036 13.749
+22 "V1" 205.39 208.60 159.63 72.416 77.985 33.423
+23 "W1" 91.572 136.12 167.03 16.393 25.254 38.122
+24 "X1" 30.336 29.306 28.957 2.3144 2.3581 1.9303
+25 "Y1" 132.60 110.17 107.91 27.102 23.276 16.813
+26 "Z1" 32.956 33.215 31.185 2.6994 2.8850 2.2332
+27 "2A1" 70.282 129.32 46.946 10.757 20.588 4.8216
+28 "2B1" 159.00 173.65 202.91 40.765 45.919 58.857
+29 "2C1" 121.19 88.150 161.84 22.864 16.381 33.839
+30 "2D1" 136.59 101.50 125.83 28.289 21.404 20.483
+31 "A2" 75.414 80.525 150.43 10.412 10.567 30.978
+32 "B2" 128.64 63.027 42.872 24.139 14.085 3.5857
+33 "C2" 101.62 116.40 166.26 19.473 21.752 38.376
+34 "D2" 129.62 78.772 146.55 26.280 16.991 28.035
+35 "E2" 164.20 144.75 82.589 42.573 39.671 9.6556
+36 "F2" 96.904 110.83 179.21 17.113 18.705 43.690
+37 "G2" 157.24 84.071 46.359 35.850 22.548 3.8554
+38 "H2" 84.439 70.424 140.26 13.037 10.392 26.425
+39 "I2" 81.661 147.44 58.864 14.946 28.262 8.1825
+40 "J2" 35.433 35.856 32.072 3.1780 3.3914 2.3047
+41 "K2" 150.66 144.08 140.58 35.880 35.178 26.683
+42 "L2" 158.35 93.379 154.74 36.434 22.147 28.386
+43 "M2" 100.38 93.492 99.897 17.689 17.022 15.072
+44 "N2" 158.93 93.968 95.921 36.396 23.022 13.623
+45 "O2" 102.60 137.21 171.09 19.884 26.988 39.544
+46 "P2" 74.932 50.703 44.902 10.294 7.4271 3.4839
+47 "Q2" 115.12 165.78 166.70 24.415 37.008 36.288
+48 "R2" 157.97 92.663 150.58 36.078 21.682 26.898
+49 "S2" 83.913 88.787 161.27 12.232 12.247 33.412
+50 "T2" 72.772 133.20 48.154 12.486 23.920 5.4357
+51 "U2" 155.64 177.45 175.23 38.913 47.091 40.794
+52 "V2" 65.311 50.797 44.712 8.3779 6.8387 3.7119
+53 "W2" 30.304 29.389 28.630 2.3401 2.3804 1.9157
+54 "X2" 98.383 112.57 76.501 17.501 20.678 9.7225
+55 "Y2" 196.95 176.56 81.174 60.324 56.490 9.7029
+56 "Z2" 106.87 76.461 64.020 18.340 14.257 6.9452
+57 "2A2" 149.29 180.57 55.756 39.051 50.700 6.0515
+58 "2B2" 46.850 72.093 42.887 5.3134 8.7205 3.7883
+59 "2C2" 126.21 86.097 104.74 24.040 16.842 14.641
+60 "2D2" 68.443 138.05 59.668 10.963 22.720 8.1563
+61 "A3" 40.703 34.668 43.170 3.2215 2.8069 3.1344
+62 "B3" 140.80 177.61 130.47 35.703 49.122 24.276
+63 "C3" 71.422 72.467 144.63 9.6710 9.2674 28.141
+64 "D3" 157.83 159.05 109.87 39.368 42.329 16.693
+65 "E3" 74.511 78.412 148.91 9.8448 9.8037 29.501
+66 "F3" 172.35 192.98 167.60 50.040 60.997 38.371
+67 "G3" 72.505 72.322 146.66 9.8731 9.2375 28.216
+68 "H3" 132.85 167.63 50.020 31.926 43.409 5.2962
+69 "I3" 99.822 132.27 193.48 18.889 24.530 52.235
+70 "J3" 158.78 88.817 87.144 36.271 22.167 11.952
+71 "K3" 146.32 156.61 192.38 34.281 37.060 50.573
+72 "L3" 30.978 30.641 29.826 2.4511 2.5353 2.0398
+73 "M3" 146.27 153.30 150.57 34.120 37.136 30.510
+74 "N3" 144.16 70.919 46.534 28.309 16.461 3.7229
+75 "O3" 85.091 72.947 144.27 12.851 10.508 27.052
+76 "P3" 122.91 131.12 67.881 24.532 27.910 7.4170
+77 "Q3" 140.27 116.77 178.48 29.800 23.719 40.761
+78 "R3" 73.020 103.31 46.082 11.420 17.249 4.2060
+79 "S3" 148.58 74.828 46.145 30.098 17.923 3.6411
+80 "T3" 92.676 106.61 179.17 15.102 16.455 41.548
+81 "U3" 89.067 149.36 56.522 16.816 29.880 6.2892
+82 "V3" 104.27 103.98 177.68 19.054 17.886 40.635
+83 "W3" 122.27 64.970 44.965 21.160 13.146 3.6418
+84 "X3" 190.80 157.08 167.65 56.827 44.647 36.033
+85 "Y3" 55.033 94.759 44.077 7.3224 13.350 4.3645
+86 "Z3" 157.08 85.576 87.558 34.447 20.515 11.595
+87 "2A3" 79.250 88.578 159.52 10.795 11.448 32.464
+88 "2B3" 108.15 127.71 55.172 20.617 25.691 5.5795
+89 "2C3" 78.102 75.847 150.05 11.126 10.174 28.914
+90 "2D3" 202.83 194.20 199.16 68.724 66.102 56.410
+91 "A4" 105.09 86.729 157.14 19.671 15.554 33.919
+92 "B4" 99.696 102.26 43.923 17.501 19.753 3.8498
+93 "C4" 107.26 130.81 190.72 21.509 25.734 51.674
+94 "D4" 178.30 141.51 75.719 51.958 42.451 9.1213
+95 "E4" 86.201 111.97 176.39 14.112 17.806 42.697
+96 "F4" 192.41 193.92 143.50 61.487 65.884 27.427
+97 "G4" 161.20 95.317 155.94 38.420 23.239 29.457
+98 "H4" 74.863 77.945 149.59 9.8759 9.7311 29.126
+99 "I4" 207.61 208.23 184.16 75.793 79.718 46.695
+100 "J4" 70.931 101.90 131.71 10.595 15.480 24.653
+101 "K4" 147.01 94.545 162.80 32.187 21.331 32.814
+102 "L4" 81.586 103.83 104.84 13.777 17.753 16.663
+103 "M4" 141.06 69.848 45.247 27.339 16.027 3.5603
+104 "N4" 90.494 151.07 150.65 16.922 28.995 29.336
+105 "O4" 55.294 41.016 63.313 6.3066 4.7610 6.9921
+106 "P4" 111.84 140.47 103.22 22.390 29.210 15.592
+107 "Q4" 144.45 93.249 162.31 30.657 20.369 32.268
+108 "R4" 105.31 104.82 101.31 19.152 19.807 15.592
+109 "S4" 79.325 137.68 48.973 13.948 25.671 5.2122
+110 "T4" 84.189 150.82 120.01 14.991 27.898 19.745
+111 "U4" 81.413 52.379 93.432 11.486 7.6381 12.484
+112 "V4" 76.459 77.531 42.048 10.749 11.899 3.2945
+113 "W4" 211.28 212.44 213.00 77.699 80.782 67.995
+114 "X4" 125.79 102.06 67.469 24.430 21.610 7.1800
+115 "Y4" 36.736 36.340 46.306 3.2104 3.1999 4.2513
+116 "Z4" 83.114 150.29 61.891 14.450 27.763 8.5197
+117 "2A4" 117.71 90.574 160.37 21.679 16.235 32.574
+118 "2B4" 89.463 153.87 72.494 15.737 29.267 10.078
+119 "2C4" 42.498 47.832 65.570 3.9226 4.4418 7.5674
+120 "2D4" 115.91 156.34 101.73 24.022 34.375 15.022
+121 "A5" 100.43 57.576 43.875 16.920 10.945 3.6233
+122 "B5" 139.57 140.52 191.37 32.876 32.373 51.547
+123 "C5" 106.24 157.82 63.346 23.454 37.144 7.5202
+124 "D5" 120.63 119.68 128.56 24.762 25.266 23.702
+125 "E5" 144.32 70.530 44.643 29.192 16.958 3.5359
+126 "F5" 112.12 164.88 139.23 23.640 37.186 26.447
+127 "G5" 103.37 136.87 47.413 20.387 28.842 4.7028
+128 "H5" 36.251 41.780 35.395 3.3636 4.0809 2.6833
+129 "I5" 111.24 162.80 164.26 23.272 35.820 35.198
+130 "J5" 143.56 87.562 156.55 30.642 19.573 30.103
+131 "K5" 109.54 163.06 164.19 22.753 35.416 35.064
+132 "L5" 79.627 52.133 45.354 11.209 7.8786 3.5144
+133 "M5" 92.456 152.33 153.19 17.513 29.721 30.234
+134 "N5" 34.714 33.013 32.281 2.9935 2.9559 2.2837
+135 "O5" 166.35 175.94 163.99 44.139 49.035 35.423
+136 "P5" 89.669 79.549 47.144 14.200 13.959 3.6761
+137 "Q5" 191.24 182.85 188.48 59.021 56.831 48.262
+138 "R5" 51.675 70.899 41.770 6.3401 9.2262 3.4989
+139 "S5" 170.53 177.54 173.76 45.700 49.818 40.124
+140 "T5" 63.890 93.942 45.687 9.3142 14.437 4.0636
+141 "U5" 30.168 29.299 28.940 2.3535 2.3975 1.9290
+142 "V5" 133.08 133.14 122.05 27.766 28.925 20.091
+143 "W5" 36.054 32.923 33.276 3.0887 2.9300 2.3596
+144 "X5" 61.314 111.77 45.755 8.7239 16.803 5.0276
+145 "Y5" 203.39 209.40 208.06 70.161 75.882 63.247
+146 "Z5" 76.353 77.670 109.77 11.191 11.244 16.654
+147 "2A5" 193.86 202.18 186.73 62.394 68.771 47.096
+148 "2B5" 84.630 73.965 145.61 12.640 10.459 27.333
+149 "2C5" 68.392 129.05 45.336 11.160 21.761 5.5099
+150 "2D5" 210.46 210.49 195.89 77.413 80.592 53.936
+151 "A6" 156.79 90.614 148.03 37.703 22.453 27.499
+152 "B6" 88.481 111.82 178.46 14.943 18.301 44.058
+153 "C6" 167.90 108.82 66.983 44.282 30.956 7.9185
+154 "D6" 66.578 110.85 54.360 10.440 18.144 6.5076
+155 "E6" 148.31 87.735 154.80 32.892 20.486 29.274
+156 "F6" 93.611 155.13 115.73 17.819 31.263 19.184
+157 "G6" 40.190 39.280 58.763 3.7468 3.6058 6.3752
+158 "H6" 170.89 152.59 147.19 45.907 41.132 28.818
+159 "I6" 46.469 44.545 75.326 4.4472 4.1158 9.1204
+160 "J6" 102.48 151.18 180.03 20.388 31.549 44.358
+161 "K6" 30.872 30.164 29.516 2.3970 2.4515 1.9961
+162 "L6" 83.294 137.00 98.315 14.323 24.343 14.536
+163 "M6" 30.722 30.150 29.446 2.3623 2.4293 1.9541
+164 "N6" 163.74 151.67 194.83 41.190 36.767 50.714
+165 "O6" 199.83 203.65 49.970 62.357 69.847 6.3069
+166 "P6" 77.512 86.873 159.07 10.124 10.811 31.915
+167 "Q6" 200.99 194.31 190.45 67.127 65.762 49.352
+168 "R6" 165.39 100.99 154.68 39.128 23.959 27.822
+169 "S6" 69.496 98.026 61.986 10.413 15.222 6.7952
+170 "T6" 167.68 124.93 146.98 41.195 30.074 26.292
+171 "U6" 62.706 68.483 60.211 8.4099 9.5591 6.5834
+172 "V6" 174.63 108.06 166.14 43.942 26.976 32.439
+173 "W6" 138.60 174.02 106.27 33.397 44.263 15.513
+174 "X6" 192.12 161.59 197.05 56.629 45.672 50.137
+175 "Y6" 71.207 70.503 63.173 10.198 10.583 6.9283
+176 "Z6" 171.42 121.85 182.26 42.782 29.822 40.386
+177 "2A6" 193.77 197.98 207.14 61.567 65.013 61.274
+178 "2B6" 117.80 82.965 151.94 21.335 15.115 28.916
+179 "2C6" 36.305 39.517 34.462 3.2203 3.7212 2.6005
+180 "2D6" 152.48 145.44 148.89 35.799 34.854 29.422
+181 "A7" 93.637 153.35 73.710 19.123 33.078 10.667
+182 "B7" 83.277 73.755 144.52 12.915 10.831 28.225
+183 "C7" 206.85 206.70 197.33 77.518 80.587 57.622
+184 "D7" 77.454 73.349 146.72 11.348 10.154 28.703
+185 "E7" 70.809 132.64 47.023 12.442 24.254 5.6475
+186 "F7" 91.049 88.345 163.10 15.345 13.969 34.910
+187 "G7" 177.87 190.87 53.018 52.703 61.988 5.9513
+188 "H7" 75.960 87.720 159.98 9.9056 10.888 32.881
+189 "I7" 199.48 192.77 181.55 67.471 66.483 44.705
+190 "J7" 64.713 68.090 67.610 8.8426 9.6268 8.1730
+191 "K7" 193.08 175.48 139.54 61.925 56.855 24.968
+192 "L7" 75.430 87.876 159.79 9.6349 10.726 32.427
+193 "M7" 133.24 173.17 56.742 32.435 45.157 6.1480
+194 "N7" 148.49 75.098 46.216 30.427 18.146 3.6270
+195 "O7" 97.134 97.177 86.559 16.928 17.695 11.911
+196 "P7" 191.20 160.30 54.685 56.094 48.894 4.9467
+197 "Q7" 80.068 80.251 125.32 11.934 11.778 20.573
+198 "R7" 83.368 147.19 55.358 15.285 28.434 6.3346
+199 "S7" 37.747 44.719 36.907 3.5054 4.3615 2.7830
+200 "T7" 146.52 175.26 205.50 35.099 44.589 58.771
+201 "U7" 133.97 82.941 152.98 26.155 17.005 28.079
+202 "V7" 63.182 103.22 97.125 8.9086 14.924 14.071
+203 "W7" 144.22 147.07 76.384 30.611 33.536 8.6858
+204 "X7" 212.78 213.19 211.89 79.113 81.784 66.503
+205 "Y7" 77.896 86.298 158.37 10.240 10.764 31.229
+206 "Z7" 71.313 133.52 47.389 11.976 23.218 5.4228
+207 "2A7" 75.926 78.650 151.98 9.9976 9.7574 28.929
+208 "2B7" 118.08 168.98 98.781 25.572 39.297 14.262
+209 "2C7" 207.34 198.34 43.623 66.257 69.908 5.8960
+210 "2D7" 92.639 108.26 179.66 14.893 16.603 41.626
+211 "A8" 69.904 137.36 58.325 12.100 24.359 8.4071
+212 "B8" 167.09 144.55 49.599 42.436 39.900 4.7260
+213 "C8" 111.10 78.672 121.83 20.574 15.216 20.136
+214 "D8" 74.340 141.34 56.829 12.701 25.255 8.1803
+215 "E8" 104.13 74.055 141.65 18.324 13.039 26.409
+216 "F8" 90.075 154.61 90.911 16.724 30.035 13.212
+217 "G8" 36.341 33.896 36.868 3.3200 3.1831 3.0291
+218 "H8" 101.65 159.66 134.07 20.304 33.570 24.443
+219 "I8" 34.216 33.499 37.712 2.9169 2.9009 3.0168
+220 "J8" 174.41 165.56 173.38 48.038 46.099 39.891
+221 "K8" 46.252 66.300 42.397 5.2241 8.0427 3.5985
+222 "L8" 152.69 184.12 182.69 38.619 50.368 44.407
+223 "M8" 30.240 29.863 28.939 2.3428 2.3959 1.9273
+224 "N8" 35.593 35.467 34.190 3.1956 3.3116 2.6415
+225 "O8" 71.728 71.553 70.937 10.494 10.847 8.8081
+226 "P8" 115.92 116.10 114.68 22.233 23.005 18.548
+227 "Q8" 159.85 160.38 159.13 39.317 40.778 33.056
+228 "R8" 196.60 197.21 197.12 63.147 65.480 53.862
+229 "S8" 137.57 112.53 140.33 28.252 23.514 24.400
+230 "T8" 80.940 142.13 50.952 14.520 26.909 5.4756
+231 "U8" 99.365 133.93 196.43 18.057 24.054 51.817
+232 "V8" 30.957 30.566 29.813 2.3402 2.3911 1.9209
+233 "W8" 212.09 210.69 210.55 77.838 79.255 64.883
+234 "X8" 90.920 71.799 46.751 14.232 12.342 3.5677
+235 "Y8" 137.72 177.30 175.40 31.451 43.594 39.472
+236 "Z8" 39.524 49.911 38.685 3.8429 5.0881 3.0661
+237 "2A8" 130.39 176.13 127.05 27.844 41.491 21.140
+238 "2B8" 31.904 31.437 30.690 2.4921 2.5648 2.0613
+239 "2C8" 149.77 158.18 196.00 34.635 36.777 50.964
+240 "2D8" 30.719 29.824 29.600 2.3436 2.3905 1.9440
+241 "A9" 160.64 188.72 135.75 46.811 59.109 25.406
+242 "B9" 151.09 113.48 161.82 35.695 26.905 33.928
+243 "C9" 56.378 101.12 44.028 8.0578 15.313 4.8058
+244 "D9" 200.86 192.61 183.06 70.562 68.540 46.575
+245 "E9" 77.639 89.149 161.20 10.633 11.656 33.787
+246 "F9" 183.43 148.52 132.23 54.113 43.360 23.933
+247 "G9" 53.868 83.869 77.703 7.1200 11.358 10.328
+248 "H9" 144.68 93.693 163.06 31.237 20.786 32.756
+249 "I9" 80.625 147.05 117.46 14.469 27.174 19.553
+250 "J9" 82.612 53.093 45.342 11.813 8.1727 3.4750
+251 "K9" 179.05 178.11 205.03 51.613 51.265 59.093
+252 "L9" 68.256 128.21 45.397 11.232 21.889 5.3482
+253 "M9" 30.122 29.559 28.786 2.3389 2.3932 1.9205
+254 "N9" 44.132 44.030 43.113 4.6543 4.8363 3.9004
+255 "O9" 77.603 77.496 77.212 11.973 12.444 10.082
+256 "P9" 123.38 123.35 123.35 24.548 25.410 20.720
+257 "Q9" 167.61 168.12 167.11 43.270 44.842 36.215
+258 "R9" 202.61 204.17 205.53 68.113 71.034 59.864
+259 "S9" 152.34 81.269 59.657 31.468 18.903 5.4734
+260 "T9" 36.525 39.233 37.120 3.2297 3.6435 2.9170
+261 "U9" 147.79 112.59 105.57 31.779 25.042 15.321
+262 "V9" 128.85 175.42 85.449 30.098 43.673 10.856
+263 "W9" 202.12 182.11 203.04 65.360 57.273 55.175
+264 "X9" 58.841 111.22 46.014 7.8261 15.682 5.0934
+265 "Y9" 150.27 74.574 46.780 30.151 17.603 3.5424
+266 "Z9" 91.373 72.814 142.15 14.169 10.872 25.545
+267 "2A9" 178.42 178.87 165.68 49.740 51.961 35.295
+268 "2B9" 110.21 82.066 150.83 19.293 14.167 28.533
+269 "2C9" 115.57 141.00 82.227 23.305 29.469 10.465
+270 "2D9" 120.44 87.308 156.62 22.420 16.254 30.792
+271 "A10" 54.278 98.104 59.100 7.4766 14.316 6.7672
+272 "B10" 141.49 142.92 50.130 30.693 34.185 4.8132
+273 "C10" 69.545 135.98 93.417 11.568 23.235 13.773
+274 "D10" 30.772 30.009 29.471 2.4307 2.4999 2.0171
+275 "E10" 140.52 89.479 159.03 30.140 19.949 31.751
+276 "F10" 88.149 135.62 165.23 15.810 25.353 37.521
+277 "G10" 30.508 30.075 29.389 2.4272 2.4936 1.9887
+278 "H10" 75.794 144.13 86.030 12.549 25.096 11.562
+279 "I10" 29.881 29.286 28.915 2.3402 2.3788 1.9223
+280 "J10" 78.427 139.08 135.09 13.387 24.235 23.958
+281 "K10" 127.69 103.24 52.122 25.478 22.853 4.3681
+282 "L10" 47.518 38.484 37.887 5.0366 4.2550 2.8902
+283 "M10" 30.202 29.466 28.978 2.3374 2.3915 1.9194
+284 "N10" 50.671 50.438 49.901 5.8942 6.1054 4.9143
+285 "O10" 84.087 84.218 84.130 13.462 14.013 11.428
+286 "P10" 130.92 131.27 131.19 27.055 28.014 23.007
+287 "Q10" 174.24 174.94 173.31 47.406 49.169 39.386
+288 "R10" 208.46 209.75 212.17 73.974 77.043 65.729
+289 "S10" 61.440 61.759 68.112 7.8358 8.0794 7.9176
+290 "T10" 189.59 154.63 195.61 54.395 42.563 48.165
+291 "U10" 70.065 139.76 58.155 11.113 22.841 7.7084
+292 "V10" 34.382 38.877 37.374 2.9394 3.4387 2.9247
+293 "W10" 81.185 148.91 88.683 13.284 25.973 11.867
+294 "X10" 31.337 30.706 30.647 2.4768 2.5120 2.1182
+295 "Y10" 132.10 124.54 122.36 27.102 26.481 19.829
+296 "Z10" 128.86 77.259 44.848 23.978 16.698 3.6586
+297 "2A10" 121.07 144.71 145.24 24.659 30.178 27.108
+298 "2B10" 91.404 54.498 76.524 13.618 8.1445 8.4106
+299 "2C10" 122.45 129.19 129.52 24.412 26.496 22.533
+300 "2D10" 52.091 43.712 85.349 5.2897 4.2252 10.803
+301 "A11" 95.121 56.945 109.13 15.662 9.9371 16.757
+302 "B11" 49.247 84.978 41.595 6.3528 11.756 4.1276
+303 "C11" 201.38 188.28 201.57 70.247 65.054 58.834
+304 "D11" 165.99 106.71 48.883 42.904 30.283 4.3741
+305 "E11" 142.45 151.87 140.77 33.675 37.292 27.259
+306 "F11" 59.895 43.373 70.686 7.3260 5.3398 8.4321
+307 "G11" 185.60 179.80 175.52 56.748 56.026 41.441
+308 "H11" 62.810 62.001 39.464 8.1585 8.7147 3.0001
+309 "I11" 182.38 181.99 177.01 54.512 56.195 42.466
+310 "J11" 44.613 62.579 56.523 4.9736 7.2275 6.1574
+311 "K11" 177.26 171.26 154.02 50.413 50.352 29.675
+312 "L11" 81.706 58.921 119.57 11.783 8.3671 18.993
+313 "M11" 30.648 29.769 29.464 2.4004 2.4632 1.9865
+314 "N11" 56.534 56.324 55.689 7.0502 7.2979 5.9348
+315 "O11" 89.911 89.936 88.980 15.157 15.663 12.643
+316 "P11" 140.77 141.41 140.88 30.809 31.935 26.037
+317 "Q11" 179.84 180.21 178.58 51.029 52.827 42.179
+318 "R11" 212.60 213.84 215.53 78.892 82.077 69.152
+319 "S11" 66.493 66.184 65.558 9.0391 9.3614 7.5795
+320 "T11" 187.32 160.61 146.99 54.688 46.067 27.270
+321 "U11" 59.842 44.929 41.517 7.0672 5.5203 3.1542
+322 "V11" 100.71 151.70 177.40 19.018 30.251 40.808
+323 "W11" 32.433 32.983 31.555 2.6438 2.7813 2.1741
+324 "X11" 103.41 161.56 150.03 20.294 33.254 28.954
+325 "Y11" 49.930 46.756 84.028 4.7917 4.3367 10.461
+326 "Z11" 157.24 187.57 185.20 39.678 51.335 44.730
+327 "2A11" 69.840 130.51 46.639 11.397 22.075 5.4606
+328 "2B11" 164.73 190.77 189.92 43.396 54.386 47.692
+329 "2C11" 67.660 57.565 50.639 9.0264 8.0855 4.5632
+330 "2D11" 146.88 73.255 46.894 29.263 17.239 3.6542
+331 "A12" 149.18 183.06 84.464 42.435 55.238 11.960
+332 "B12" 72.446 71.794 146.17 10.244 9.5241 28.550
+333 "C12" 111.58 163.80 82.344 25.557 39.490 11.397
+334 "D12" 67.115 62.023 67.669 9.5983 9.1448 8.3283
+335 "E12" 202.25 206.59 144.16 71.285 77.444 27.463
+336 "F12" 142.52 142.52 149.46 32.592 33.399 30.058
+337 "G12" 52.218 46.843 96.005 5.1430 4.3541 13.182
+338 "H12" 95.301 157.62 128.66 18.517 32.087 22.562
+339 "I12" 58.804 42.199 45.333 6.9977 5.1440 3.8014
+340 "J12" 201.67 207.01 112.57 69.246 76.042 17.844
+341 "K12" 76.878 56.433 113.86 10.642 7.6407 17.593
+342 "L12" 110.43 164.87 121.89 21.927 35.477 19.681
+343 "M12" 31.073 30.502 29.828 2.4287 2.4921 1.9949
+344 "N12" 60.863 60.999 60.221 7.9937 8.3172 6.7360
+345 "O12" 97.436 97.414 96.425 17.120 17.761 14.285
+346 "P12" 146.96 147.76 147.68 33.242 34.486 28.221
+347 "Q12" 186.04 186.49 184.52 55.136 57.000 45.303
+348 "R12" 217.03 217.69 217.80 84.047 87.070 71.496
+349 "S12" 173.81 166.67 164.48 46.799 45.618 34.284
+350 "T12" 61.998 59.133 126.43 6.6921 6.0185 19.773
+351 "U12" 133.08 178.53 107.01 31.583 45.365 15.646
+352 "V12" 159.59 92.307 67.569 35.975 22.808 7.0470
+353 "W12" 92.823 93.091 102.67 15.632 16.004 15.333
+354 "X12" 153.31 79.910 48.385 32.188 19.569 3.7849
+355 "Y12" 76.169 77.765 152.48 9.9981 9.6287 28.171
+356 "Z12" 80.867 145.82 55.209 14.429 27.348 6.4901
+357 "2A12" 88.726 84.091 155.13 13.822 12.446 29.936
+358 "2B12" 201.37 208.14 195.53 68.261 74.431 52.067
+359 "2C12" 32.004 31.705 30.813 2.4685 2.5621 2.0287
+360 "2D12" 135.53 145.08 187.78 28.798 30.974 45.549
+361 "A13" 86.415 69.301 138.07 14.129 10.884 26.286
+362 "B13" 104.58 136.42 46.559 21.280 29.662 4.6515
+363 "C13" 147.82 79.114 100.32 32.064 17.966 13.604
+364 "D13" 75.334 85.157 157.26 9.9611 10.733 32.277
+365 "E13" 178.81 179.96 148.24 51.845 54.927 28.181
+366 "F13" 55.482 72.327 100.16 6.3441 8.5421 15.068
+367 "G13" 200.09 199.75 184.43 68.699 71.452 46.387
+368 "H13" 76.688 84.710 151.41 10.374 10.902 29.169
+369 "I13" 129.73 107.35 52.022 26.203 23.997 4.3858
+370 "J13" 119.03 160.66 198.42 25.753 36.554 54.373
+371 "K13" 56.473 50.032 106.16 5.9068 4.9157 15.287
+372 "L13" 153.18 106.56 46.936 35.348 27.270 4.1933
+373 "M13" 32.264 31.978 31.289 2.6154 2.7053 2.1626
+374 "N13" 66.470 66.520 66.246 9.3637 9.6843 7.8391
+375 "O13" 105.28 105.86 104.76 19.359 20.106 16.147
+376 "P13" 153.75 154.63 154.12 36.624 37.957 30.671
+377 "Q13" 192.03 192.25 191.27 59.859 61.861 49.702
+378 "R13" 221.68 221.89 221.96 89.957 93.025 75.604
+379 "S13" 104.20 75.856 145.33 17.613 12.665 26.369
+380 "T13" 121.87 124.18 72.370 22.969 25.114 7.7873
+381 "U13" 91.392 55.886 72.021 13.623 8.3610 7.6135
+382 "V13" 193.01 176.53 161.70 59.661 54.433 33.140
+383 "W13" 97.047 61.496 44.638 15.045 10.609 3.4373
+384 "X13" 124.18 171.27 172.48 26.676 39.190 37.659
+385 "Y13" 100.95 103.55 45.932 16.770 18.831 3.7976
+386 "Z13" 98.719 76.722 108.03 16.243 13.004 15.620
+387 "2A13" 193.99 204.68 205.71 62.079 69.145 59.254
+388 "2B13" 77.619 94.368 167.50 9.8300 11.508 34.315
+389 "2C13" 91.512 53.191 50.701 13.378 8.4024 4.1065
+390 "2D13" 76.609 104.98 135.45 11.706 16.228 24.708
+391 "A14" 129.84 79.131 147.61 27.260 17.709 28.446
+392 "B14" 74.731 86.884 158.53 10.007 11.116 33.309
+393 "C14" 178.45 194.65 198.02 55.170 63.826 56.969
+394 "D14" 122.80 65.789 71.805 22.474 12.791 7.8220
+395 "E14" 99.529 101.15 66.691 17.833 19.340 7.6068
+396 "F14" 133.73 68.160 43.803 25.508 15.387 3.5019
+397 "G14" 130.31 174.28 137.87 28.674 42.244 25.294
+398 "H14" 110.49 77.413 145.63 20.013 14.167 27.201
+399 "I14" 30.999 29.764 29.587 2.4079 2.4331 1.9867
+400 "J14" 120.14 114.13 178.99 23.527 21.066 41.374
+401 "K14" 128.29 64.544 74.117 23.203 12.509 8.0121
+402 "L14" 176.70 157.34 183.40 48.486 42.511 43.112
+403 "M14" 59.777 105.00 45.141 8.6031 15.926 4.6200
+404 "N14" 182.00 181.11 190.30 53.107 53.884 48.977
+405 "O14" 42.651 40.749 62.042 3.9836 3.7404 6.5852
+406 "P14" 151.32 150.31 182.91 35.517 35.068 43.001
+407 "Q14" 162.51 94.932 125.82 37.782 22.314 19.042
+408 "R14" 99.745 94.301 93.467 17.467 17.105 13.104
+409 "S14" 107.25 159.78 58.090 22.370 35.508 5.9931
+410 "T14" 128.01 72.369 117.84 23.886 13.985 16.868
+411 "U14" 107.76 101.18 90.766 19.591 19.252 12.172
+412 "V14" 204.14 190.16 48.644 63.345 63.812 5.6645
+413 "W14" 82.597 65.626 44.970 12.028 10.481 3.3856
+414 "X14" 166.39 166.20 175.82 42.635 43.420 39.912
+415 "Y14" 100.69 152.17 51.384 19.856 32.192 5.1431
+416 "Z14" 91.650 100.20 90.746 15.570 17.641 12.711
+417 "2A14" 143.62 91.243 162.04 29.733 19.495 31.105
+418 "2B14" 214.71 215.34 214.31 81.209 84.285 67.959
+419 "2C14" 101.32 87.875 161.74 16.972 13.897 32.504
+420 "2D14" 105.18 138.75 68.497 20.262 27.906 7.7177
+421 "A15" 124.84 68.758 114.78 24.922 14.319 17.652
+422 "B15" 99.929 150.22 167.53 20.394 32.266 39.177
+423 "C15" 121.25 165.78 57.133 28.847 42.042 6.3345
+424 "D15" 129.49 121.04 130.58 27.579 26.553 23.681
+425 "E15" 161.27 95.254 123.97 38.874 23.290 19.357
+426 "F15" 148.20 174.03 142.22 36.703 46.352 27.027
+427 "G15" 150.41 98.923 167.42 34.061 22.803 34.531
+428 "H15" 75.180 91.615 158.04 10.113 11.921 31.879
+429 "I15" 196.22 179.25 172.32 64.423 58.008 39.281
+430 "J15" 66.558 62.105 60.696 9.4542 9.1717 6.7120
+431 "K15" 174.25 177.65 112.40 48.073 52.062 16.154
+432 "L15" 116.33 112.29 162.02 22.225 20.657 33.169
+433 "M15" 133.68 171.46 73.038 32.410 43.840 8.6168
+434 "N15" 102.47 146.49 198.15 19.932 29.141 53.896
+435 "O15" 64.715 46.574 43.032 8.2354 6.1612 3.2448
+436 "P15" 105.91 159.08 164.78 21.186 33.689 34.860
+437 "Q15" 34.379 33.754 36.003 2.8839 2.9372 2.7235
+438 "R15" 102.62 61.271 119.89 16.647 10.677 18.389
+439 "S15" 159.62 86.224 48.021 35.874 22.454 3.7685
+440 "T15" 95.611 158.57 127.45 18.024 31.495 21.231
+441 "U15" 133.03 87.140 157.87 26.027 17.635 30.172
+442 "V15" 48.894 77.368 43.484 5.7623 9.6302 3.8733
+443 "W15" 182.54 191.20 178.40 53.522 58.991 41.376
+444 "X15" 126.00 95.308 166.74 23.991 17.693 34.060
+445 "Y15" 38.376 39.986 33.590 3.5595 3.8688 2.3978
+446 "Z15" 148.30 148.94 147.37 34.055 35.265 28.241
+447 "2A15" 146.55 75.055 68.429 29.379 16.826 7.3609
+448 "2B15" 71.706 70.645 144.63 9.2107 8.5579 25.691
+449 "2C15" 133.02 80.816 44.452 25.707 18.129 3.6532
+450 "2D15" 82.777 113.54 117.52 13.875 19.568 19.948
+451 "A16" 147.43 75.104 45.417 32.441 19.582 3.7464
+452 "B16" 162.22 145.05 108.87 43.738 40.362 16.554
+453 "C16" 43.273 69.681 61.261 4.9159 8.3065 7.2559
+454 "D16" 154.15 106.72 65.606 37.197 27.832 7.4910
+455 "E16" 149.19 148.83 193.98 36.691 35.876 51.695
+456 "F16" 157.80 111.38 47.573 38.836 30.036 4.3338
+457 "G16" 100.61 79.449 149.13 17.555 13.363 28.888
+458 "H16" 206.92 207.61 211.44 75.466 77.836 67.216
+459 "I16" 97.895 82.272 66.348 16.954 15.112 7.2992
+460 "J16" 172.80 178.46 82.595 47.072 52.200 10.669
+461 "K16" 183.90 191.17 190.75 55.962 60.759 50.287
+462 "L16" 75.048 76.021 151.02 10.165 9.7715 28.705
+463 "M16" 159.42 88.282 47.938 36.859 23.500 3.8297
+464 "N16" 133.24 176.09 150.50 29.735 43.076 29.229
+465 "O16" 59.749 48.251 40.198 7.3545 6.2503 2.9745
+466 "P16" 171.06 168.69 161.58 46.114 46.965 33.568
+467 "Q16" 53.260 54.841 37.700 6.2218 6.8107 2.7776
+468 "R16" 147.03 76.735 66.424 29.663 17.214 6.8286
+469 "S16" 63.206 78.809 107.93 8.0381 10.138 16.410
+470 "T16" 150.22 77.352 47.226 31.088 18.734 3.6263
+471 "U16" 97.824 159.49 134.64 18.911 32.215 23.899
+472 "V16" 133.39 81.567 151.23 26.145 16.883 27.363
+473 "W16" 155.32 82.024 48.477 33.705 20.761 3.7986
+474 "X16" 81.301 147.86 56.013 14.361 27.487 7.1744
+475 "Y16" 141.53 112.75 178.08 29.882 22.982 39.138
+476 "Z16" 31.556 30.196 30.075 2.4233 2.4412 1.9648
+477 "2A16" 166.04 149.78 129.15 42.519 39.188 21.270
+478 "2B16" 53.939 48.402 100.70 5.2349 4.4461 13.823
+479 "2C16" 152.96 83.435 68.500 32.492 19.465 7.0806
+480 "2D16" 141.97 181.00 131.63 33.454 46.863 22.481
+481 "A17" 93.002 152.57 112.99 18.355 31.722 19.048
+482 "B17" 48.562 44.595 81.595 4.8626 4.2833 10.526
+483 "C17" 155.24 137.12 108.13 39.631 36.092 16.781
+484 "D17" 37.723 54.820 48.270 3.8471 5.8319 4.8962
+485 "E17" 163.04 158.68 133.73 43.491 44.349 22.904
+486 "F17" 155.10 81.151 47.477 35.028 21.444 3.8305
+487 "G17" 119.76 108.26 175.73 23.826 20.302 40.464
+488 "H17" 155.83 188.69 108.40 44.211 56.679 16.435
+489 "I17" 54.125 49.417 43.252 6.5115 6.2807 3.6004
+490 "J17" 146.89 182.65 155.76 35.464 48.530 31.500
+491 "K17" 148.71 77.345 50.119 31.217 18.833 4.2081
+492 "L17" 53.925 41.854 39.291 6.1796 5.0138 3.0160
+493 "M17" 105.46 156.83 167.45 21.355 33.511 36.981
+494 "N17" 149.16 79.222 103.08 31.382 17.339 13.539
+495 "O17" 109.98 107.89 138.78 20.515 20.076 24.934
+496 "P17" 151.99 78.765 47.620 32.143 19.447 3.7592
+497 "Q17" 195.13 169.06 200.08 60.501 50.532 52.708
+498 "R17" 73.599 136.55 49.271 12.657 24.420 5.6873
+499 "S17" 37.757 33.718 34.938 3.4355 3.1639 2.5372
+500 "T17" 132.61 142.77 197.68 28.192 29.771 51.322
+501 "U17" 181.82 145.93 103.48 52.660 42.651 15.019
+502 "V17" 204.10 203.91 210.35 69.823 71.393 63.731
+503 "W17" 175.82 179.36 52.229 44.995 50.413 5.2421
+504 "X17" 155.69 156.50 143.88 37.401 38.967 26.702
+505 "Y17" 65.731 44.495 80.654 8.0339 5.5483 9.9052
+506 "Z17" 67.930 138.21 88.916 10.382 21.829 11.599
+507 "2A17" 156.40 87.866 68.881 34.325 21.131 7.2263
+508 "2B17" 68.870 68.990 41.254 9.1863 9.9839 3.1512
+509 "2C17" 209.60 202.66 207.60 75.534 73.388 61.922
+510 "2D17" 108.97 87.062 152.98 19.258 14.916 29.290
+511 "A18" 200.70 204.40 197.90 74.089 78.770 59.424
+512 "B18" 164.93 117.44 177.68 42.903 30.204 40.715
+513 "C18" 61.227 72.827 56.013 8.7363 10.837 6.0872
+514 "D18" 204.51 199.19 195.95 75.509 74.636 56.066
+515 "E18" 73.692 73.322 148.06 10.413 9.8011 28.727
+516 "F18" 68.313 128.24 45.392 11.836 22.861 5.4226
+517 "G18" 131.14 85.288 152.52 26.954 18.177 29.358
+518 "H18" 89.609 153.68 93.128 16.220 29.699 13.154
+519 "I18" 30.179 29.676 28.922 2.3647 2.3980 1.9090
+520 "J18" 208.65 210.69 209.48 77.786 81.283 65.463
+521 "K18" 88.863 70.346 63.498 14.363 11.898 6.7644
+522 "L18" 197.92 177.16 54.602 60.378 57.128 5.2726
+523 "M18" 143.55 91.470 162.00 30.554 20.123 31.743
+524 "N18" 121.99 131.52 122.44 24.748 27.645 20.451
+525 "O18" 172.04 115.00 91.328 44.929 31.003 12.389
+526 "P18" 81.187 74.923 148.64 11.881 10.425 27.990
+527 "Q18" 117.91 169.48 101.12 25.737 39.690 14.823
+528 "R18" 183.93 145.62 52.297 52.300 42.856 4.6545
+529 "S18" 55.452 105.38 65.438 7.2126 14.429 7.1944
+530 "T18" 211.11 208.92 210.90 77.679 78.425 65.193
+531 "U18" 177.41 124.39 134.00 47.084 32.209 22.319
+532 "V18" 53.506 90.891 78.889 6.6683 11.848 10.189
+533 "W18" 197.65 205.13 206.81 65.207 70.970 60.729
+534 "X18" 51.396 86.860 43.674 6.2806 11.330 4.1095
+535 "Y18" 193.58 175.18 113.92 60.571 56.044 16.070
+536 "Z18" 96.313 103.94 178.60 15.673 16.045 40.153
+537 "2A18" 168.53 106.08 128.73 41.178 25.901 19.845
+538 "2B18" 85.496 104.15 136.89 13.693 16.674 24.407
+539 "2C18" 208.09 210.26 179.31 74.223 78.952 41.733
+540 "2D18" 161.94 92.492 50.116 38.135 24.817 4.1395
+541 "A19" 156.71 112.54 101.12 39.239 29.094 15.479
+542 "B19" 42.513 58.501 40.536 4.7416 7.0454 3.4644
+543 "C19" 172.17 152.13 143.40 49.395 43.657 28.522
+544 "D19" 159.73 183.13 54.703 42.332 52.372 5.7052
+545 "E19" 119.21 105.48 95.518 24.242 22.424 14.026
+546 "F19" 30.235 29.764 29.269 2.4551 2.5226 2.0256
+547 "G19" 88.689 134.99 136.69 16.677 25.667 25.825
+548 "H19" 86.392 53.901 45.566 13.124 8.8453 3.5763
+549 "I19" 126.88 158.15 160.78 28.527 37.076 34.358
+550 "J19" 120.42 79.125 64.241 22.891 16.490 6.9749
+551 "K19" 108.34 160.80 164.56 22.666 35.279 35.631
+552 "L19" 49.327 39.952 38.592 5.4212 4.5484 2.9627
+553 "M19" 67.917 128.27 44.976 11.199 21.925 5.4425
+554 "N19" 127.84 62.080 45.945 22.708 12.897 3.6335
+555 "O19" 74.380 138.45 130.11 12.507 23.738 22.498
+556 "P19" 30.060 29.337 28.817 2.3442 2.3896 1.9267
+557 "Q19" 147.97 73.539 46.480 30.303 17.749 3.5777
+558 "R19" 75.153 76.608 151.15 10.104 9.7374 28.448
+559 "S19" 78.339 144.41 52.936 13.901 26.685 6.3873
+560 "T19" 31.312 30.556 30.767 2.4391 2.4780 2.0610
+561 "U19" 169.32 177.98 203.35 45.972 49.473 57.247
+562 "V19" 44.471 72.376 45.634 4.8275 8.3485 3.9886
+563 "W19" 199.02 205.39 55.979 63.154 70.855 7.5811
+564 "X19" 90.125 81.954 73.039 14.863 14.166 8.5770
+565 "Y19" 207.33 211.44 212.22 74.340 78.986 66.286
+566 "Z19" 182.95 126.85 175.69 49.996 33.344 36.894
+567 "2A19" 77.857 90.598 163.13 10.165 11.287 33.081
+568 "2B19" 202.16 204.23 49.052 62.642 69.515 6.2139
+569 "2C19" 162.94 96.440 68.740 38.429 24.741 7.3479
+570 "2D19" 83.952 84.116 160.97 12.944 12.283 32.608
+END_DATA
diff --git a/scanin/ColorChecker.cht b/scanin/ColorChecker.cht
new file mode 100644
index 0000000..1da441b
--- /dev/null
+++ b/scanin/ColorChecker.cht
@@ -0,0 +1,59 @@
+
+BOXES 25
+ F _ _ 10.0 9.75 321.25 9.75 321.26 217 10.0 217
+ D ALL ALL _ _ 330 228 0 0 0 0
+ Y 1 6 A D 46.0 46.0 12.0 11.5 52.5 52.5
+
+BOX_SHRINK 6.0
+
+REF_ROTATION 0.0
+
+XLIST 12
+ 12.0 1.0 1.0
+ 58.5 1.0 1.0
+ 64.5 1.0 1.0
+ 110.5 1.0 1.0
+ 117.0 1.0 1.0
+ 163.0 1.0 1.0
+ 169.5 1.0 1.0
+ 215.5 1.0 1.0
+ 221.5 1.0 1.0
+ 267.5 1.0 1.0
+ 274.0 1.0 1.0
+ 320.0 1.0 1.0
+
+YLIST 8
+ 11.5 1.0 1.0
+ 58.0 1.0 1.0
+ 64.0 1.0 1.0
+ 110.0 1.0 1.0
+ 116.5 1.0 1.0
+ 163.0 1.0 1.0
+ 169.0 1.0 1.0
+ 215.0 1.0 1.0
+
+EXPECTED XYZ 24
+ A1 11.773 10.213 4.9219
+ A2 40.174 36.201 20.217
+ A3 17.675 19.409 26.983
+ A4 11.121 13.530 5.5615
+ A5 25.551 24.404 34.847
+ A6 31.744 43.164 35.249
+ B1 40.056 30.947 4.8180
+ B2 12.544 11.700 28.648
+ B3 30.675 20.352 10.837
+ B4 8.3961 6.5047 10.849
+ B5 36.036 44.991 8.9494
+ B6 50.203 44.570 6.2773
+ C1 7.4590 6.0952 23.518
+ C2 15.439 23.986 7.7482
+ C3 22.850 13.022 4.1188
+ C4 59.637 60.332 7.3520
+ C5 30.450 20.015 22.947
+ C6 13.591 19.466 30.479
+ D1 86.776 90.361 70.642
+ D2 56.865 59.038 48.218
+ D3 34.763 36.036 29.378
+ D4 18.884 19.603 16.309
+ D5 8.4332 8.7464 7.1022
+ D6 3.0110 3.0971 2.5475
diff --git a/scanin/ColorChecker.cie b/scanin/ColorChecker.cie
new file mode 100644
index 0000000..8c78ff6
--- /dev/null
+++ b/scanin/ColorChecker.cie
@@ -0,0 +1,39 @@
+IT8.7/2
+ORIGINATOR "Graeme Gill, ArgyllCMS from Gretag Macbeth reference"
+DESCRIPTOR "ColorChecker 24"
+CREATED "Feb 18, 2008"
+MANUFACTURER "X-Rite/Gretag Macbeth"
+
+NUMBER_OF_FIELDS 4
+BEGIN_DATA_FORMAT
+SAMPLE_ID LAB_L LAB_A LAB_B
+END_DATA_FORMAT
+
+NUMBER_OF_SETS 24
+BEGIN_DATA
+A01 37.99 13.56 14.06
+A02 65.71 18.13 17.81
+A03 49.93 -4.88 -21.93
+A04 43.14 -13.10 21.91
+A05 55.11 8.84 -25.40
+A06 70.72 -33.40 -0.20
+B01 62.66 36.07 57.10
+B02 40.02 10.41 -45.96
+B03 51.12 48.24 16.25
+B04 30.33 22.98 -21.59
+B05 72.53 -23.71 57.26
+B06 71.94 19.36 67.86
+C01 28.78 14.18 -50.30
+C02 55.26 -38.34 31.37
+C03 42.10 53.38 28.19
+C04 81.73 4.04 79.82
+C05 51.94 49.99 -14.57
+C06 51.04 -28.63 -28.64
+D01 96.54 -0.43 1.19
+D02 81.26 -0.64 -0.34
+D03 66.77 -0.73 -0.50
+D04 50.87 -0.15 -0.27
+D05 35.66 -0.42 -1.23
+D06 20.46 -0.08 -0.97
+END_DATA
+
diff --git a/scanin/ColorCheckerDC.cht b/scanin/ColorCheckerDC.cht
new file mode 100644
index 0000000..ad7ea29
--- /dev/null
+++ b/scanin/ColorCheckerDC.cht
@@ -0,0 +1,318 @@
+
+BOXES 241
+ F _ _ 26.09 18.7 330.2 18.7 330.2 202.4 26.09 202.4
+ D ALL ALL _ _ 355 216 0 0 0 0
+ X A T 01 12 12.675 12.75 27.5 20.0 15.175 15.25
+
+BOX_SHRINK 1.6
+
+XLIST 40
+ 27.5 1.0 1.0
+ 40.175 1.0 1.0
+ 42.675 1.0 1.0
+ 55.35 1.0 1.0
+ 57.85 1.0 1.0
+ 70.525 1.0 1.0
+ 73.025 1.0 1.0
+ 85.7 1.0 1.0
+ 88.2 1.0 1.0
+ 100.875 1.0 1.0
+ 103.375 1.0 1.0
+ 116.05 1.0 1.0
+ 118.55 1.0 1.0
+ 131.225 1.0 1.0
+ 133.725 1.0 1.0
+ 146.4 1.0 1.0
+ 148.9 1.0 1.0
+ 161.575 1.0 1.0
+ 164.075 1.0 1.0
+ 176.75 1.0 1.0
+ 179.25 1.0 1.0
+ 191.925 1.0 1.0
+ 194.425 1.0 1.0
+ 207.1 1.0 1.0
+ 209.6 1.0 1.0
+ 222.275 1.0 1.0
+ 224.775 1.0 1.0
+ 237.45 1.0 1.0
+ 239.95 1.0 1.0
+ 252.625 1.0 1.0
+ 255.125 1.0 1.0
+ 267.8 1.0 1.0
+ 270.3 1.0 1.0
+ 282.975 1.0 1.0
+ 285.475 1.0 1.0
+ 298.15 1.0 1.0
+ 300.65 1.0 1.0
+ 313.325 1.0 1.0
+ 315.825 1.0 1.0
+ 328.5 1.0 1.0
+
+YLIST 24
+ 20.0 1.0 1.0
+ 32.75 1.0 1.0
+ 35.25 1.0 1.0
+ 48.0 1.0 1.0
+ 50.5 1.0 1.0
+ 63.25 1.0 1.0
+ 65.75 1.0 1.0
+ 78.5 1.0 1.0
+ 81.0 1.0 1.0
+ 93.75 1.0 1.0
+ 96.25 1.0 1.0
+ 109.0 1.0 1.0
+ 111.5 1.0 1.0
+ 124.25 1.0 1.0
+ 126.75 1.0 1.0
+ 139.5 1.0 1.0
+ 142.0 1.0 1.0
+ 154.75 1.0 1.0
+ 157.25 1.0 1.0
+ 170.0 1.0 1.0
+ 172.5 1.0 1.0
+ 185.25 1.0 1.0
+ 187.75 1.0 1.0
+ 200.5 1.0 1.0
+
+EXPECTED XYZ 240
+A01 74.79 77.56 64.46
+A02 3.08 3.19 2.77
+A03 18.98 19.71 16.53
+A04 74.57 77.34 64.38
+A05 3.1 3.21 2.78
+A06 18.88 19.62 16.44
+A07 74.65 77.43 64.37
+A08 3.05 3.16 2.74
+A09 18.86 19.59 16.42
+A10 74.33 77.13 64.18
+A11 3.05 3.16 2.74
+A12 18.94 19.67 16.48
+B01 18.98 19.71 16.53
+B02 9.28 6.32 3.27
+B03 7.34 6.63 3.64
+B04 19.71 19.69 11.13
+B05 10.69 11.58 6.54
+B06 5.55 6.26 3.53
+B07 4.69 6.4 3.88
+B08 4.45 6.24 5.6
+B09 5.26 6.13 6.71
+B10 4.16 4.4 7.55
+B11 7.13 6.18 5.2
+B12 74.68 77.45 64.31
+C01 3.03 3.14 2.72
+C02 22.27 12.45 5.91
+C03 13.73 11.57 5.26
+C04 12.35 11.83 4.46
+C05 16.62 19.15 7.75
+C06 8.81 11.49 5.72
+C07 7.38 11.51 5.94
+C08 7.96 11.09 9.57
+C09 5.61 6.35 9.68
+C10 5.49 6.22 12.37
+C11 8.5 6.58 6.19
+C12 3.04 3.15 2.73
+D01 74.7 77.48 64.38
+D02 23.6 12.43 3.41
+D03 15.71 12.25 4.16
+D04 19.85 19.78 4.3
+D05 15.7 19.2 4.89
+D06 22.07 30.17 8.05
+D07 6.22 11.44 6.34
+D08 11.56 19.31 16.65
+D09 5.69 6.13 12.53
+D10 5.6 5.88 14.72
+D11 9.13 6.35 7.28
+D12 18.91 19.64 16.46
+E01 18.95 19.69 16.5
+E02 35.41 20.25 6.45
+E03 28.25 20.38 3.83
+E04 20.73 19.59 4.47
+E05 16.4 19.03 4.34
+E06 34.81 43.02 17.75
+E07 11.78 19.58 11.32
+E08 6.88 11.75 14.39
+E09 7.88 11.31 19.64
+E10 9.22 11.09 27.08
+E11 17.84 11.47 13.98
+E12 74.56 77.32 64.24
+F01 3.04 3.15 2.73
+F02 48.3 30.25 11.41
+F03 16.14 10.93 3.9
+F04 31.33 29.91 8.52
+F05 14.11 18.71 6.4
+F06 38.16 43.99 9.51
+F07 10.52 19.33 8.14
+F08 7.96 11.49 16.13
+F09 8.63 11.3 17.86
+F10 10.41 11.24 27.64
+F11 21.49 12.15 9.52
+F12 3.04 3.15 2.73
+G01 74.73 77.5 64.38
+G02 44.51 29.8 15.52
+G03 24.34 19.34 8.11
+G04 25.26 19.88 4.38
+G05 13.65 19.52 4.62
+G06 36.49 43.18 5.73
+G07 19.42 29.58 14.51
+G08 19.86 28.9 37.67
+G09 14.15 18.75 25.13
+G10 10.69 11.62 23.13
+G11 31.57 19.76 17.91
+G12 18.91 19.64 16.46
+H01 18.98 19.71 16.53
+H02 33.5 20.33 9.91
+H03 34.67 28.65 13.17
+H04 16.86 12.12 2.75
+H05 21.16 30.06 6.66
+H06 52.08 58.65 19.39
+H07 22.63 29.16 10.83
+H08 21.74 29.15 34.08
+H09 13.59 18.65 33.2
+H10 17.32 18.86 39.16
+H11 29.3 19.82 17.88
+H12 74.65 77.41 64.27
+I01 3.05 3.16 2.74
+I02 22.03 19.1 15.09
+I03 31.97 28.79 15.9
+I04 42.69 29.41 6.24
+I05 82.21 85.41 69.86
+I06 30.96 32.15 26.58
+I07 16.49 17.18 14.34
+I08 7.03 7.28 6.15
+I09 24.91 29.34 38.48
+I10 29.04 29.31 45.2
+I11 41.09 29.82 26.18
+I12 3.05 3.16 2.75
+J01 74.72 77.5 64.44
+J02 26.91 19.35 14.94
+J03 34.54 29.66 11.16
+J04 58.1 42.93 8.71
+J05 64.14 66.75 54.99
+J06 87.06 90.39 72.76
+J07 86.82 90.15 72.56
+J08 4.28 4.47 3.84
+J09 24.87 28.59 45.17
+J10 41.47 42.2 57.7
+J11 53.38 42.55 36.49
+J12 18.92 19.65 16.47
+K01 18.97 19.7 16.52
+K02 36.7 29.69 19.45
+K03 36.83 30.22 8.83
+K04 53.42 43.05 10.13
+K05 51.78 53.86 44.86
+K06 86.85 90.18 72.55
+K07 86.91 90.25 72.64
+K08 3.45 3.59 3.07
+K09 21.49 28.73 46.46
+K10 21.85 19.42 36.39
+K11 32.48 29.05 24.22
+K12 74.63 77.39 64.28
+L01 3.03 3.14 2.73
+L02 53.85 42.1 29.27
+L03 58.21 42.93 21.15
+L04 40.96 30.79 3.87
+L05 40.79 42.45 35.04
+L06 23.51 24.47 20.31
+L07 11.1 11.62 9.8
+L08 3.02 3.13 2.71
+L09 31.86 41.84 53.57
+L10 7.9 6.16 10.17
+L11 62.34 57.68 47.93
+L12 3.03 3.14 2.72
+M01 74.74 77.52 64.43
+M02 68.21 58.76 45.3
+M03 54.32 42.79 26.91
+M04 68.17 58.65 13.41
+M05 76.51 80.88 14.08
+M06 15.06 19.36 18.82
+M07 16.03 29.31 11.79
+M08 52.05 57.11 51.73
+M09 32.54 42.52 46.33
+M10 7.2 6.19 8.57
+M11 78.01 78.48 67.07
+M12 18.93 19.67 16.48
+N01 18.97 19.7 16.51
+N02 65.97 60.13 42.01
+N03 50.5 42.33 28.58
+N04 63.78 59.07 15.95
+N05 62.58 58.72 7.2
+N06 21.33 29.57 29.73
+N07 17.06 29.9 17.13
+N08 54.05 58.63 61.58
+N09 49.41 57.82 58.22
+N10 16.9 12.02 19.29
+N11 41.76 29.82 32.52
+N12 74.67 77.43 64.28
+O01 3.02 3.13 2.71
+O02 47.1 42.51 31.08
+O03 51.87 42.51 15.81
+O04 77.2 77.91 50.55
+O05 45.99 42.6 4.49
+O06 23.64 29.5 29.06
+O07 26.97 42.73 19.28
+O08 27.97 29.35 28.33
+O09 50.32 57.45 48.38
+O10 23.06 19.58 23.21
+O11 32.18 29.25 34.95
+O12 3.01 3.12 2.7
+P01 74.64 77.41 64.32
+P02 44.39 42.98 33.4
+P03 71.49 59.42 23.1
+P04 79.86 78.26 38.44
+P05 31.59 29.6 5.37
+P06 20.07 28.76 23.92
+P07 31.22 42.07 27.31
+P08 17.15 18.99 23.19
+P09 37.97 42.95 38.15
+P10 19.91 19.24 17.99
+P11 44.36 43.41 45.48
+P12 18.95 19.68 16.49
+Q01 18.96 19.69 16.51
+Q02 59.99 58.87 41.85
+Q03 67.98 58.85 30.96
+Q04 82.15 79.03 48.34
+Q05 33.18 29.48 5.74
+Q06 18.92 29.39 27.97
+Q07 44.1 58.65 39.14
+Q08 37.51 41.38 26.69
+Q09 40.27 42.04 17.38
+Q10 24.86 28.48 22.3
+Q11 9.43 11.28 9.5
+Q12 74.61 77.37 64.24
+R01 3.05 3.16 2.74
+R02 31.16 30.09 20.86
+R03 56.92 56.98 36.53
+R04 79.37 78.57 56.95
+R05 42.68 42.54 8.26
+R06 28.15 41.91 31.53
+R07 48.61 58.02 33.46
+R08 26.81 28.91 16.95
+R09 39.83 43.08 12.47
+R10 11.8 11.72 5.87
+R11 17.1 19.71 16.41
+R12 3.02 3.13 2.71
+S01 74.66 77.43 64.34
+S02 40.67 42.42 26.09
+S03 65.09 68.67 41.71
+S04 3.01 3.34 15.4
+S05 3.26 11.22 2.89
+S06 17.7 8.11 0.42
+S07 89.01 92.27 74.63
+S08 0.28 0.29 0.24
+S09 72.92 74.82 4.56
+S10 17.03 7.57 3.05
+S11 6.56 11.29 24.29
+S12 18.91 19.64 16.46
+T01 18.91 19.64 16.46
+T02 3.05 3.16 2.73
+T03 74.38 77.15 64.2
+T04 18.94 19.68 16.48
+T05 3.12 3.24 2.79
+T06 74.2 76.97 63.98
+T07 18.87 19.61 16.43
+T08 3.04 3.16 2.73
+T09 74.41 77.19 64.05
+T10 18.9 19.63 16.46
+T11 3.09 3.2 2.78
+T12 74.57 77.32 64.19
+
diff --git a/scanin/ColorCheckerPassport.cht b/scanin/ColorCheckerPassport.cht
new file mode 100644
index 0000000..758cad7
--- /dev/null
+++ b/scanin/ColorCheckerPassport.cht
@@ -0,0 +1,122 @@
+BOXES 51
+ F _ _ 97.0 17.0 157.3 17.0 157.3 107.0 97.0 107.0
+ D ALL ALL _ _ 59.0 5.0 98.0 11.0 0.0 0.0
+ X SAT SAT 1 8 10.0 10.0 13.5 102.1 0.0 -12.9
+ X WBP WBP 1 5 13.4 13.4 26.4 87.0 0.0 -16.0
+ X WBL WBL 1 5 13.4 13.4 42.5 87.0 0.0 -16.0
+ X NEU NEU 1 8 10.0 10.0 58.6 102.1 0.0 -12.9
+ X A D 1 6 12.4 12.4 98.5 93.4 15.0 -15.0
+
+BOX_SHRINK 2.0
+
+REF_ROTATION 0.0
+
+XLIST 16
+ 13.5 1.0 1.0
+ 23.5 1.0 1.0
+ 26.4 1.0 1.0
+ 39.8 1.0 1.0
+ 42.5 1.0 1.0
+ 55.9 1.0 1.0
+ 58.6 1.0 1.0
+ 68.6 1.0 1.0
+ 98.5 1.0 1.0
+ 110.9 1.0 1.0
+ 113.5 1.0 1.0
+ 125.9 1.0 1.0
+ 128.5 1.0 1.0
+ 140.9 1.0 1.0
+ 143.5 1.0 1.0
+ 155.9 1.0 1.0
+
+YLIST 38
+ 11.5 1.0 1.0
+ 18.5 1.0 1.0
+ 21.6 1.0 1.0
+ 23.4 1.0 1.0
+ 24.3 1.0 1.0
+ 30.9 1.0 1.0
+ 33.5 1.0 1.0
+ 34.6 1.0 1.0
+ 36.5 1.0 1.0
+ 37.2 1.0 1.0
+ 39.2 1.0 1.0
+ 45.8 1.0 1.0
+ 47.5 1.0 1.0
+ 48.4 1.0 1.0
+ 50.2 1.0 1.0
+ 52.5 1.0 1.0
+ 55.2 1.0 1.0
+ 60.4 1.0 1.0
+ 60.7 1.0 1.0
+ 63.1 1.0 1.0
+ 63.4 1.0 1.0
+ 68.4 1.0 1.0
+ 71.1 1.0 1.0
+ 73.3 1.0 1.0
+ 75.7 1.0 1.0
+ 76.1 1.0 1.0
+ 78.4 1.0 1.0
+ 84.3 1.0 1.0
+ 86.3 1.0 1.0
+ 87.0 1.0 1.0
+ 89.0 1.0 1.0
+ 90.6 1.0 1.0
+ 93.4 1.0 1.0
+ 99.4 1.0 1.0
+ 100.4 1.0 1.0
+ 102.1 1.0 1.0
+ 105.7 1.0 1.0
+ 112.4 1.0 1.0
+
+EXPECTED XYZ
+SAT1 31.444334 19.286094 6.888559
+SAT2 56.064662 44.030373 6.427599
+SAT3 67.324242 67.447159 7.468562
+SAT4 15.570651 29.465796 11.20434
+SAT5 21.619963 29.778971 41.25516
+SAT6 17.456572 19.372800 46.61877
+SAT7 24.114288 18.910816 30.96444
+SAT8 30.360635 18.858573 16.93814
+WBP1 55.603171 57.729295 46.42132
+WBP2 57.049606 60.324512 49.89966
+WBP3 53.811668 58.177050 49.42470
+WBP4 52.378305 57.343255 49.64556
+WBP5 52.469763 58.680177 51.97625
+WBL1 59.661034 59.332741 48.55177
+WBL2 59.041480 59.368909 48.29234
+WBL3 55.920447 58.052670 47.03935
+WBL4 55.083575 58.526581 51.10275
+WBL5 53.892264 58.200472 53.59833
+NEU1 3.151107 3.263644 2.737095
+NEU2 3.610326 3.768524 3.225077
+NEU3 4.585675 4.814770 4.063276
+NEU4 5.242110 5.523981 4.582603
+NEU5 44.365128 46.364955 37.75652
+NEU6 55.836099 58.281967 47.53593
+NEU7 70.627230 73.603361 59.69403
+NEU8 88.638466 92.368156 71.51666
+A1 11.411919 10.072672 5.112897
+A2 38.118655 34.074205 18.53409
+A3 16.687521 18.224488 25.41503
+A4 11.119565 13.272712 5.165291
+A5 23.995041 23.022599 32.25695
+A6 29.864887 41.242607 33.61720
+B1 40.774497 30.980646 4.853984
+B2 12.786912 11.654674 29.31582
+B3 29.900282 19.631992 10.31426
+B4 8.854344 6.750602 10.88177
+B5 35.445183 44.094908 8.863355
+B6 49.113465 43.647237 6.214123
+C1 6.659992 5.799339 20.11315
+C2 15.407588 23.700772 7.772715
+C3 21.254733 12.710445 3.970060
+C4 58.616280 59.203678 6.871706
+C5 31.444170 20.458848 23.34756
+C6 13.290512 19.046913 29.20373
+D1 87.016740 90.636247 69.99650
+D2 57.632418 59.860806 48.52846
+D3 35.434916 36.927843 30.23039
+D4 18.558911 19.319142 15.73822
+D5 8.770657 9.165214 7.636996
+D6 3.216922 3.320339 2.732945
diff --git a/scanin/ColorCheckerPassport.cie b/scanin/ColorCheckerPassport.cie
new file mode 100644
index 0000000..f25c019
--- /dev/null
+++ b/scanin/ColorCheckerPassport.cie
@@ -0,0 +1,64 @@
+CTI3
+DESCRIPTOR "ColorChecker Passport CIE data"
+ORIGINATOR "Ben Goren <ben@trumpetpower.com>"
+CREATED "Fri Aug 3 15:35:05 MST 2012"
+
+KEYWORD "SAMPLE_LOC"
+NUMBER_OF_FIELDS 7
+BEGIN_DATA_FORMAT
+SAMPLE_LOC XYZ_X XYZ_Y XYZ_Z LAB_L LAB_A LAB_B
+END_DATA_FORMAT
+
+NUMBER_OF_SETS 50
+BEGIN_DATA
+SAT1 31.444334 19.286094 6.888559 51.020256 55.280672 28.133194
+SAT2 56.064662 44.030373 6.427599 72.248793 36.945117 66.729238
+SAT3 67.324242 67.447159 7.468562 85.729258 5.091307 85.588609
+SAT4 15.570651 29.465796 11.204340 61.190534 -60.437314 30.279584
+SAT5 21.619963 29.778971 41.255169 61.463043 -30.131006 -25.196180
+SAT6 17.456572 19.372800 46.618774 51.120542 -6.455450 -49.629615
+SAT7 24.114288 18.910816 30.964446 50.582702 28.026310 -29.474885
+SAT8 30.360635 18.858573 16.938149 50.521331 53.431168 -3.299773
+WBP1 55.603171 57.729295 46.421327 80.588052 -0.148185 1.410213
+WBP2 57.049606 60.324512 49.899666 82.014259 -2.717710 -0.156044
+WBP3 53.811668 58.177050 49.424708 80.837125 -5.740350 -1.647214
+WBP4 52.378305 57.343255 49.645561 80.372273 -7.424711 -2.699450
+WBP5 52.469763 58.680177 51.976257 81.115480 -10.390873 -4.020205
+WBL1 59.661034 59.332741 48.551776 81.474151 5.919789 0.449649
+WBL2 59.041480 59.368909 48.292349 81.493953 4.354460 0.782852
+WBL3 55.920447 58.052670 47.039351 80.768065 -0.134018 0.991035
+WBL4 55.083575 58.526581 51.102758 81.030672 -3.356452 -3.200486
+WBL5 53.892264 58.200472 53.598335 80.850119 -5.590938 -6.243136
+NEU1 3.151107 3.263644 2.737095 21.070257 0.072763 -0.353478
+NEU2 3.610326 3.768524 3.225077 22.890939 -0.358852 -0.826826
+NEU3 4.585675 4.814770 4.063276 26.200395 -0.743922 -0.554941
+NEU4 5.242110 5.523981 4.582603 28.178271 -1.007717 -0.143878
+NEU5 44.365128 46.364955 37.756524 73.781723 -0.983457 0.663808
+NEU6 55.836099 58.281967 47.535933 80.895303 -0.892210 0.628840
+NEU7 70.627230 73.603361 59.694037 88.734687 -0.724494 1.018307
+NEU8 88.638466 92.368156 71.516660 96.970594 -0.771975 4.070471
+A1 11.411919 10.072672 5.112897 37.972545 12.849750 13.905933
+A2 38.118655 34.074205 18.534096 65.021429 17.736538 18.105244
+A3 16.687521 18.224488 25.415039 49.767263 -4.838656 -21.688821
+A4 11.119565 13.272712 5.165291 43.171313 -11.673243 22.599880
+A5 23.995041 23.022599 32.256955 55.095608 8.054000 -23.674108
+A6 29.864887 41.242607 33.617201 70.345563 -33.879730 0.591375
+B1 40.774497 30.980646 4.853984 62.491292 36.974257 57.538878
+B2 12.786912 11.654674 29.315826 40.661930 10.745627 -43.972357
+B3 29.900282 19.631992 10.314269 51.418556 47.835790 16.229131
+B4 8.854344 6.750602 10.881778 31.232269 21.992198 -20.376399
+B5 35.445183 44.094908 8.863355 72.291888 -22.389553 57.145982
+B6 49.113465 43.647237 6.214123 71.992078 20.037754 67.243006
+C1 6.659992 5.799339 20.113152 28.900464 11.613838 -47.531575
+C2 15.407588 23.700772 7.772715 55.786947 -38.099805 32.761011
+C3 21.254733 12.710445 3.970060 42.323678 50.646029 27.808803
+C4 58.616280 59.203678 6.871706 81.403423 3.722966 80.589317
+C5 31.444170 20.458848 23.347563 52.352077 49.539466 -13.465303
+C6 13.290512 19.046913 29.203736 50.742046 -29.398805 -26.412062
+D1 87.016740 90.636247 69.996509 96.260066 -0.692605 4.206323
+D2 57.632418 59.860806 48.528468 81.762472 -0.207864 0.973581
+D3 35.434916 36.927843 30.230398 67.222860 -0.574879 0.364223
+D4 18.558911 19.319142 15.738222 51.058515 -0.355289 0.481164
+D5 8.770657 9.165214 7.636996 36.300462 -0.566396 -0.303560
+D6 3.216922 3.320339 2.732945 21.283684 0.258119 0.046995
+END_DATA
diff --git a/scanin/ColorCheckerSG.cht b/scanin/ColorCheckerSG.cht
new file mode 100644
index 0000000..1836f73
--- /dev/null
+++ b/scanin/ColorCheckerSG.cht
@@ -0,0 +1,203 @@
+
+BOXES 141
+ F _ _ 26.09 18.7 239.15 18.7 239.15 171.9 26.09 171.9
+ D ALL ALL _ _ 263.95 185.5 0 0 0 0
+ X A N 1 10 12.675 12.75 27.5 20.0 15.175 15.25
+
+BOX_SHRINK 1.6
+
+XLIST 28
+ 27.5 1.0 1.0
+ 40.175 1.0 1.0
+ 42.675 1.0 1.0
+ 55.35 1.0 1.0
+ 57.85 1.0 1.0
+ 70.525 1.0 1.0
+ 73.025 1.0 1.0
+ 85.7 1.0 1.0
+ 88.2 1.0 1.0
+ 100.875 1.0 1.0
+ 103.375 1.0 1.0
+ 116.05 1.0 1.0
+ 118.55 1.0 1.0
+ 131.225 1.0 1.0
+ 133.725 1.0 1.0
+ 146.4 1.0 1.0
+ 148.9 1.0 1.0
+ 161.575 1.0 1.0
+ 164.075 1.0 1.0
+ 176.75 1.0 1.0
+ 179.25 1.0 1.0
+ 191.925 1.0 1.0
+ 194.425 1.0 1.0
+ 207.1 1.0 1.0
+ 209.6 1.0 1.0
+ 222.275 1.0 1.0
+ 224.775 1.0 1.0
+ 237.45 1.0 1.0
+
+YLIST 20
+ 20.0 1.0 1.0
+ 32.75 1.0 1.0
+ 35.25 1.0 1.0
+ 48.0 1.0 1.0
+ 50.5 1.0 1.0
+ 63.25 1.0 1.0
+ 65.75 1.0 1.0
+ 78.5 1.0 1.0
+ 81.0 1.0 1.0
+ 93.75 1.0 1.0
+ 96.25 1.0 1.0
+ 109.0 1.0 1.0
+ 111.5 1.0 1.0
+ 124.25 1.0 1.0
+ 126.75 1.0 1.0
+ 139.5 1.0 1.0
+ 142.0 1.0 1.0
+ 154.75 1.0 1.0
+ 157.25 1.0 1.0
+ 170.0 1.0 1.0
+
+
+EXPECTED XYZ 140
+ A1 88.25 87.51 67.62
+ A2 0.79 0.79 0.62
+ A3 18.38 18.15 14.12
+ A4 88.68 87.94 68.1
+ A5 0.82 0.81 0.66
+ A6 18.44 18.2 14.18
+ A7 88.65 87.9 67.95
+ A8 0.83 0.82 0.66
+ A9 18.12 17.89 13.95
+ A10 88.87 88.13 68.08
+ B1 18.02 17.8 13.91
+ B2 13.95 7.23 8.27
+ B3 35.63 27.85 32.84
+ B4 10.67 5.28 15.25
+ B5 14.41 15.79 39.4
+ B6 20.68 26.24 36.89
+ B7 1.45 2.35 2.56
+ B8 18.04 25.72 27.04
+ B9 3.38 3.27 1.58
+ B10 18.48 18.24 14.19
+ C1 0.83 0.81 0.66
+ C2 4.58 3.29 5.62
+ C3 14.6 11.31 24.29
+ C4 2.74 2.49 8.91
+ C5 22.92 25.79 40.25
+ C6 1.84 2.4 5.03
+ C7 26.45 26.69 42.35
+ C8 10.25 16.92 17.28
+ C9 19.05 27.21 12.27
+ C10 0.81 0.8 0.64
+ D1 88.5 87.75 67.91
+ D2 61.95 61.45 55.8
+ D3 70.83 63.53 49.33
+ D4 55.96 62.69 49.91
+ D5 71.17 64.56 44.57
+ D6 58.43 62.22 28.86
+ D7 38.57 30.13 8.72
+ D8 38.93 32.31 15.64
+ D9 9.71 17.25 8.69
+ D10 88.85 88.1 68.15
+ E1 18.14 17.91 13.97
+ E2 8.92 6.95 2.01
+ E3 39.66 29.42 2.06
+ E4 3.85 2.22 16.31
+ E5 88.54 87.8 67.91
+ E6 0.8 0.79 0.64
+ E7 57.95 50.11 24.14
+ E8 55.65 45.09 20.47
+ E9 1.8 2.87 1.55
+ E10 18.29 18.05 14.05
+ F1 0.82 0.81 0.65
+ F2 37.55 31.16 14.86
+ F3 9.34 7.46 25.35
+ F4 12.16 19.3 4.13
+ F5 54.56 53.71 42.14
+ F6 6.41 6.34 5.07
+ F7 35.94 31.61 12.54
+ F8 37.12 32.69 16.79
+ F9 17.63 26.21 16.52
+ F10 0.82 0.8 0.65
+ G1 88.12 87.38 67.64
+ G2 14.14 14.42 21.78
+ G3 27.07 16.15 6.53
+ G4 19.38 9.32 1.23
+ G5 33.43 32.97 25.94
+ G6 11.06 10.95 8.66
+ G7 16.15 13.52 3.95
+ G8 37.66 32.45 16.85
+ G9 21.4 28.64 6.94
+ G10 88.76 88.01 68.03
+ H1 18.23 18 14.03
+ H2 7.94 10.03 2.21
+ H3 5.33 3.13 7.61
+ H4 58.28 57.39 3.84
+ H5 18.06 17.83 13.93
+ H6 27.87 27.48 21.66
+ H7 40.98 36.13 18.85
+ H8 38.13 32.77 16.09
+ H9 10.91 18.91 3.26
+ H10 18.32 18.08 14.08
+ I1 0.88 0.87 0.69
+ I2 20.96 18.51 28.03
+ I3 33.58 40.91 5.29
+ I4 28.76 16.58 19.42
+ I5 8.34 8.25 6.47
+ I6 47.53 46.8 36.74
+ I7 18.56 14.28 2.54
+ I8 10.92 8.92 2.27
+ I9 17.44 28.57 5.55
+ I10 0.82 0.81 0.65
+ J1 88.45 87.71 67.89
+ J2 28.25 36.79 28.98
+ J3 46.48 40.82 3.05
+ J4 10.73 14.67 25.45
+ J5 1.95 1.94 1.63
+ J6 72.43 71.6 56.6
+ J7 39 31.84 12.66
+ J8 41.29 34.62 13.36
+ J9 34.26 30.19 5.4
+ J10 88.69 87.94 67.95
+ K1 18.29 18.06 14.09
+ K2 69.8 64.78 36.61
+ K3 65.69 71.95 50.54
+ K4 65.74 62.36 54.38
+ K5 56.64 60.89 55.78
+ K6 40.88 40.32 31.77
+ K7 14.74 14.56 11.28
+ K8 2.93 2.89 2.29
+ K9 26.26 30.23 4.54
+ K10 18.27 18.04 14.06
+ L1 0.86 0.85 0.69
+ L2 6.16 3.5 1.81
+ L3 26.34 13.44 1.4
+ L4 38.55 28.09 19.93
+ L5 39.77 29.23 14.66
+ L6 46.55 31.09 1.68
+ L7 40.32 44.89 2.28
+ L8 30.88 30.54 4.31
+ L9 34.35 43.52 4.2
+ L10 0.8 0.79 0.64
+ M1 18.19 17.97 14.01
+ M2 23.95 12.47 6.9
+ M3 4.92 2.88 3.25
+ M4 22.84 11.31 2.24
+ M5 37.86 20.94 3.23
+ M6 68.62 59.75 4.77
+ M7 62.14 60.57 3.46
+ M8 36.4 42.08 3.08
+ M9 3.85 2.98 0.82
+ M10 18.24 18.01 14.03
+ N1 88.23 87.51 67.77
+ N2 18.24 18.01 14.03
+ N3 0.9 0.89 0.72
+ N4 88.57 87.82 67.97
+ N5 18.12 17.9 13.96
+ N6 0.91 0.9 0.73
+ N7 88.71 87.96 67.97
+ N8 18.2 17.97 14
+ N9 0.84 0.83 0.67
+ N10 88.82 88.07 68.12
+
diff --git a/scanin/Hutchcolor.cht b/scanin/Hutchcolor.cht
new file mode 100644
index 0000000..fcc5135
--- /dev/null
+++ b/scanin/Hutchcolor.cht
@@ -0,0 +1,597 @@
+
+BOXES 639
+ F _ _ 99.5 25.5 815.5 25.5 816.5 565.5 99.5 565.5
+ D ALL ALL _ _ 716 540 99.5 25.5 0 0
+ Y 01 29 A V 24.689655 24.545454 99.5 25.5 24.689655 24.545454
+
+BOX_SHRINK 3.0
+
+REF_ROTATION 0.0
+
+XLIST 29
+ 100.0 0.362323 0.148148
+ 110.211740 0.039102 0.222222
+ 125.481836 0.971171 0.333333
+ 150.418808 0.792286 0.925926
+ 174.477814 0.734537 0.962963
+ 199.527718 0.787966 1.000000
+ 224.576901 0.650620 0.925926
+ 248.755279 0.417736 0.888889
+ 274.032480 0.613223 0.888889
+ 298.327154 0.543354 0.925926
+ 323.197333 0.847712 0.925926
+ 347.120895 0.812650 0.925926
+ 372.370502 0.860240 0.777778
+ 396.353101 0.679006 0.703704
+ 421.201328 1.000000 0.814815
+ 446.684199 0.456817 0.703704
+ 470.550542 0.508343 0.740741
+ 495.397842 0.625561 0.703704
+ 520.420248 0.386812 0.777778
+ 544.869239 0.452824 0.777778
+ 569.748767 0.547266 0.666667
+ 594.755774 0.465053 0.666667
+ 618.496351 0.477468 0.703704
+ 644.014919 0.454719 0.703704
+ 668.040606 0.479467 0.666667
+ 693.095409 0.514558 0.666667
+ 717.270886 0.882612 0.666667
+ 791.542805 0.771497 0.444444
+ 817.003432 0.882693 0.111111
+
+YLIST 24
+ 25.546595 1.000000 0.071429
+ 49.424472 0.975749 0.392857
+ 75.392921 0.738973 0.964286
+ 99.186517 0.910072 1.000000
+ 124.186786 0.852166 0.964286
+ 149.214757 0.795826 0.892857
+ 173.106047 0.641529 0.857143
+ 198.089764 0.742257 0.928571
+ 223.213674 0.604765 0.928571
+ 246.745099 0.949854 1.000000
+ 271.506325 0.862745 0.857143
+ 294.880215 0.786340 0.571429
+ 320.004798 0.644361 0.750000
+ 344.432584 0.421439 0.857143
+ 369.861483 0.378724 0.678571
+ 393.074962 0.877958 0.964286
+ 418.078441 0.680991 1.000000
+ 442.078222 0.783297 1.000000
+ 466.901848 0.648933 0.678571
+ 491.506012 0.239702 0.642857
+ 515.663175 0.154566 0.285714
+ 524.564065 0.106932 0.250000
+ 540.560259 0.769595 0.250000
+ 565.915658 0.797211 0.178571
+
+EXPECTED XYZ 528
+ A01 76.63 79.337 62.787
+ A15 0.049 0.047 0.04
+ A16 76.799 79.369 62.869
+ A29 76.788 79.423 62.902
+ B01 0.051 0.046 0.042
+ B02 23.742 12.069 27.69
+ B03 29.571 17.273 33.401
+ B04 36.377 24.341 39.101
+ B05 46.402 36.285 46.046
+ B06 60.163 54.778 54.102
+ B07 76.815 79.257 62.889
+ B08 70.22 73.135 37.781
+ B09 52.398 46.914 32.877
+ B10 37.18 27.08 27.417
+ B11 25.856 14.882 21.359
+ B12 19.163 9.224 17.005
+ B13 12.41 5.017 11.384
+ B14 68.099 66.231 58.21
+ B15 64.604 64.824 58.092
+ B16 58.787 61.057 57.489
+ B17 60.985 65.447 59.249
+ B18 62.617 69.06 60.756
+ B19 61.499 67.999 56.83
+ B20 59.996 66.835 49.738
+ B21 66.383 71.172 50.262
+ B22 72.856 75.172 50.47
+ B23 68.187 68.027 48.999
+ B24 63.438 60.952 47.208
+ B25 65.486 62.759 54.536
+ B26 74.738 77.87 62.671
+ B27 76.752 79.251 62.956
+ B28 72.734 74.343 59.875
+ B29 0.046 0.043 0.032
+ C01 21.633 22.888 20.349
+ C02 18.412 9.88 27.737
+ C03 22.631 14.555 33.706
+ C04 27.389 20.594 38.954
+ C05 34.465 30.776 45.359
+ C06 43.817 46.079 52.702
+ C07 51.156 60.04 58.504
+ C08 46.443 56.25 36.915
+ C09 37.868 39.579 33.063
+ C10 27.157 22.631 27.438
+ C11 19.372 12.293 21.901
+ C12 14.769 7.361 17.468
+ C13 9.986 4.001 11.409
+ C14 53.009 44.867 50.025
+ C15 43.073 40.486 49.663
+ C16 33.474 35.535 49.096
+ C17 39.616 46.653 54.254
+ C18 43.877 54.8 57.684
+ C19 39.842 51.529 40.856
+ C20 37.241 49.298 27.293
+ C21 49.103 57.6 26.678
+ C22 68.052 71.088 26.663
+ C23 54.158 50.585 23.324
+ C24 42.126 34.204 20.788
+ C25 44.759 35.882 34.073
+ C26 63.526 65.924 58.034
+ C27 66.134 68.606 57.21
+ C28 66.402 69.263 52.556
+ C29 21.945 23.311 20.081
+ D02 11.393 6.774 27.149
+ D03 14.898 11.118 33.316
+ D04 18.919 16.908 38.782
+ D05 25.042 26.809 45.487
+ D06 32.814 40.626 52.643
+ D07 38.506 50.724 57.005
+ D08 34.084 47.3 37.959
+ D09 27.203 34.516 34.19
+ D10 18.226 18.563 28.41
+ D11 11.924 8.905 22.544
+ D12 8.489 4.576 17.784
+ D13 5.402 2.081 11.094
+ D14 41.275 29.968 42.582
+ D15 28.108 24.154 42.115
+ D16 20.099 21.735 43.019
+ D17 29.182 37.879 52.109
+ D18 35.153 47.891 56.53
+ D19 30.314 44.189 35.897
+ D20 26.406 40.305 16.077
+ D21 38.111 48.316 13.175
+ D22 65.451 67.965 13.429
+ D23 43.805 37.363 9.249
+ D24 27.598 18.034 7.037
+ D25 30.174 19.545 20.547
+ D26 58.48 57.966 51.192
+ D27 54.554 56.734 46.744
+ D28 53.009 56.386 44.417
+ E02 8.386 5.781 27.763
+ E03 11.574 10.412 34.225
+ E04 15.311 16.641 40.002
+ E05 21.099 27.099 47.037
+ E06 27.464 38.835 53.265
+ E07 30.583 43.614 55.324
+ E08 27.553 41.253 43.72
+ E09 23.131 33.91 40.619
+ E10 15.049 18.612 33.316
+ E11 9.165 8.213 26.012
+ E12 5.821 3.476 19.352
+ E13 3.178 1.134 11.065
+ E14 32.772 20.441 36.137
+ E15 18.574 14.486 36.000
+ E16 12.165 13.283 37.532
+ E17 20.936 30.082 49.114
+ E18 26.179 39.081 53.662
+ E19 21.215 35.148 32.511
+ E20 16.763 30.052 8.608
+ E21 30.574 41.333 6.293
+ E22 63.883 65.154 6.804
+ E23 36.079 27.5 3.064
+ E24 18.426 9.673 1.782
+ E25 20.328 10.668 11.479
+ E26 47.129 50.747 43.494
+ E27 45.528 47.626 39.123
+ E28 44.554 44.762 35.204
+ F02 6.368 4.959 27.103
+ F03 8.954 9.421 33.626
+ F04 12.046 15.412 39.432
+ F05 16.445 24.385 45.858
+ F06 20.113 31.659 50.153
+ F07 22.051 34.426 51.578
+ F08 19.632 32.109 44.547
+ F09 17.001 27.957 42.024
+ F10 11.577 16.618 35.22
+ F11 6.918 7.101 27.029
+ F12 4.309 2.758 19.695
+ F13 2.222 0.731 10.705
+ F14 26.945 14.78 30.904
+ F15 12.401 8.777 30.923
+ F16 6.658 6.438 29.4
+ F17 12.797 19.297 42.136
+ F18 18.427 29.885 49.126
+ F19 12.294 24.337 23.523
+ F20 8.47 18.427 2.954
+ F21 25.113 35.116 2.692
+ F22 62.917 62.389 3.348
+ F23 29.138 19.14 0.691
+ F24 12.647 5.718 0.185
+ F25 14.014 6.371 5.555
+ F26 35.636 36.998 34.385
+ F27 37.357 39.073 32.505
+ F28 39.18 41.212 29.994
+ G02 4.654 3.718 23.728
+ G03 6.283 6.908 29.334
+ G04 8.163 11.056 34.327
+ G05 10.842 17.236 39.952
+ G06 13.027 21.956 43.616
+ G07 14.821 24.858 45.838
+ G08 12.501 21.969 39.898
+ G09 10.848 18.953 37.473
+ G10 7.763 11.612 31.571
+ G11 5.023 5.215 24.355
+ G12 3.357 2.126 17.864
+ G13 1.743 0.552 9.45
+ G14 13.383 5.464 14.796
+ G15 4.308 1.645 13.987
+ G16 2.167 0.77 11.785
+ G17 6.278 7.264 29.187
+ G18 12.468 21.262 42.54
+ G19 7.159 16.892 13.939
+ G20 4.6 10.846 0.834
+ G21 19.409 27.107 1.076
+ G22 47.378 44.339 1.058
+ G23 22.289 13.428 0.239
+ G24 8.959 3.816 0.018
+ G25 9.579 4.041 2.544
+ G26 31.736 31.893 28.491
+ G27 30.165 31.682 26.269
+ G28 29.662 31.861 25.568
+ H02 0.461 0.186 2.441
+ H03 0.941 0.992 4.564
+ H04 1.756 3.154 6.521
+ H05 3.582 8.288 8.926
+ H06 6.013 14.762 11.175
+ H07 7.642 17.876 13.524
+ H08 9.843 19.999 25.663
+ H09 8.177 16.912 23.1
+ H10 5.372 9.823 18.883
+ H11 3.192 4.082 14.531
+ H12 2.003 1.475 10.492
+ H13 1.061 0.343 5.739
+ H14 6.674 2.663 8.453
+ H15 2.34 0.823 8.659
+ H16 1.424 0.443 7.72
+ H17 3.7 3.583 18.392
+ H18 8.309 14.901 28.931
+ H19 4.341 11.205 6.88
+ H20 3.147 7.651 0.625
+ H21 10.944 16.126 0.762
+ H22 21.411 20.364 0.606
+ H23 9.9 6.02 0.151
+ H24 5.002 2.171 0.019
+ H25 5.207 2.252 1.331
+ H26 26.152 28.536 24.809
+ H27 24.71 26.089 21.71
+ H28 23.442 23.841 19.131
+ I02 0.623 0.275 2.27
+ I03 1.358 1.348 4.048
+ I04 2.894 4.478 5.928
+ I05 6.469 12.434 8.339
+ I06 11.078 22.462 10.722
+ I07 13.785 26.952 13.421
+ I08 16.457 29.743 27.76
+ I09 13.658 25.303 24.605
+ I10 8.595 14.351 20.015
+ I11 4.549 5.587 14.961
+ I12 2.594 1.929 10.771
+ I13 1.342 0.462 6.167
+ I14 2.562 1.067 3.842
+ I15 1.095 0.427 4.104
+ I16 0.725 0.257 3.88
+ I17 1.545 1.606 7.557
+ I18 3.219 6.001 11.357
+ I19 1.747 4.545 2.842
+ I20 1.353 3.291 0.356
+ I21 4.249 6.369 0.411
+ I22 7.927 7.76 0.334
+ I23 3.799 2.502 0.109
+ I24 1.902 0.861 0.02
+ I25 1.978 0.901 0.681
+ I26 19.442 20.5 19.781
+ I27 19.851 21.168 17.794
+ I28 20.769 22.202 16.051
+ J02 1.401 0.622 2.136
+ J03 2.702 2.007 3.488
+ J04 5.051 5.588 5.045
+ J05 9.951 14.434 7.059
+ J06 16.982 27.791 9.331
+ J07 21.361 35.241 11.98
+ J08 23.982 38.364 25.345
+ J09 19.362 30.687 21.964
+ J10 11.868 16.245 17.578
+ J11 6.56 6.591 13.379
+ J12 3.902 2.573 9.871
+ J13 2.147 0.819 6.098
+ J14 0.76 0.365 1.233
+ J15 0.36 0.181 1.226
+ J16 0.239 0.122 1.194
+ J17 0.451 0.554 1.991
+ J18 0.797 1.511 2.705
+ J19 0.492 1.178 0.84
+ J20 0.405 0.916 0.16
+ J21 0.959 1.409 0.164
+ J22 2.126 2.146 0.166
+ J23 1.144 0.84 0.077
+ J24 0.574 0.286 0.025
+ J25 0.588 0.301 0.282
+ J26 15.996 16.353 15.287
+ J27 15.535 16.669 14.029
+ J28 15.219 16.756 13.611
+ K02 3.453 1.498 2.025
+ K03 5.457 3.199 3.005
+ K04 8.24 6.666 4.026
+ K05 13.803 15.032 5.462
+ K06 22.068 29.259 7.331
+ K07 28.861 41.569 10.129
+ K08 30.536 43.905 20.585
+ K09 23.641 31.285 17.109
+ K10 15.215 16.368 13.852
+ K11 9.442 7.446 10.876
+ K12 6.523 3.691 8.576
+ K13 4.218 1.708 5.864
+ K14 0.11 0.08 0.178
+ K15 0.057 0.052 0.165
+ K16 0.056 0.05 0.163
+ K17 0.084 0.11 0.219
+ K18 0.12 0.202 0.266
+ K19 0.097 0.169 0.114
+ K20 0.092 0.152 0.055
+ K21 0.091 0.149 0.054
+ K22 0.176 0.204 0.055
+ K23 0.118 0.109 0.036
+ K24 0.083 0.058 0.024
+ K25 0.084 0.062 0.055
+ K26 13.207 14.81 13.496
+ K27 11.931 12.895 11.183
+ K28 11.35 11.769 9.683
+ L01 76.732 79.364 62.827
+ L02 7.664 3.259 1.993
+ L03 11.545 5.898 2.9
+ L04 15.727 10.146 3.785
+ L05 23.087 19.47 4.953
+ L06 33.342 35.063 6.642
+ L07 41.546 50.533 9.282
+ L08 43.151 53.11 19.221
+ L09 34.773 37.02 16.135
+ L10 24.286 20.62 13.122
+ L11 16.886 10.882 10.491
+ L12 12.652 6.416 8.353
+ L13 8.532 3.519 5.826
+ L14 76.675 79.226 62.699
+ L15 0.038 0.035 0.025
+ L16 21.288 22.639 19.359
+ L26 9.11 9.854 9.906
+ L27 9.204 10.05 8.65
+ L28 9.602 10.532 7.77
+ L29 0.04 0.037 0.028
+ M02 9.87 4.175 1.899
+ M03 15.719 7.67 2.788
+ M04 22.009 12.713 3.541
+ M05 32.688 23.723 4.613
+ M06 47.45 42.107 6.039
+ M07 64.575 66.511 9.359
+ M08 66.43 69.356 18.887
+ M09 48.955 44.07 15.064
+ M10 34.018 24.945 12.469
+ M11 23.221 13.444 9.974
+ M12 16.908 8.226 8.018
+ M13 10.7 4.424 5.556
+ M14 37.403 32.664 35.56
+ M15 32.819 30.306 35.072
+ M16 26.055 26.69 34.653
+ M17 31.517 36.319 38.023
+ M18 34.22 41.544 39.547
+ M19 33.174 40.732 33.899
+ M20 31.317 39.16 23.785
+ M21 39.198 43.977 23.638
+ M22 45.035 47.614 23.798
+ M23 41.489 41.406 22.88
+ M24 34.664 30.711 20.744
+ M25 36.343 31.891 30.185
+ M26 7.308 7.575 7.29
+ M27 7.054 7.689 6.661
+ M28 6.736 7.597 6.317
+ N02 9.564 4.071 0.433
+ N03 15.244 7.361 0.692
+ N04 21.49 12.159 0.981
+ N05 32.074 22.601 1.513
+ N06 46.699 40.175 2.375
+ N07 63.358 63.683 4.666
+ N08 61.976 59.689 1.924
+ N09 46.634 38.442 0.816
+ N10 32.085 21.804 0.415
+ N11 21.453 11.832 0.181
+ N12 15.04 7.149 0.074
+ N13 9.388 3.991 0.02
+ N14 35.079 32.892 32.747
+ N15 32.606 31.487 32.409
+ N16 28.131 29.186 32.205
+ N17 31.664 35.404 34.064
+ N18 33.404 38.542 34.958
+ N19 32.65 37.87 31.68
+ N20 31.542 36.992 25.464
+ N21 36.414 39.679 25.151
+ N22 39.129 41.364 25.394
+ N23 37.199 38.042 24.882
+ N24 33.221 31.488 23.357
+ N25 34.4 32.364 29.693
+ N26 5.531 6.401 6.067
+ N27 4.987 5.505 4.891
+ N28 4.581 4.801 4.063
+ O02 7.307 3.137 0.433
+ O03 11.063 5.574 0.707
+ O04 15.275 9.584 1.061
+ O05 22.42 18.23 1.628
+ O06 32.349 32.649 2.497
+ O07 40.54 47.841 4.313
+ O08 40.237 44.49 1.571
+ O09 32.794 30.944 0.812
+ O10 22.853 17.525 0.434
+ O11 15.44 9.303 0.194
+ O12 11.054 5.451 0.077
+ O13 7.224 3.094 0.021
+ O14 34.294 34.203 31.000
+ O15 33.121 33.542 30.87
+ O16 31.021 32.405 30.573
+ O17 32.709 35.229 31.551
+ O18 33.535 36.782 31.879
+ O19 33.11 36.372 30.011
+ O20 32.623 35.989 27.478
+ O21 34.678 37.087 27.421
+ O22 35.836 37.705 27.441
+ O23 35.015 36.226 27.209
+ O24 33.385 33.432 26.6
+ O25 34.104 34.07 29.423
+ O26 3.264 3.634 3.923
+ O27 3.356 3.715 3.454
+ O28 3.505 3.974 3.077
+ P02 3.123 1.387 0.434
+ P03 4.978 2.868 0.72
+ P04 7.69 6.024 1.097
+ P05 13.026 13.656 1.794
+ P06 20.866 26.635 2.733
+ P07 27.647 38.762 4.545
+ P08 26.873 34.908 1.482
+ P09 20.633 24.173 0.833
+ P10 13.01 12.581 0.452
+ P11 7.668 5.626 0.201
+ P12 4.955 2.744 0.083
+ P13 3.065 1.355 0.022
+ P14 11.506 6.825 13.879
+ P15 7.899 5.228 14.444
+ P16 5.052 4.19 17.555
+ P17 10.715 14.734 26.092
+ P18 15.327 24.08 30.67
+ P19 13.062 22.129 18.974
+ P20 10.514 18.873 5.22
+ P21 16.35 20.537 3.911
+ P22 21.802 22.81 3.581
+ P23 16.148 14.308 2.827
+ P24 9.332 5.626 1.729
+ P25 10.413 6.295 7.89
+ P26 2.481 2.647 2.748
+ P27 2.24 2.556 2.402
+ P28 2.085 2.463 2.166
+ Q02 1.092 0.522 0.456
+ Q03 2.16 1.621 0.814
+ Q04 4.314 4.723 1.353
+ Q05 8.882 12.573 2.169
+ Q06 15.507 24.675 3.276
+ Q07 19.737 32.052 4.983
+ Q08 18.787 28.048 1.414
+ Q09 15.056 21.662 0.911
+ Q10 8.8 11.263 0.541
+ Q11 4.288 4.253 0.241
+ Q12 2.144 1.488 0.093
+ Q13 1.037 0.489 0.025
+ Q14 9.661 6.986 11.373
+ Q15 7.814 6.216 12.03
+ Q16 5.87 5.682 14.411
+ Q17 9.735 12.66 18.551
+ Q18 12.576 18.175 20.725
+ Q19 11.486 17.281 14.793
+ Q20 9.913 15.513 6.281
+ Q21 13.03 15.869 5.096
+ Q22 15.405 16.598 4.712
+ Q23 12.441 11.949 4.157
+ Q24 8.247 6.088 3.129
+ Q25 9.056 6.64 7.831
+ Q26 1.635 1.969 1.965
+ Q27 1.464 1.693 1.616
+ Q28 1.358 1.499 1.326
+ R02 0.299 0.171 0.464
+ R03 0.8 0.952 0.9
+ R04 2.08 3.512 1.535
+ R05 5.297 10.401 2.45
+ R06 9.536 19.375 3.545
+ R07 12.138 24.003 5.201
+ R08 11.159 20.153 1.28
+ R09 9.046 16.391 0.89
+ R10 5.192 9.04 0.572
+ R11 2.019 3.032 0.264
+ R12 0.731 0.781 0.098
+ R13 0.242 0.135 0.024
+ R14 9.08 8.401 9.714
+ R15 8.492 8.242 10.094
+ R16 7.625 8.08 10.952
+ R17 9.408 11.104 12.207
+ R18 10.351 12.767 12.774
+ R19 9.979 12.455 10.779
+ R20 9.358 11.859 7.552
+ R21 10.401 11.969 6.926
+ R22 11.128 12.163 6.697
+ R23 10.211 10.67 6.45
+ R24 8.438 7.916 5.779
+ R25 8.908 8.293 8.294
+ R26 0.824 0.967 1.118
+ R27 0.892 1.044 0.995
+ R28 0.898 1.066 0.866
+ S02 0.108 0.076 0.463
+ S03 0.314 0.595 0.923
+ S04 0.895 2.271 1.497
+ S05 2.381 6.453 2.317
+ S06 4.54 12.112 3.23
+ S07 6 15.207 4.627
+ S08 5.164 12.038 1.021
+ S09 3.972 9.549 0.735
+ S10 2.102 5.137 0.479
+ S11 0.748 1.782 0.243
+ S12 0.201 0.412 0.099
+ S13 0.034 0.033 0.023
+ S14 1.725 1.253 2.271
+ S15 1.29 1.041 2.329
+ S16 0.733 0.725 2.351
+ S17 1.231 1.755 3.158
+ S18 1.614 2.609 3.588
+ S19 1.41 2.414 2.387
+ S20 1.173 2.065 0.872
+ S21 2.237 2.832 0.862
+ S22 2.94 3.185 0.824
+ S23 2.319 2.239 0.688
+ S24 1.415 1.023 0.463
+ S25 1.59 1.165 1.443
+ S26 0.517 0.577 0.634
+ S27 0.418 0.508 0.509
+ S28 0.389 0.49 0.474
+ T01 20.906 22.173 18.934
+ T14 1.673 1.457 2.146
+ T15 1.39 1.301 2.148
+ T16 0.95 1.044 2.135
+ T17 1.331 1.79 2.561
+ T18 1.571 2.278 2.786
+ T19 1.464 2.181 2.147
+ T20 1.302 1.983 1.137
+ T21 1.948 2.412 1.151
+ T22 2.312 2.604 1.125
+ T23 1.997 2.087 1.027
+ T24 1.446 1.262 0.839
+ T25 1.578 1.38 1.593
+ T26 0.226 0.308 0.356
+ T27 0.189 0.243 0.247
+ T28 0.177 0.205 0.205
+ T29 76.694 79.235 62.936
+ U01 0.04 0.037 0.026
+ U14 1.521 1.579 1.795
+ U15 1.449 1.541 1.83
+ U16 1.215 1.388 1.789
+ U17 1.372 1.691 1.882
+ U18 1.47 1.863 1.965
+ U19 1.436 1.833 1.767
+ U20 1.377 1.769 1.375
+ U21 1.604 1.908 1.358
+ U22 1.702 1.968 1.36
+ U23 1.61 1.806 1.34
+ U24 1.417 1.476 1.246
+ U25 1.493 1.552 1.626
+ U26 0.045 0.043 0.088
+ U27 0.044 0.048 0.036
+ U28 0.059 0.074 0.038
+ U29 0.042 0.039 0.029
+ V01 76.341 78.885 62.676
+ V14 21.197 22.447 19.277
+ V15 76.612 79.131 62.823
+ V26 0.039 0.036 0.025
+ V27 0.04 0.037 0.026
+ V28 0.039 0.037 0.026
+ V29 0.038 0.035 0.027
+
diff --git a/scanin/Jamfile b/scanin/Jamfile
new file mode 100644
index 0000000..b6e58f1
--- /dev/null
+++ b/scanin/Jamfile
@@ -0,0 +1,33 @@
+
+
+#PREF_CCFLAGS += $(CCOPTFLAG) ; # Turn optimisation on
+PREF_CCFLAGS += $(CCDEBUGFLAG) ; # Debugging flags
+PREF_LINKFLAGS += $(LINKDEBUGFLAG) ;
+
+#Products
+Libraries = libscanrd ;
+Executables = scanin ;
+Headers = scanrd.h ;
+Samples = it8.cht ColorChecker.cht ColorChecker.cie ColorCheckerDC.cht
+ ColorCheckerSG.cht Hutchcolor.cht i1_RGB_Scan_1.4.cht
+ ColorCheckerPassport.cht ColorCheckerPassport.cie
+ QPcard_201.cht QPcard_201.cie QPcard_202.cht QPcard_202.cie CMP_DT_003.cht
+ CMP_Digital_Target-3.cht CMP_Digital_Target-3.ti2 CMP_Digital_Target-3.cie
+ LaserSoftDCPro.cht SpyderChecker.cht SpyderChecker.cie ;
+
+#Install
+InstallBin $(DESTDIR)$(PREFIX)/bin : $(Executables) ;
+InstallFile $(DESTDIR)$(PREFIX)/$(REFSUBDIR) : $(Samples) ;
+#InstallFile $(DESTDIR)$(PREFIX)/h : $(Headers) ;
+#InstallLib $(DESTDIR)$(PREFIX)/lib : $(Libraries) ;
+
+# Chart recognition library
+Library libscanrd : scanrd.c : : : ../numlib ;
+
+# IT8 chart reader - sucks in tiff file and spits out cgats file
+Main scanin : scanin.c ;
+# ObjectHdrs scanin : ../h ../cgats ../numlib ../icc ../rspl ../gamut ../xicc $(TIFFINC) ;
+ObjectHdrs scanin : ../h ../numlib ../icc ../cgats ../rspl ../xicc ../gamut ../spectro $(TIFFINC) ;
+LinkLibraries scanin : libscanrd ../xicc/libxicc ../spectro/libinsttypes
+ ../gamut/libgamut ../rspl/librspl ../cgats/libcgats
+ ../icc/libicc ../numlib/libnum ../plot/libplot ../plot/libvrml $(TIFFLIB) $(JPEGLIB) ;
diff --git a/scanin/LaserSoftDCPro.cht b/scanin/LaserSoftDCPro.cht
new file mode 100644
index 0000000..e5df741
--- /dev/null
+++ b/scanin/LaserSoftDCPro.cht
@@ -0,0 +1,202 @@
+
+BOXES 141
+ F _ _ 26.09 18.7 239.15 18.7 239.15 171.9 26.09 171.9
+ D ALL ALL _ _ 263.95 185.5 0 0 0 0
+ Y 1 14 A J 12.675 12.75 27.5 20.0 15.175 15.25
+
+BOX_SHRINK 1.6
+
+XLIST 28
+ 27.5 1.0 1.0
+ 40.175 1.0 1.0
+ 42.675 1.0 1.0
+ 55.35 1.0 1.0
+ 57.85 1.0 1.0
+ 70.525 1.0 1.0
+ 73.025 1.0 1.0
+ 85.7 1.0 1.0
+ 88.2 1.0 1.0
+ 100.875 1.0 1.0
+ 103.375 1.0 1.0
+ 116.05 1.0 1.0
+ 118.55 1.0 1.0
+ 131.225 1.0 1.0
+ 133.725 1.0 1.0
+ 146.4 1.0 1.0
+ 148.9 1.0 1.0
+ 161.575 1.0 1.0
+ 164.075 1.0 1.0
+ 176.75 1.0 1.0
+ 179.25 1.0 1.0
+ 191.925 1.0 1.0
+ 194.425 1.0 1.0
+ 207.1 1.0 1.0
+ 209.6 1.0 1.0
+ 222.275 1.0 1.0
+ 224.775 1.0 1.0
+ 237.45 1.0 1.0
+
+YLIST 20
+ 20.0 1.0 1.0
+ 32.75 1.0 1.0
+ 35.25 1.0 1.0
+ 48.0 1.0 1.0
+ 50.5 1.0 1.0
+ 63.25 1.0 1.0
+ 65.75 1.0 1.0
+ 78.5 1.0 1.0
+ 81.0 1.0 1.0
+ 93.75 1.0 1.0
+ 96.25 1.0 1.0
+ 109.0 1.0 1.0
+ 111.5 1.0 1.0
+ 124.25 1.0 1.0
+ 126.75 1.0 1.0
+ 139.5 1.0 1.0
+ 142.0 1.0 1.0
+ 154.75 1.0 1.0
+ 157.25 1.0 1.0
+ 170.0 1.0 1.0
+
+EXPECTED XYZ 140
+ A1 95.054 99.997 108.93
+ A2 1.1997 1.1177 1.8435
+ A3 6.7205 6.2574 11.555
+ A4 95.054 99.997 108.93
+ A5 95.054 99.997 108.93
+ A6 95.054 99.997 108.93
+ A7 0.49927 0.43693 0.87678
+ A8 95.054 99.997 108.93
+ A9 95.054 99.997 108.93
+ A10 95.054 99.997 108.93
+ A11 95.054 99.997 108.93
+ A12 5.5560 5.1972 9.8224
+ A13 1.0432 0.94378 1.8118
+ A14 95.054 99.997 108.93
+ B1 95.054 99.997 108.93
+ B2 67.091 47.876 87.898
+ B3 11.506 12.701 30.786
+ B4 58.530 82.881 29.773
+ B5 51.017 26.596 49.038
+ B6 13.118 10.105 52.231
+ B7 81.415 94.542 37.089
+ B8 44.474 25.101 11.115
+ B9 21.340 12.736 95.969
+ B10 66.377 85.211 107.59
+ B11 42.717 22.847 6.6395
+ B12 95.054 99.997 108.93
+ B13 78.392 91.406 108.15
+ B14 95.054 99.997 108.93
+ C1 0.91819 0.83268 1.3889
+ C2 78.242 93.273 20.381
+ C3 39.945 21.012 96.586
+ C4 95.054 99.997 108.93
+ C5 8.0062 6.3371 5.5237
+ C6 95.054 99.997 108.93
+ C7 95.054 99.997 108.93
+ C8 95.054 99.997 108.93
+ C9 95.054 99.997 108.93
+ C10 26.464 19.481 13.094
+ C11 55.073 48.678 101.13
+ C12 77.003 92.778 13.853
+ C13 43.434 22.924 11.065
+ C14 0.78528 0.68981 1.5301
+ D1 5.7964 5.4769 9.7970
+ D2 72.996 88.624 107.90
+ D3 86.456 96.558 63.644
+ D4 62.452 84.629 42.335
+ D5 92.459 98.959 95.262
+ D6 35.935 71.602 11.974
+ D7 60.015 81.931 107.29
+ D8 59.633 29.164 97.123
+ D9 20.716 10.192 95.511
+ D10 95.054 99.997 108.93
+ D11 77.045 92.795 14.074
+ D12 43.764 31.444 13.884
+ D13 95.054 99.997 108.93
+ D14 5.4327 5.0906 9.4972
+ E1 95.054 99.997 108.93
+ E2 93.580 97.048 108.44
+ E3 95.054 99.997 108.93
+ E4 95.054 99.997 108.93
+ E5 95.054 99.997 108.93
+ E6 95.054 99.997 108.93
+ E7 0.56064 0.49941 0.85338
+ E8 95.054 99.997 108.93
+ E9 55.394 48.320 10.491
+ E10 95.054 99.997 108.93
+ E11 18.067 9.9172 10.525
+ E12 95.054 99.997 108.93
+ E13 51.153 77.999 78.081
+ E14 95.054 99.997 108.93
+ F1 95.054 99.997 108.93
+ F2 77.003 92.778 13.854
+ F3 95.054 99.997 108.93
+ F4 34.402 22.375 97.141
+ F5 95.054 99.997 108.93
+ F6 53.813 78.733 107.00
+ F7 95.054 99.997 108.93
+ F8 0.67207 0.58997 1.0828
+ F9 60.272 30.442 97.336
+ F10 52.097 40.728 12.472
+ F11 77.003 92.778 13.853
+ F12 95.054 99.997 108.93
+ F13 10.781 6.4490 18.321
+ F14 95.054 99.997 108.93
+ G1 95.054 99.997 108.93
+ G2 55.324 79.512 107.07
+ G3 95.054 99.997 108.93
+ G4 83.272 95.285 46.871
+ G5 58.118 49.046 25.961
+ G6 42.215 22.651 3.9825
+ G7 35.762 71.514 11.921
+ G8 19.846 9.7483 95.471
+ G9 77.003 92.778 13.853
+ G10 95.054 99.997 108.93
+ G11 77.003 92.778 13.853
+ G12 95.054 99.997 108.93
+ G13 60.898 82.386 107.33
+ G14 95.054 99.997 108.93
+ H1 0.54264 0.48279 0.79835
+ H2 89.532 88.954 107.09
+ H3 77.759 93.080 17.834
+ H4 42.861 43.829 100.84
+ H5 36.752 71.940 15.774
+ H6 95.054 99.997 108.93
+ H7 95.054 99.997 108.93
+ H8 95.054 99.997 108.93
+ H9 95.054 99.997 108.93
+ H10 53.813 78.733 107.00
+ H11 15.498 10.161 46.468
+ H12 54.711 27.778 69.401
+ H13 46.548 76.933 18.826
+ H14 0.62698 0.56386 0.99911
+ I1 95.054 99.997 108.93
+ I2 78.269 93.284 20.520
+ I3 79.484 91.969 108.20
+ I4 95.054 99.997 108.93
+ I5 7.1761 7.4119 9.1084
+ I6 68.605 86.360 107.69
+ I7 49.882 38.374 5.3364
+ I8 6.2853 7.7839 9.5291
+ I9 95.054 99.997 108.93
+ I10 71.643 79.666 19.448
+ I11 61.032 72.662 49.370
+ I12 54.931 79.309 107.05
+ I13 79.147 93.635 25.144
+ I14 95.054 99.997 108.93
+ J1 95.054 99.997 108.93
+ J2 0.42397 0.37158 0.62164
+ J3 95.054 99.997 108.93
+ J4 95.054 99.997 108.93
+ J5 95.054 99.997 108.93
+ J6 95.054 99.997 108.93
+ J7 95.054 99.997 108.93
+ J8 95.054 99.997 108.93
+ J9 94.564 99.017 108.77
+ J10 50.473 49.809 73.299
+ J11 17.292 16.601 26.910
+ J12 3.7657 3.5269 5.6717
+ J13 0.50304 0.44681 0.73017
+ J14 95.054 99.997 108.93
+
diff --git a/scanin/License.txt b/scanin/License.txt
new file mode 100644
index 0000000..a871fcf
--- /dev/null
+++ b/scanin/License.txt
@@ -0,0 +1,662 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<http://www.gnu.org/licenses/>.
+
diff --git a/scanin/Makefile.am b/scanin/Makefile.am
new file mode 100644
index 0000000..0e4f65a
--- /dev/null
+++ b/scanin/Makefile.am
@@ -0,0 +1,19 @@
+include $(top_srcdir)/Makefile.shared
+
+privatelib_LTLIBRARIES = libscanrd.la
+privatelibdir = $(pkglibdir)
+
+libscanrd_la_SOURCES = scanrd.h scanrd_.h scanrd.c
+libscanrd_la_LIBADD = ../rspl/librspl.la ../numlib/libargyllnum.la
+
+LDADD = ./libscanrd.la ../numlib/libargyllnum.la $(ICC_LIBS) \
+ ../cgats/libcgats.la ../xicc/libxicc.la $(TIFF_LIBS) \
+ ../libargyll.la
+
+bin_PROGRAMS = scanin
+
+refdir = $(datadir)/color/argyll/ref
+
+ref_DATA = $(wildcard *.cht) $(wildcard *.cie) $(wildcard *.ti2)
+
+EXTRA_DIST = $(ref_DATA) License.txt Readme.txt
diff --git a/scanin/QPcard_201.cht b/scanin/QPcard_201.cht
new file mode 100644
index 0000000..a9d9542
--- /dev/null
+++ b/scanin/QPcard_201.cht
@@ -0,0 +1,76 @@
+
+BOXES 31
+ F _ _ 0.0 0.0 1654.0 0.0 1654.0 478.0 0.0 478.0
+ D ALL ALL _ _ 1654.0 478.0 0.0 0.0 0 0
+ Y 1 10 A C 118.0 119.0 239.0 66.0 141.5 142.0
+
+BOX_SHRINK 15.0
+
+REF_ROTATION 0.0
+
+XLIST 22
+ 150.618062 0.144363 0.166667
+ 161.602137 0.245110 0.333333
+ 238.956434 0.946427 1.000000
+ 357.016882 0.855510 1.000000
+ 379.969304 0.948635 1.000000
+ 497.948383 0.927859 1.000000
+ 521.902072 1.000000 1.000000
+ 640.004059 0.922588 1.000000
+ 662.964760 0.948369 1.000000
+ 782.052769 0.951028 1.000000
+ 804.971146 0.886492 1.000000
+ 923.008950 0.922731 1.000000
+ 946.989395 0.865832 1.000000
+ 1066.007371 0.876333 1.000000
+ 1088.986522 0.865792 1.000000
+ 1208.017128 0.871015 1.000000
+ 1229.826796 0.868345 1.000000
+ 1349.181703 0.847707 1.000000
+ 1372.210921 0.767835 1.000000
+ 1490.553264 0.783286 1.000000
+ 1513.822380 0.891664 1.000000
+ 1633.171522 0.868381 1.000000
+
+YLIST 8
+ 65.941330 0.342831 1.000000
+ 184.978043 0.345580 1.000000
+ 207.991441 0.442638 1.000000
+ 325.524788 1.000000 1.000000
+ 348.940333 0.208095 1.000000
+ 366.421405 0.090483 0.647059
+ 378.396408 0.079155 0.617647
+ 466.955243 0.226972 0.000000
+
+EXPECTED XYZ 30
+ A1 47.8 50.5 53.2
+ A2 9.3 9.6 27.6
+ A3 60.7 62.1 11.2
+ A4 4.4 4.6 4.9
+ A5 10.5 11.1 11.9
+ A6 16.8 17.8 19.5
+ A7 32.9 34.7 36.8
+ A8 65.1 68.9 72.5
+ A9 81.8 86.6 91.0
+ A10 47.8 50.5 53.2
+ B1 36.9 43.1 7.0
+ B2 15.7 26.5 17.4
+ B3 29.8 19.5 20.8
+ B4 6.2 5.7 7.9
+ B5 10.1 12.3 6.8
+ B6 19.9 20.5 6.5
+ B7 11.0 9.9 6.8
+ B8 36.2 34.2 25.0
+ B9 60.7 67.5 73.7
+ B10 56.3 56.5 70.3
+ C1 47.8 50.5 53.2
+ C2 14.5 8.9 4.3
+ C3 18.6 24.8 52.5
+ C4 9.6 7.2 6.1
+ C5 18.0 19.8 34.8
+ C6 43.0 30.9 5.8
+ C7 57.0 49.6 6.6
+ C8 69.3 68.9 47.9
+ C9 64.5 69.9 59.9
+ C10 47.8 50.5 53.2
+
diff --git a/scanin/QPcard_201.cie b/scanin/QPcard_201.cie
new file mode 100644
index 0000000..ecd1a3a
--- /dev/null
+++ b/scanin/QPcard_201.cie
@@ -0,0 +1,42 @@
+IT8.7/2
+ORIGINATOR "Graeme Gill, ArgyllCMS"
+DESCRIPTOR "QPCard 201"
+CREATED "Nov 29, 2007"
+MANUFACTURER "QPcard AB"
+NUMBER_OF_FIELDS 4
+BEGIN_DATA_FORMAT
+SAMPLE_ID XYZ_X XYZ_Y XYZ_Z
+END_DATA_FORMAT
+NUMBER_OF_SETS 30
+BEGIN_DATA
+A1 47.8 50.5 53.2
+A2 9.3 9.6 27.6
+A3 60.7 62.1 11.2
+A4 4.4 4.6 4.9
+A5 10.5 11.1 11.9
+A6 16.8 17.8 19.5
+A7 32.9 34.7 36.8
+A8 65.1 68.9 72.5
+A9 81.8 86.6 91.0
+A10 47.8 50.5 53.2
+B1 36.9 43.1 7.0
+B2 15.7 26.5 17.4
+B3 29.8 19.5 20.8
+B4 6.2 5.7 7.9
+B5 10.1 12.3 6.8
+B6 19.9 20.5 6.5
+B7 11.0 9.9 6.8
+B8 36.2 34.2 25.0
+B9 60.7 67.5 73.7
+B10 56.3 56.5 70.3
+C1 47.8 50.5 53.2
+C2 14.5 8.9 4.3
+C3 18.6 24.8 52.5
+C4 9.6 7.2 6.1
+C5 18.0 19.8 34.8
+C6 43.0 30.9 5.8
+C7 57.0 49.6 6.6
+C8 69.3 68.9 47.9
+C9 64.5 69.9 59.9
+C10 47.8 50.5 53.2
+END_DATA
diff --git a/scanin/QPcard_202.cht b/scanin/QPcard_202.cht
new file mode 100644
index 0000000..4d10406
--- /dev/null
+++ b/scanin/QPcard_202.cht
@@ -0,0 +1,80 @@
+BOXES 36
+ F _ _ 22 20 638 22 638 440 20 440
+ D ALL ALL _ _ 655 455 0 0 0 0
+ Y 1 7 A E 68 68 45 30 83 83
+
+BOX_SHRINK 4.0
+
+REF_ROTATION 0.0
+
+XLIST 19
+ 43.608721 0.740781 0.800000
+ 114.976109 0.783190 0.800000
+ 126.951628 0.918228 0.900000
+ 198.016123 0.974824 0.900000
+ 210.073338 0.901194 1.000000
+ 281.139260 0.985920 1.000000
+ 294.215088 0.898482 1.000000
+ 364.783383 0.991485 1.000000
+ 377.849374 0.904129 1.000000
+ 448.984652 1.000000 1.000000
+ 460.979320 0.954860 1.000000
+ 532.024333 0.997190 1.000000
+ 544.239647 0.898460 1.000000
+ 615.407960 0.946305 1.000000
+ 631.910471 0.186078 0.000000
+ 636.058422 0.233922 0.000000
+ 642.588462 0.132382 0.000000
+ 643.533506 0.270593 0.000000
+ 644.296155 0.191706 0.000000
+
+YLIST 12
+ 26.695891 1.000000 0.875000
+ 98.238688 0.936512 1.000000
+ 110.194036 0.994019 1.000000
+ 181.442800 0.892945 1.000000
+ 194.417673 0.992089 0.937500
+ 264.518175 0.877113 0.875000
+ 277.701905 0.986234 1.000000
+ 347.941384 0.877209 1.000000
+ 360.406157 0.132648 0.312500
+ 361.033254 0.706971 0.875000
+ 431.983255 0.253427 0.750000
+ 432.622344 0.399964 0.000000
+
+EXPECTED XYZ 35
+A1 67.856459 69.512840 9.015609
+A2 67.076305 59.459332 7.827210
+A3 58.305183 45.461038 7.748888
+A4 52.785392 65.096997 59.594305
+A5 45.322673 55.739548 35.288307
+A6 58.323014 65.546227 28.470190
+A7 75.997301 76.369564 28.920869
+B1 27.185068 21.163573 31.661582
+B2 19.566090 10.470287 2.899649
+B3 7.735493 16.256731 8.259450
+B4 7.305847 10.044629 23.599855
+B5 38.157321 40.254508 6.905462
+B6 12.286120 18.850102 28.606420
+B7 70.618705 63.122300 27.332985
+C1 27.875888 20.161730 25.953035
+C2 21.562223 11.180011 4.672983
+C3 9.915698 19.077936 6.906382
+C4 9.400751 9.282240 29.346924
+C5 33.030305 38.085875 6.761389
+C6 12.379400 21.440342 24.610384
+C7 68.775747 59.666057 36.778798
+D1 30.312295 21.545103 20.899732
+D2 19.513673 10.562642 7.925905
+D3 9.957248 17.257178 4.623671
+D4 9.916827 8.308213 24.940996
+D5 24.060360 31.480055 7.467898
+D6 11.633929 19.652104 17.324887
+D7 64.052575 58.473280 56.362590
+E1 81.660435 84.707213 65.484182
+E2 61.845146 64.255877 51.551387
+E3 35.051455 36.358983 29.400605
+E4 22.483519 23.314508 19.344460
+E5 10.265340 10.652815 8.624928
+E6 5.291836 5.472343 4.344900
+E7 3.074245 3.153171 2.547055
diff --git a/scanin/QPcard_202.cie b/scanin/QPcard_202.cie
new file mode 100644
index 0000000..683b6c0
--- /dev/null
+++ b/scanin/QPcard_202.cie
@@ -0,0 +1,48 @@
+IT8.7/2
+ORIGINATOR "jose pereira jpereira.net"
+DESCRIPTOR "QPCARD 202"
+CREATED "may 2012"
+MANUFACTURER "QPcard"
+NUMBER_OF_FIELDS 7
+BEGIN_DATA_FORMAT
+SAMPLE_ID XYZ_X XYZ_Y XYZ_Z LAB_L LAB_A LAB_B
+END_DATA_FORMAT
+NUMBER_OF_SETS 35
+BEGIN_DATA
+A01 67.856459 69.512840 9.015609 86.757375 1.825575 81.545134
+A02 67.076305 59.459332 7.827210 81.543425 22.588491 76.956535
+A03 58.305183 45.461038 7.748888 73.194438 38.355857 62.867015
+A04 52.785392 65.096997 59.594305 84.533694 -24.307666 -6.124730
+A05 45.322673 55.739548 35.288307 79.465357 -22.724528 13.897787
+A06 58.323014 65.546227 28.470190 84.764423 -11.471673 33.441882
+A07 75.997301 76.369564 28.920869 90.030651 4.834878 41.785201
+B01 27.185068 21.163573 31.661582 53.128052 29.896273 -26.161046
+B02 19.566090 10.470287 2.899649 38.673581 58.158660 28.749194
+B03 7.735493 16.256731 8.259450 47.309526 -57.238786 16.283396
+B04 7.305847 10.044629 23.599855 37.922410 -20.845869 -38.815336
+B05 38.157321 40.254508 6.905462 69.650420 -2.091972 60.182723
+B06 12.286120 18.850102 28.606420 50.511369 -35.081968 -25.838514
+B07 70.618705 63.122300 27.332985 83.506686 21.791859 33.166706
+C01 27.875888 20.161730 25.953035 52.019576 37.428172 -18.752022
+C02 21.562223 11.180011 4.672983 39.882016 62.620005 19.536557
+C03 9.915698 19.077936 6.906382 50.778263 -53.582415 27.640637
+C04 9.400751 9.282240 29.346924 36.522120 3.739027 -51.160034
+C05 33.030305 38.085875 6.761389 68.083859 -12.577785 58.094494
+C06 12.379400 21.440342 24.610384 53.428091 -47.018893 -13.936155
+C07 68.775747 59.666057 36.778798 81.656338 25.812200 15.582830
+D01 30.312295 21.545103 20.899732 53.540986 40.234784 -6.655919
+D02 19.513673 10.562642 7.925905 38.833864 57.205173 2.937550
+D03 9.957248 17.257178 4.623671 48.582459 -43.791000 34.808467
+D04 9.916827 8.308213 24.940996 34.616704 16.088365 -46.965354
+D05 24.060360 31.480055 7.467898 62.910807 -25.347082 46.249115
+D06 11.633929 19.652104 17.324887 51.441570 -43.624665 -2.604415
+D07 64.052575 58.473280 56.362590 81.001208 18.165842 -8.910822
+E01 81.660435 84.707213 65.484182 93.756804 -0.027533 4.049779
+E02 61.845146 64.255877 51.551387 84.098816 -0.256360 1.591948
+E03 35.051455 36.358983 29.400605 66.793307 -0.019940 0.945311
+E04 22.483519 23.314508 19.344460 55.394826 0.016750 -0.239174
+E05 10.265340 10.652815 8.624928 38.989459 -0.046959 0.588338
+E06 5.291836 5.472343 4.344900 28.040182 0.184525 0.961005
+E07 3.074245 3.153171 2.547055 20.647174 0.585930 0.440283
+
+END_DATA
diff --git a/scanin/Readme.txt b/scanin/Readme.txt
new file mode 100644
index 0000000..2976595
--- /dev/null
+++ b/scanin/Readme.txt
@@ -0,0 +1,8 @@
+
+This directory contains the code to extract charts from
+TIFF scan files, and output the patch values using the
+CGATS file format. Typically this is used to get the
+patch information from a scan of an IT8 calibration
+chart.
+
+scanin.exe processes a TIFF file into a CGATS file.
diff --git a/scanin/SpyderChecker.cht b/scanin/SpyderChecker.cht
new file mode 100644
index 0000000..1c0ef3a
--- /dev/null
+++ b/scanin/SpyderChecker.cht
@@ -0,0 +1,115 @@
+BOXES 49
+ F _ _ 5 4 470 5 470 307 5 307
+
+ D ALL ALL _ _ 475 315 0 0 0 0
+
+ X A D 1 6 42 42 11 10 49 50
+ X E H 1 6 42 42 274 10 49 50
+
+
+BOX_SHRINK 3.0
+
+REF_ROTATION 0.0
+
+XLIST 27
+ 4.984973 0.109746 0.076923
+ 9.286927 0.952526 1.000000
+ 52.887948 0.925344 1.000000
+ 59.167435 1.000000 0.923077
+ 102.212773 0.458586 0.923077
+ 102.919173 0.494117 0.384615
+ 108.965439 0.995672 1.000000
+ 152.335524 0.929079 1.000000
+ 158.540776 0.956677 1.000000
+ 202.023990 0.948802 1.000000
+ 207.819679 0.348860 0.076923
+ 213.309915 0.948588 0.000000
+ 230.603930 0.364584 0.000000
+ 234.074857 0.305779 0.000000
+ 240.406366 0.419481 0.000000
+ 243.970119 0.780089 0.000000
+ 260.767091 0.913272 0.000000
+ 266.441147 0.117583 0.076923
+ 271.869223 0.913611 1.000000
+ 315.052562 0.921224 1.000000
+ 321.439873 0.976014 1.000000
+ 364.747788 0.940719 1.000000
+ 371.336194 0.968158 1.000000
+ 414.647393 0.874415 1.000000
+ 420.992026 0.882322 1.000000
+ 464.404335 0.944708 1.000000
+ 469.433263 0.121537 0.076923
+
+YLIST 22
+ 4.526138 1.000000 0.000000
+ 9.908960 0.796520 0.730769
+ 53.044496 0.103192 0.730769
+ 53.524920 0.636430 0.576923
+ 60.203792 0.830553 0.730769
+ 103.048172 0.307118 0.730769
+ 103.492169 0.530702 0.615385
+ 109.526018 0.312034 0.153846
+ 109.974584 0.523340 0.730769
+ 153.023131 0.597133 0.730769
+ 153.456869 0.213758 0.461538
+ 159.493872 0.317053 0.115385
+ 159.963940 0.430105 0.884615
+ 202.530321 0.801088 0.884615
+ 209.038198 0.511082 0.115385
+ 209.470794 0.321862 0.846154
+ 252.013363 0.513537 0.884615
+ 252.528335 0.297345 0.538462
+ 258.522376 0.203926 0.076923
+ 258.989151 0.557818 1.000000
+ 301.519003 0.098341 1.000000
+ 302.011325 0.695429 0.000000
+
+EXPECTED XYZ 48
+ A1 35.76 28.63 20.95
+ A2 45.15 47.41 16.97
+ A3 26.68 36.02 22.61
+ A4 24.43 30.51 49.72
+ A5 27.36 28.54 55.85
+ A6 33.51 26.53 34.35
+ B1 58.65 60.38 64.15
+ B2 55.22 59.80 62.04
+ B3 57.62 60.06 69.00
+ B4 5.28 5.14 5.28
+ B5 4.44 5.04 5.06
+ B6 5.12 5.19 6.86
+ C1 64.60 65.02 55.99
+ C2 45.46 45.77 29.03
+ C3 33.13 32.26 14.73
+ C4 15.61 13.92 7.09
+ C5 5.16 5.03 3.92
+ C6 4.01 4.17 4.76
+ D1 77.89 81.93 87.49
+ D2 70.10 73.80 78.79
+ D3 43.43 45.76 48.92
+ D4 24.24 25.51 27.51
+ D5 12.32 12.98 13.91
+ D6 5.22 5.50 6.02
+ E1 85.00 89.31 96.33
+ E2 54.09 56.94 61.35
+ E3 32.55 34.33 36.79
+ E4 17.72 18.68 20.09
+ E5 7.89 8.30 8.87
+ E6 2.65 2.78 3.12
+ F1 12.84 18.14 36.39
+ F2 28.20 18.34 29.84
+ F3 57.08 61.01 8.56
+ F4 19.63 11.29 4.87
+ F5 13.70 22.39 9.24
+ F6 7.32 5.35 26.41
+ G1 35.76 28.16 5.45
+ G2 12.71 11.07 36.60
+ G3 27.74 18.51 13.48
+ G4 9.34 6.93 16.38
+ G5 33.22 43.77 10.98
+ G6 46.49 42.42 7.53
+ H1 30.35 42.13 42.95
+ H2 24.45 23.05 44.34
+ H3 9.73 12.51 6.59
+ H4 17.28 18.47 34.69
+ H5 36.71 34.18 24.66
+ H6 10.43 9.32 6.27
diff --git a/scanin/SpyderChecker.cie b/scanin/SpyderChecker.cie
new file mode 100644
index 0000000..302e416
--- /dev/null
+++ b/scanin/SpyderChecker.cie
@@ -0,0 +1,62 @@
+IT8.7/2
+ORIGINATOR "Jose Pereira"
+DESCRIPTOR "Datacolor SpyderCheckr (D65)"
+CREATED "dec, 19. 2011"
+MANUFACTURER "DataColor"
+
+NUMBER_OF_FIELDS 7
+BEGIN_DATA_FORMAT
+SAMPLE_ID XYZ_X XYZ_Y XYZ_Z
+END_DATA_FORMAT
+
+NUMBER_OF_SETS 48
+BEGIN_DATA
+A1 35.76 28.63 20.95
+A2 45.15 47.41 16.97
+A3 26.68 36.02 22.61
+A4 24.43 30.51 49.72
+A5 27.36 28.54 55.85
+A6 33.51 26.53 34.35
+B1 58.65 60.38 64.15
+B2 55.22 59.80 62.04
+B3 57.62 60.06 69.00
+B4 5.28 5.14 5.28
+B5 4.44 5.04 5.06
+B6 5.12 5.19 6.86
+C1 64.60 65.02 55.99
+C2 45.46 45.77 29.03
+C3 33.13 32.26 14.73
+C4 15.61 13.92 7.09
+C5 5.16 5.03 3.92
+C6 4.01 4.17 4.76
+D1 77.89 81.93 87.49
+D2 70.10 73.80 78.79
+D3 43.43 45.76 48.92
+D4 24.24 25.51 27.51
+D5 12.32 12.98 13.91
+D6 5.22 5.50 6.02
+E1 85.00 89.31 96.33
+E2 54.09 56.94 61.35
+E3 32.55 34.33 36.79
+E4 17.72 18.68 20.09
+E5 7.89 8.30 8.87
+E6 2.65 2.78 3.12
+F1 12.84 18.14 36.39
+F2 28.20 18.34 29.84
+F3 57.08 61.01 8.56
+F4 19.63 11.29 4.87
+F5 13.70 22.39 9.24
+F6 7.32 5.35 26.41
+G1 35.76 28.16 5.45
+G2 12.71 11.07 36.60
+G3 27.74 18.51 13.48
+G4 9.34 6.93 16.38
+G5 33.22 43.77 10.98
+G6 46.49 42.42 7.53
+H1 30.35 42.13 42.95
+H2 24.45 23.05 44.34
+H3 9.73 12.51 6.59
+H4 17.28 18.47 34.69
+H5 36.71 34.18 24.66
+H6 10.43 9.32 6.27
+END_DATA
diff --git a/scanin/afiles b/scanin/afiles
new file mode 100644
index 0000000..bc4d9c7
--- /dev/null
+++ b/scanin/afiles
@@ -0,0 +1,28 @@
+Readme.txt
+License.txt
+afiles
+Jamfile
+scanin.c
+scanrd.c
+scanrd.h
+scanrd_.h
+it8.cht
+ColorChecker.cht
+ColorChecker.cie
+ColorCheckerDC.cht
+ColorCheckerSG.cht
+Hutchcolor.cht
+i1_RGB_Scan_1.4.cht
+ColorCheckerPassport.cht
+ColorCheckerPassport.cie
+QPcard_201.cht
+QPcard_201.cie
+QPcard_202.cht
+QPcard_202.cie
+CMP_DT_003.cht
+CMP_Digital_Target-3.cht
+CMP_Digital_Target-3.ti2
+CMP_Digital_Target-3.cie
+LaserSoftDCPro.cht
+SpyderChecker.cht
+SpyderChecker.cie
diff --git a/scanin/i1_RGB_Scan_1.4.cht b/scanin/i1_RGB_Scan_1.4.cht
new file mode 100644
index 0000000..3a3d7c9
--- /dev/null
+++ b/scanin/i1_RGB_Scan_1.4.cht
@@ -0,0 +1,341 @@
+
+BOXES 289
+ F _ _ 49 33 662 33 662 466 49 466
+ D ALL ALL _ _ 613 433 49.0 33.0 0 0
+ X A R 1 16 34.0 27.0 49.0 33.0 34.0 27.0
+
+BOX_SHRINK 3.3
+
+REF_ROTATION 0.0
+
+XLIST 19
+ 49.0 1.0 1.0
+ 83.0 1.0 1.0
+ 117.0 1.0 1.0
+ 151.0 1.0 1.0
+ 185.0 1.0 1.0
+ 219.0 1.0 1.0
+ 253.0 1.0 1.0
+ 287.0 1.0 1.0
+ 321.0 1.0 1.0
+ 355.0 1.0 1.0
+ 389.0 1.0 1.0
+ 423.0 1.0 1.0
+ 457.0 1.0 1.0
+ 491.0 1.0 1.0
+ 525.0 1.0 1.0
+ 559.0 1.0 1.0
+ 593.0 1.0 1.0
+ 627.0 1.0 1.0
+ 662.0 1.0 1.0
+
+YLIST 17
+ 33.0 1.0 1.0
+ 60.0 1.0 1.0
+ 87.0 1.0 1.0
+ 114.0 1.0 1.0
+ 141.0 1.0 1.0
+ 168.0 1.0 1.0
+ 195.0 1.0 1.0
+ 222.0 1.0 1.0
+ 249.0 1.0 1.0
+ 276.0 1.0 1.0
+ 303.0 1.0 1.0
+ 330.0 1.0 1.0
+ 357.0 1.0 1.0
+ 384.0 1.0 1.0
+ 411.0 1.0 1.0
+ 439.0 1.0 1.0
+ 466.0 1.0 1.0
+
+EXPECTED XYZ 288
+ A1 0.52 0.59 0.56
+ A2 10.27 10 32.62
+ A3 13.89 7.3 2.84
+ A4 33.34 23.69 4.75
+ A5 31.79 33.22 28.61
+ A6 13.85 14.51 12.72
+ A7 35.52 25.53 10.91
+ A8 27.16 27.04 25.26
+ A9 6.12 6.42 5.76
+ A10 6.04 6.8 6.75
+ A11 4.4 3.47 20.55
+ A12 6.34 3.41 11.34
+ A13 9.03 7.05 14.88
+ A14 7.91 6.52 8.41
+ A15 16.18 10.77 24.37
+ A16 1.97 1.1 8.66
+ B1 27.55 34.83 6.23
+ B2 17.94 11.4 1.02
+ B3 34.87 35.19 5.79
+ B4 13.05 19.54 29.87
+ B5 6.88 9.15 27.74
+ B6 70.59 73.69 44.02
+ B7 2.04 2.19 1.41
+ B8 1.35 2.84 0.94
+ B9 49.51 40.62 36.32
+ B10 43.26 35.61 12.01
+ B11 2.65 3.13 3.25
+ B12 17.77 24.45 21.08
+ B13 8.51 14.13 31.85
+ B14 7.84 11.93 30.12
+ B15 5.81 12.04 16.38
+ B16 11.49 8.06 0.99
+ C1 34.5 19.81 37.1
+ C2 42.27 43.99 33.47
+ C3 4.36 7.54 14.12
+ C4 58.73 57.76 6.5
+ C5 11.22 5.5 17.82
+ C6 1.55 1.89 2.09
+ C7 37.5 39.03 33.61
+ C8 23.67 18.54 4.44
+ C9 8.12 14.07 4.81
+ C10 2.9 7.38 1.41
+ C11 34.09 35.82 26.91
+ C12 9.45 9.34 9.05
+ C13 41.11 33.34 5.29
+ C14 27.38 21.65 19.2
+ C15 27.38 28.66 24.61
+ C16 48.58 49.16 58.53
+ D1 7.47 12.02 1.86
+ D2 17.27 8.47 18.91
+ D3 24.86 14.78 0.99
+ D4 8.61 4.81 2.74
+ D5 28.85 26.03 5.07
+ D6 20.34 26.75 33.64
+ D7 5.39 3.09 6.34
+ D8 7.42 3.7 17.12
+ D9 60.57 66.88 64.61
+ D10 4.42 4.33 4.38
+ D11 2.63 2.73 10.91
+ D12 46.48 54.19 25.5
+ D13 1.24 2.02 2.92
+ D14 2.43 5.58 3.81
+ D15 4.46 2.69 0.72
+ D16 23.58 11.3 19.67
+ E1 18.33 16.26 18.07
+ E2 13.25 17.2 1.73
+ E3 16.1 22.52 11.75
+ E4 50.7 52.96 40.49
+ E5 0.93 1.03 0.98
+ E6 2.58 1.28 12.24
+ E7 61.11 63.67 49.46
+ E8 9.69 16.36 11.19
+ E9 19.64 20.4 5.14
+ E10 68.63 71.55 34.47
+ E11 33.86 33.75 30.83
+ E12 18.65 9.44 0.83
+ E13 12.88 22.5 20.49
+ E14 40.57 35.97 51.8
+ E15 25.89 26.22 33.39
+ E16 30.29 39.21 13.78
+ F1 3.2 7.65 4.38
+ F2 3.71 3.35 16.24
+ F3 23.31 14.61 25.55
+ F4 20.14 13.3 9.05
+ F5 19.65 21.55 20.22
+ F6 1.21 1.33 0.75
+ F7 5.8 8.65 21.51
+ F8 20.82 10.37 7.25
+ F9 12.98 6.05 24.77
+ F10 15.96 14.23 4.62
+ F11 54.03 50.45 2.31
+ F12 23.38 24.4 20.86
+ F13 4.42 5.8 1.14
+ F14 17.42 25.87 5.81
+ F15 1.8 3.63 3.4
+ F16 12.33 9.08 3.65
+ G1 41.01 28.98 32.18
+ G2 14.7 24.18 31.92
+ G3 4.63 5.68 19.04
+ G4 73.8 76.63 58.3
+ G5 25.16 11.63 27.13
+ G6 23.64 27.52 5.74
+ G7 40.74 42.65 24.74
+ G8 5.04 6.8 4
+ G9 15.6 21.51 42.53
+ G10 47.5 50.89 46.6
+ G11 11.95 13.23 12.63
+ G12 0.74 0.68 2.21
+ G13 20.56 17.42 28.91
+ G14 3.34 3.61 2.24
+ G15 15.88 7.93 12.58
+ G16 32.76 42.79 24.95
+ H1 1.99 1.85 1.96
+ H2 5.23 4.73 13.17
+ H3 35.1 45.5 39.54
+ H4 15.44 16.22 14.02
+ H5 18.85 19.72 16.96
+ H6 19.88 10.01 3.06
+ H7 12.31 8.36 31.05
+ H8 37.11 33.66 35.77
+ H9 31.4 29.01 12.25
+ H10 5.92 7.65 8.81
+ H11 65.82 68.12 58.73
+ H12 15.16 18.34 19.38
+ H13 14.99 12.73 1.65
+ H14 56.71 60.58 54.75
+ H15 9.34 13.22 17.86
+ H16 14.91 7.72 6.98
+ I1 25.37 20.12 10.52
+ I2 49.34 51.3 44.11
+ I3 6.63 6.47 6.58
+ I4 1.07 0.94 1.24
+ I5 8.34 4.69 0.92
+ I6 23.16 18.65 41.02
+ I7 6.72 10.56 4.53
+ I8 27.97 23.98 1.71
+ I9 3.27 1.46 15.75
+ I10 55.74 51.88 24.53
+ I11 7.7 11.77 9.88
+ I12 1.57 1.7 1.49
+ I13 28.92 28.09 47.66
+ I14 28.27 17.22 9.16
+ I15 72.13 75.17 50.99
+ I16 3.58 5.09 12.93
+ J1 13.14 15.8 38.03
+ J2 9.81 17.93 5.25
+ J3 68.6 65.82 63.75
+ J4 43.83 30.19 45.87
+ J5 18.39 8.6 26.07
+ J6 5.36 10.17 15.84
+ J7 55.67 57.15 61.71
+ J8 17.35 19.86 30.55
+ J9 9.34 5.12 6.56
+ J10 4.06 4.65 4.46
+ J11 59.67 55.56 40.66
+ J12 49.63 49.42 45.4
+ J13 41.62 45.81 6.35
+ J14 4.44 4.57 7.21
+ J15 12.73 12.57 11.83
+ J16 33.9 31.33 21.78
+ K1 39.02 40.41 2.14
+ K2 6.59 5.47 20
+ K3 11 14.42 27.43
+ K4 1.86 2.4 6.45
+ K5 18.68 28.5 12.59
+ K6 75.76 78.23 67.4
+ K7 7.32 7.87 5.36
+ K8 18.49 18.14 1.64
+ K9 37.54 38.95 13.07
+ K10 55.35 62.8 63.8
+ K11 8.76 9.83 9.34
+ K12 8.38 8.86 7.56
+ K13 21.69 14.04 16.27
+ K14 16.19 22.43 2.19
+ K15 62.66 63.7 14.84
+ K16 7.22 13.33 24.18
+ L1 4.75 2.89 2.62
+ L2 38.93 48.68 58.9
+ L3 65.92 71 57.4
+ L4 66.52 69 26.91
+ L5 15.55 13.99 36.92
+ L6 17.18 15.48 10.42
+ L7 51.65 59.75 43.16
+ L8 18.25 11.8 33.47
+ L9 3.47 3.71 3.23
+ L10 12.9 9.35 8.48
+ L11 64.65 59.55 61.46
+ L12 45.13 38.69 1.96
+ L13 6.43 10.92 22.75
+ L14 21.35 21.35 19.83
+ L15 33.05 24.65 43.94
+ L16 22.66 23.84 17.58
+ M1 65.11 63.01 48.58
+ M2 1.94 1.46 0.65
+ M3 22.58 16.86 1.28
+ M4 10.28 5.41 11.55
+ M5 11.02 20.07 11.6
+ M6 59.78 59.84 54.76
+ M7 2.71 4.6 7.83
+ M8 23.91 34.53 36.93
+ M9 30.27 18.26 16.91
+ M10 26.5 36.45 52.65
+ M11 13.33 16.63 10.94
+ M12 4.04 8.83 9.08
+ M13 5.17 5.59 5.05
+ M14 3.61 9.2 4.51
+ M15 10.14 10.8 7.78
+ M16 3.13 3.26 0.84
+ N1 3.29 6.6 8.25
+ N2 4.65 10.78 9.31
+ N3 30.72 33.49 30.48
+ N4 10.72 12.58 1.45
+ N5 10.17 7.54 21.82
+ N6 11.18 18 19.3
+ N7 53.3 49.34 13.79
+ N8 7.14 6.18 3.7
+ N9 7.18 8.54 15.43
+ N10 10.26 10.86 9.44
+ N11 64.17 66.15 63.94
+ N12 29.82 22.94 30.75
+ N13 41.27 41.14 38.01
+ N14 13.72 13.09 26.73
+ N15 67.54 72.16 65.85
+ N16 59.57 51.66 57.98
+ O1 13.5 7.07 0.76
+ O2 49.21 44.05 5.87
+ O3 18.71 12.3 3.74
+ O4 38.42 41.38 37.84
+ O5 14.29 19.83 5.34
+ O6 38.12 27.36 19.84
+ O7 0.82 1.5 0.8
+ O8 58.19 65.09 49.46
+ O9 22.03 24.06 1.89
+ O10 54.38 44.04 54.64
+ O11 17.33 18.31 13.39
+ O12 3.07 2.92 2.94
+ O13 8.57 8.65 1.33
+ O14 2.04 4.54 1.45
+ O15 5.58 2.53 20.51
+ O16 9.55 10.01 4.38
+ P1 27.28 32.46 22.7
+ P2 1.26 0.88 4.93
+ P3 33.17 37.11 53.34
+ P4 8.31 3.84 22.57
+ P5 0.65 0.9 1.06
+ P6 3.58 1.97 10.4
+ P7 17.53 26.57 45.79
+ P8 38.54 30.11 1.74
+ P9 70.01 70.01 56.41
+ P10 45.98 54.96 61.34
+ P11 5.76 6.4 24.4
+ P12 27.4 28.95 21.64
+ P13 8.91 9.56 24.29
+ P14 14.32 10.16 15.55
+ P15 44.13 50.38 14.14
+ P16 25.91 15.77 35.63
+ Q1 7.89 5.75 27.26
+ Q2 19.45 21.15 43.11
+ Q3 2.18 1.59 2.48
+ Q4 2.47 2.57 2.36
+ Q5 71.98 71.6 65.38
+ Q6 25.4 27.72 25.57
+ Q7 46.78 38.92 22.52
+ Q8 30.85 35.91 37
+ Q9 9.11 15.52 1.94
+ Q10 11.65 11.86 16.65
+ Q11 20.6 21.96 11.36
+ Q12 2.49 6.09 1.38
+ Q13 43.66 45.37 39.11
+ Q14 56.73 58.92 50.71
+ Q15 13.37 14.14 10.18
+ Q16 25.67 30.42 12.63
+ R1 11.96 14.98 5.06
+ R2 25.55 30.36 2.16
+ R3 20.96 31.53 22.46
+ R4 26.63 16.16 3.75
+ R5 15.45 17.15 15.99
+ R6 2.87 1.83 5.87
+ R7 5.94 8.9 1.32
+ R8 22.2 10.8 12.85
+ R9 32.45 19.2 26.87
+ R10 31.17 21.44 1.48
+ R11 4.71 2.36 15.65
+ R12 16.81 16.68 15.67
+ R13 33.39 31.69 1.87
+ R14 6.67 5.46 0.93
+ R15 23.87 29.65 49.1
+ R16 5.1 5.43 3.45
+
+
diff --git a/scanin/it8.cht b/scanin/it8.cht
new file mode 100644
index 0000000..f1e256d
--- /dev/null
+++ b/scanin/it8.cht
@@ -0,0 +1,338 @@
+
+
+BOXES 290
+ F _ _ 1 1 616.0 1.5 615.5 358 1 358.5
+ D ALL ALL _ _ 615 409 1 1 0 0
+ D MARK MARK _ _ 14 14 1 1 0 0
+ Y 01 22 A L 25.625 25.625 26.625 26.625 25.625 25.625
+ X GS00 GS23 _ _ 25.625 51.25 0.0 358.75 25.625 0.0
+
+BOX_SHRINK 3.2
+
+REF_ROTATION -0.002006
+
+XLIST 32
+ 1.799625 1.000000 0.312500
+ 27.064987 0.874039 0.750000
+ 52.592403 0.133439 0.687500
+ 78.196610 0.264191 0.687500
+ 104.117756 0.165427 0.937500
+ 129.377994 0.844432 0.937500
+ 155.144274 0.501218 0.875000
+ 180.839181 0.491428 0.937500
+ 206.359758 0.212384 0.937500
+ 232.038808 0.851851 0.937500
+ 257.854725 0.162956 0.625000
+ 283.552463 0.101243 0.812500
+ 300.534000 0.024750 0.812500
+ 309.507688 0.093829 1.000000
+ 334.711314 0.856821 1.000000
+ 360.428194 0.787677 1.000000
+ 385.849730 0.748130 0.937500
+ 386.650071 0.039487 0.687500
+ 394.630372 0.024725 0.687500
+ 411.835654 0.802501 0.750000
+ 414.017731 0.041974 0.937500
+ 437.133504 0.674062 0.937500
+ 437.975355 0.103714 1.000000
+ 462.938460 0.671643 1.000000
+ 463.880560 0.093836 0.937500
+ 488.517995 0.679022 1.000000
+ 514.338544 0.760511 1.000000
+ 540.037492 0.111108 0.625000
+ 565.856396 0.133330 0.562500
+ 591.114717 0.565475 0.562500
+ 603.447516 0.032097 0.312500
+ 615.984915 0.829608 0.250000
+
+YLIST 22
+ 2.477956 0.993407 0.142857
+ 12.988903 0.016393 0.190476
+ 14.739109 0.036082 0.190476
+ 26.746171 0.911487 0.428571
+ 52.537114 0.303282 0.904762
+ 78.060317 0.585303 0.857143
+ 103.498271 0.606862 0.761905
+ 128.994535 0.567266 0.761905
+ 154.483041 0.550814 0.714286
+ 179.935985 0.623055 0.666667
+ 205.552940 0.350826 0.714286
+ 212.051372 0.016393 0.714286
+ 231.153547 0.824618 0.857143
+ 256.697418 0.744268 0.952381
+ 282.145841 0.736126 0.904762
+ 307.899015 0.536075 0.952381
+ 333.262903 0.903282 0.809524
+ 340.217754 0.019722 0.190476
+ 344.988867 0.019671 0.095238
+ 346.988885 0.018032 0.095238
+ 358.840278 0.999967 1.000000
+ 409.201393 1.000000 0.000000
+
+EXPECTED XYZ 264
+ A01 3.85 3.22 1.9
+ A02 4.89 3.27 1.6
+ A03 5.87 3.31 1.33
+ A04 6.3 3.38 1.19
+ A05 13.01 11.44 7.64
+ A06 16.14 11.99 6.81
+ A07 19.35 12.41 6.06
+ A08 20.41 11.97 5.3
+ A09 43.5 42.81 32.65
+ A10 45.58 42.37 30.95
+ A11 48.99 43.2 29.9
+ A12 50.73 44.02 29.96
+ A13 74.46 78.76 66.06
+ A14 75.66 76.42 64.08
+ A15 78.36 81.34 65.41
+ A16 70.52 73.3 59.16
+ A17 74.98 75.98 60.69
+ A18 72.85 77.3 60.25
+ A19 73.09 75.52 64.54
+ B01 3.47 3.08 1.41
+ B02 4.41 3.25 0.9
+ B03 5.04 3.23 0.58
+ B04 5.19 3.11 0.47
+ B05 13.36 11.59 5.56
+ B06 15.97 12.03 3.69
+ B07 19.2 12.49 2.2
+ B08 19.73 11.52 1.17
+ B09 42.19 41.84 29.34
+ B10 44.83 42.17 25.93
+ B11 48.06 42.9 23.01
+ B12 49.63 43.08 21.34
+ B13 66.21 72.54 64.61
+ B14 70.16 67.1 60.33
+ B15 75.46 78.69 51.58
+ B16 57.47 59.58 47.66
+ B17 68.33 66.45 49.05
+ B18 63.89 70.29 51.3
+ B19 61.12 62.16 59.79
+ C01 4.97 4.75 1.98
+ C02 5.18 4.65 1.23
+ C03 5.51 4.58 0.71
+ C04 5.77 4.61 0.67
+ C05 24.57 23.44 10.14
+ C06 28.1 24.64 5.22
+ C07 31.15 25.28 2.2
+ C08 30.85 23.68 1.35
+ C09 49.16 49.36 32.37
+ C10 51.72 50.72 26.53
+ C11 55.24 53.14 21.93
+ C12 56.87 53.62 18.46
+ C13 57.68 65.65 62.7
+ C14 63.46 56.66 55.49
+ C15 73 76.11 40.78
+ C16 44.73 46.38 36.8
+ C17 60.64 55.73 38.1
+ C18 52.15 60.27 41.5
+ C19 48.13 49.18 54.38
+ D01 4.19 4.41 1.93
+ D02 4.48 4.72 1.24
+ D03 4.55 4.78 0.8
+ D04 4.32 4.53 0.78
+ D05 27.33 28.55 12.95
+ D06 28.68 30.04 7.25
+ D07 29.51 31.01 3.41
+ D08 27.55 28.44 1.83
+ D09 56.06 58.19 38.21
+ D10 56.03 58.46 30.02
+ D11 56.2 59.33 24.44
+ D12 56.19 59.41 19.14
+ D13 48.21 57.42 59.53
+ D14 58.18 49.14 51.36
+ D15 70.98 73.73 33.63
+ D16 34.31 35.73 28.22
+ D17 54.27 47.53 29.58
+ D18 41.67 50.64 32.28
+ D19 36.95 37.82 48.09
+ E01 4.15 4.75 2.03
+ E02 4 4.98 1.37
+ E03 3.3 4.49 0.86
+ E04 3.11 4.3 0.86
+ E05 13.11 14.9 7.06
+ E06 12.26 15.23 4.18
+ E07 11.53 15.57 2.27
+ E08 9.69 13.74 1.51
+ E09 39.15 42.08 27.33
+ E10 37.43 41.51 22.23
+ E11 36.99 42.5 18.85
+ E12 36.4 42.58 16.27
+ E13 39.97 49.81 56.15
+ E14 52.08 41.07 46.36
+ E15 68.71 70.76 26.45
+ E16 25.7 26.97 21.28
+ E17 48.53 40.6 22
+ E18 31.62 40.82 23.35
+ E19 31.19 31.19 43.4
+ F01 1.51 1.91 1.06
+ F02 1.29 2.04 0.98
+ F03 1.16 2.09 0.82
+ F04 1.14 2.04 0.8
+ F05 6.53 8.25 5.13
+ F06 5.61 8.66 4.38
+ F07 4.6 8.77 3.7
+ F08 3.45 7.63 2.78
+ F09 37.8 41.07 30.91
+ F10 35.92 40.76 29.03
+ F11 35.42 41.99 29.07
+ F12 34 41.8 28
+ F13 32.13 42.12 51.99
+ F14 45.72 33.34 40.77
+ F15 66.26 67.29 19.65
+ F16 17.02 18.07 14.4
+ F17 41.59 32.53 15.16
+ F18 26.26 35.26 18.81
+ F19 24.3 23.6 37.48
+ G01 2.31 3 2.27
+ G02 2 3.21 2.58
+ G03 1.66 3.21 2.75
+ G04 1.58 3.03 2.6
+ G05 8.99 11.08 8.79
+ G06 7.68 11.3 9.56
+ G07 6.52 11.5 10.2
+ G08 5.5 10.85 10.55
+ G09 38.29 41.75 33.45
+ G10 35.83 41.16 34.11
+ G11 34.56 41.83 35.63
+ G12 33.69 42.14 36.7
+ G13 25.95 35.68 48
+ G14 40.6 27.62 36.14
+ G15 63.72 63.63 14.35
+ G16 10.85 11.82 9.58
+ G17 37.23 27.64 11.62
+ G18 20.28 28.97 14.15
+ G19 17.7 16.74 31.7
+ H01 2.56 3.04 2.92
+ H02 2.34 3.2 4.12
+ H03 2.12 3.28 5.43
+ H04 2.06 3.18 5.29
+ H05 10.07 11.6 11.24
+ H06 9.01 11.68 14.81
+ H07 8.22 12 19.42
+ H08 7.25 11.55 21.45
+ H09 39.25 42.31 36.81
+ H10 37.58 41.85 40.37
+ H11 37.16 43.07 45.79
+ H12 36.27 43.78 49.47
+ H13 21.47 30.78 44.22
+ H14 36.49 23.35 32.38
+ H15 61.58 60.55 10.95
+ H16 8.21 8.71 6.91
+ H17 33.04 23.26 8.38
+ H18 16.22 24.35 10.41
+ H19 12.86 11.84 26.82
+ I01 4.22 4.44 5.28
+ I02 4.35 4.48 8.36
+ I03 4.4 4.44 11.94
+ I04 4.48 4.58 12.17
+ I05 15.15 15.78 15.23
+ I06 14.56 15.12 19.52
+ I07 14.37 14.81 24.48
+ I08 14.11 14.76 30.03
+ I09 41.03 42.58 36.94
+ I10 40.85 42.23 40.73
+ I11 40.86 42.33 45.05
+ I12 41.31 42.73 47.77
+ I13 17.26 25.93 40.23
+ I14 32.66 19.63 28.81
+ I15 59.37 57.18 7.79
+ I16 4.97 5.32 4.32
+ I17 28.62 18.88 5.48
+ I18 11.58 18.98 7.25
+ I19 9.58 8.34 22.87
+ I20 0.45 0.4 0.33
+ I21 2.28 1.78 0.98
+ I22 2.37 1.95 0.85
+ J01 2.15 1.9 2.6
+ J02 2.57 2 4.72
+ J03 2.93 1.95 8.1
+ J04 3.15 1.92 10.76
+ J05 11.73 11.6 11.81
+ J06 12.98 11.93 16.19
+ J07 13.91 12.07 20.95
+ J08 14.01 11.59 24.35
+ J09 40.75 41.22 36.34
+ J10 41.26 41.07 39.74
+ J11 42.63 41.68 44.51
+ J12 44.02 41.78 49.25
+ J13 13.82 21.69 35.98
+ J14 28.87 16.33 25.08
+ J15 56.04 52.29 4.97
+ J16 2.46 2.63 2.29
+ J17 24.04 14.75 3.15
+ J18 8.12 14.49 4.55
+ J19 5.98 4.79 17.76
+ J20 8.26 5.37 1.04
+ J21 11.52 7.81 1.62
+ J22 14.67 10.72 2.6
+ K01 5.63 4.7 4.86
+ K02 6.74 4.58 7.23
+ K03 8.04 4.48 9.73
+ K04 9.39 4.76 11.79
+ K05 16.66 15.39 14.44
+ K06 18.72 15.18 18.23
+ K07 21.56 15.5 22.97
+ K08 23 15.02 25.37
+ K09 42.5 42.02 36.05
+ K10 44.55 41.63 39.71
+ K11 47.19 41.96 44.03
+ K12 49.9 43.14 47.21
+ K13 10.61 17.44 31.24
+ K14 24.84 13.19 21
+ K15 53.12 48.05 3.19
+ K16 1.05 1.14 1.13
+ K17 19.93 11.34 1.67
+ K18 5.3 10.47 2.73
+ K19 3.95 2.76 13.94
+ K20 30.61 26.43 11.04
+ K21 34.91 29.6 11.78
+ K22 38.95 34.57 18.4
+ L01 3.88 3.12 2.32
+ L02 4.93 3.2 2.69
+ L03 5.75 3.14 3.02
+ L04 7.31 3.79 3.4
+ L05 13.29 11.54 9.39
+ L06 16.22 11.73 10.32
+ L07 19.95 12.08 12.06
+ L08 20.79 11.31 12.01
+ L09 43.22 42.09 33.78
+ L10 45.52 41.88 34.65
+ L11 49.04 42.87 35.98
+ L12 51.03 43.83 37.78
+ L13 7.45 12.77 25.59
+ L14 21.26 10.76 17.73
+ L15 49.45 43.12 2.14
+ L16 0.47 0.49 0.5
+ L17 16.04 8.49 0.78
+ L18 2.91 6.5 1.39
+ L19 2.5 1.45 10.28
+ L20 38.7 33.98 20.86
+ L21 39.36 35.23 21.23
+ L22 41.36 38.77 23.51
+ GS00 79.47 82.51 69.04
+ GS01 72.62 74.94 59.17
+ GS02 63.15 65.11 51.57
+ GS03 54.72 56.51 45.03
+ GS04 48.1 49.81 39.24
+ GS05 42.22 43.64 34.45
+ GS06 37.33 38.7 30.5
+ GS07 32.38 33.61 26.11
+ GS08 27.56 28.7 22.11
+ GS09 22.5 23.4 17.99
+ GS10 18.77 19.55 14.83
+ GS11 15.48 16.08 12.04
+ GS12 12.69 13.29 9.98
+ GS13 10.35 10.81 7.97
+ GS14 8.39 8.77 6.37
+ GS15 6.45 6.79 4.97
+ GS16 4.95 5.18 3.7
+ GS17 3.58 3.82 2.76
+ GS18 2.76 2.89 2.06
+ GS19 1.97 2.08 1.45
+ GS20 1.22 1.31 0.98
+ GS21 1 1.05 0.74
+ GS22 0.87 0.89 0.65
+ GS23 0.34 0.32 0.32
+
+
diff --git a/scanin/scanin.c b/scanin/scanin.c
new file mode 100644
index 0000000..94771fe
--- /dev/null
+++ b/scanin/scanin.c
@@ -0,0 +1,1436 @@
+
+/*
+ * Argyll Color Correction System
+ *
+ * Scanin: Input the scan of a test chart, and output cgats data
+ * Uses scanrd to do the hard work.
+ *
+ * Author: Graeme W. Gill
+ * Date: 29/1/97
+ *
+ * Copyright 1995 - 2002 Graeme W. Gill
+ * All rights reserved.
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+/*
+ TTBD
+
+ Add "single pixel patch" mode, for pure digital processing for
+ abstract profile creation.
+
+
+ */
+
+#include <stdio.h>
+#include <fcntl.h> /* In case DOS binary stuff is needed */
+#include <ctype.h>
+#include <string.h>
+#include <math.h>
+
+#include <time.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "copyright.h"
+#include "aconfig.h"
+#include "numlib.h"
+#include "cgats.h"
+#include "icc.h"
+#include "xicc.h"
+#include "scanrd.h"
+#include "tiffio.h"
+
+/* Utilities */
+
+void fix_it8(char *o, char *i);
+
+#ifdef NT /* You'd think there might be some standards.... */
+# ifndef __BORLANDC__
+# define stricmp _stricmp
+# endif
+#else
+# define stricmp strcasecmp
+#endif
+
+#define TXBUF (256*1024L)
+
+/* NOTE: We aren't handling the libtiff error/warning messages !! */
+
+/* Read a line of data from the input Grey, RGB or CMYK tiff file */
+/* return non-zero on error */
+int read_line(
+void *fdata,
+int y,
+char *dst
+) {
+ if (TIFFReadScanline((TIFF *)fdata, (tdata_t)dst, y, 0) < 0)
+ return 1;
+ return 0;
+}
+
+/* Write a line of data to the diagnostic RGB tiff file */
+/* return non-zero on error */
+static int
+write_line(
+void *ddata,
+int y,
+char *src
+) {
+ if (TIFFWriteScanline((TIFF *)ddata, (tdata_t)src, y, 0) < 0)
+ return 1;
+ return 0;
+}
+
+void
+usage(void) {
+ fprintf(stderr,"Scanin, Version %s\n",ARGYLL_VERSION_STR);
+ fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n");
+ fprintf(stderr,"\n");
+ fprintf(stderr,"usage: scanin [options] input.tif recogin.cht valin.cie [diag.tif]\n");
+ fprintf(stderr," :- inputs 'input.tif' and outputs scanner 'input.ti3', or\n");
+ fprintf(stderr,"\n");
+ fprintf(stderr,"usage: scanin -g [options] input.tif recogout.cht [diag.tif]\n");
+ fprintf(stderr," :- outputs file 'recogout.cht', or\n");
+ fprintf(stderr,"\n");
+ fprintf(stderr,"usage: scanin -o [options] input.tif recogin.cht [diag.tif]\n");
+ fprintf(stderr," :- outputs file 'input.val', or\n");
+ fprintf(stderr,"\n");
+ fprintf(stderr,"usage: scanin -c [options] input.tif recogin.cht scanprofile.[%s|mpp] pbase [diag.tif]\n",ICC_FILE_EXT_ND);
+ fprintf(stderr," :- inputs pbase.ti2 and outputs printer pbase.ti3, or\n");
+ fprintf(stderr,"\n");
+ fprintf(stderr,"usage: scanin -r [options] input.tif recogin.cht pbase [diag.tif]\n");
+ fprintf(stderr," :- inputs pbase.ti2+.ti3 and outputs pbase.ti3\n");
+ fprintf(stderr,"\n");
+ fprintf(stderr," -g Generate a chart reference (.cht) file\n");
+ fprintf(stderr," -o Output patch values in .val file\n");
+ fprintf(stderr," -c Use image to measure color to convert printer pbase .ti2 to .ti3\n");
+ fprintf(stderr," -ca Same as -c, but accumulates more values to pbase .ti3\n");
+ fprintf(stderr," from subsequent pages\n");
+ fprintf(stderr," -r Replace device values in pbase .ti2/.ti3\n");
+ fprintf(stderr," Default is to create a scanner .ti3 file\n");
+ fprintf(stderr," -F x1,y1,x2,y2,x3,y3,x4,y4\n");
+ fprintf(stderr," Don't auto recognize, locate using four fiducual marks\n");
+ fprintf(stderr," -p Compensate for perspective distortion\n");
+ fprintf(stderr," -a Recognise chart in normal orientation only (-A fallback as is)\n");
+ fprintf(stderr," Default is to recognise all possible chart angles\n");
+ fprintf(stderr," -m Return true mean (default is robust mean)\n");
+ fprintf(stderr," -G gamma Approximate gamma encoding of image\n");
+ fprintf(stderr," -v [n] Verbosity level 0-9\n");
+ fprintf(stderr," -d [ihvglLIcrsonap] Generate diagnostic output (try -dipn)\n");
+ fprintf(stderr," i diag - B&W of input image\n");
+ fprintf(stderr," h diag - Horizontal edge/tick detection\n");
+ fprintf(stderr," v diag - Vertical edge/tick detection\n");
+ fprintf(stderr," g diag - Groups detected\n");
+ fprintf(stderr," l diag - Lines detected\n");
+ fprintf(stderr," L diag - All lines detected\n");
+ fprintf(stderr," I diag - lines used to improve fit\n");
+ fprintf(stderr," c diag - lines perspective corrected\n");
+ fprintf(stderr," r diag - lines rotated\n");
+ fprintf(stderr," s diag - diagnostic sample boxes rotated\n");
+ fprintf(stderr," o diag - sample box outlines\n");
+ fprintf(stderr," n diag - sample box names\n");
+ fprintf(stderr," a diag - sample box areas\n");
+ fprintf(stderr," p diag - pixel areas sampled\n");
+ fprintf(stderr," -O outputfile Override the default output filename & extension.\n");
+ exit(1);
+ }
+
+
+int main(int argc, char *argv[])
+{
+ int fa,nfa; /* current argument we're looking at */
+ static char tiffin_name[MAXNAMEL+1] = { 0 }; /* TIFF Input file name (.tif) */
+ static char datin_name[MAXNAMEL+4+1] = { 0 }; /* Data input name (.cie/.q60) */
+ static char datout_name[MAXNAMEL+4+1] = { 0 }; /* Data output name (.ti3/.val) */
+ static char recog_name[MAXNAMEL+1] = { 0 }; /* Reference chart name (.cht) */
+ static char prof_name[MAXNAMEL+1] = { 0 }; /* scanner profile name (.cht) */
+ static char diag_name[MAXNAMEL+1] = { 0 }; /* Diagnostic Output (.tif) name, if used */
+ int verb = 1;
+ int tmean = 0; /* Return true mean, rather than robust mean */
+ int repl = 0; /* Replace .ti3 device values from raster file */
+ int outo = 0; /* Output the values read, rather than creating scanner .ti3 */
+ int colm = 0; /* Use inage values to measure color for print profile. > 1 == append */
+ int flags = SI_GENERAL_ROT; /* Default allow all rotations */
+
+ TIFF *rh = NULL, *wh = NULL;
+ uint16 depth, bps; /* Useful depth, bits per sample */
+ uint16 tdepth; /* Total depth including alpha */
+ uint16 pconfig, photometric;
+ uint16 rextrasamples; /* Extra "alpha" samples */
+ uint16 *rextrainfo; /* Info about extra samples */
+ int gotres = 0;
+ uint16 resunits;
+ float resx, resy;
+
+ icColorSpaceSignature tiffs = 0; /* Type of tiff color space */
+
+ int i, j;
+ double gamma = 0.0; /* default */
+ double _sfid[8], *sfid = NULL; /* Specified fiducials */
+ int width, height; /* x and y size */
+
+ scanrd *sr; /* Scanrd object */
+ int err;
+ char *errm;
+ int pnotscan = 0; /* Number of patches that wern't scanned */
+
+ if (argc <= 1)
+ usage();
+
+ error_program = argv[0];
+
+ /* Process the arguments */
+ for(fa = 1;fa < argc;fa++) {
+ nfa = fa; /* skip to nfa if next argument is used */
+ if (argv[fa][0] == '-') /* Look for any flags */
+ {
+ char *na = NULL; /* next argument after flag, null if none */
+
+ if (argv[fa][2] != '\000')
+ na = &argv[fa][2]; /* next is directly after flag */
+ else
+ {
+ if ((fa+1) < argc)
+ {
+ if (argv[fa+1][0] != '-')
+ {
+ nfa = fa + 1;
+ na = argv[nfa]; /* next is seperate non-flag argument */
+ }
+ }
+ }
+
+ if (argv[fa][1] == '?') {
+ usage();
+ } else if (argv[fa][1] == 'v') {
+ verb = 2;
+ if (na != NULL && isdigit(na[0])) {
+ verb = atoi(na);
+ }
+ } else if (argv[fa][1] == 'm') {
+ tmean = 1;
+
+ } else if (argv[fa][1] == 'g') {
+ flags |= SI_BUILD_REF;
+ repl = 0;
+ outo = 0;
+ colm = 0;
+
+ } else if (argv[fa][1] == 'r') {
+ repl = 1;
+ outo = 0;
+ colm = 0;
+
+ } else if (argv[fa][1] == 'o') {
+ repl = 0;
+ outo = 1;
+ colm = 0;
+
+ } else if (argv[fa][1] == 'c') {
+ repl = 0;
+ outo = 0;
+ colm = 1;
+ if (argv[fa][2] != '\000' && argv[fa][2] == 'a')
+ colm = 2;
+
+ /* Approximate gamma encoding of image */
+ } else if (argv[fa][1] == 'G') {
+ fa = nfa;
+ if (na == NULL) usage();
+ gamma = atof(na);
+ if (gamma < 0.0 || gamma > 5.0)
+ usage();
+
+ /* Use specified fiducials instead of auto recognition */
+ } else if (argv[fa][1] == 'F') {
+ fa = nfa;
+ if (na == NULL) usage();
+ if (sscanf(na, " %lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf ",
+ &_sfid[0], &_sfid[1], &_sfid[2], &_sfid[3],
+ &_sfid[4], &_sfid[5], &_sfid[6], &_sfid[7]) != 8) {
+ usage();
+ }
+
+ sfid = _sfid;
+
+ /* Compensate for perspective */
+ } else if (argv[fa][1] == 'p') {
+ flags |= SI_PERSPECTIVE;
+
+ /* Don't recognise rotations */
+ } else if (argv[fa][1] == 'a') {
+ flags &= ~SI_GENERAL_ROT;
+
+ /* Don't recognise rotations, and read patches */
+ /* anyway "as is", if everything else failes */
+ } else if (argv[fa][1] == 'A') {
+ flags &= ~SI_GENERAL_ROT;
+ flags |= SI_ASISIFFAIL;
+
+ } else if (argv[fa][1] == 'd' || argv[fa][1] == 'D') {
+ while (na != NULL && *na != '\000') {
+ switch (*na) {
+ case 'i':
+ flags |= SI_SHOW_IMAGE;
+ break;
+ case 'h':
+ flags |= SI_SHOW_DIFFSH;
+ break;
+ case 'v':
+ flags |= SI_SHOW_DIFFSV;
+ break;
+ case 'g':
+ flags |= SI_SHOW_GROUPS;
+ break;
+ case 'l':
+ flags |= SI_SHOW_LINES;
+ break;
+ case 'L':
+ flags |= SI_SHOW_ALL_LINES;
+ break;
+ case 'I':
+ flags |= SI_SHOW_IMPL;
+ break;
+ case 'c':
+ flags |= SI_SHOW_PERS;
+ break;
+ case 'r':
+ flags |= SI_SHOW_ROT;
+ break;
+ case 's':
+ flags |= SI_SHOW_SBOX;
+ break;
+ case 'o':
+ flags |= SI_SHOW_SBOX_OUTLINES;
+ break;
+ case 'n':
+ flags |= SI_SHOW_SBOX_NAMES;
+ break;
+ case 'a':
+ flags |= SI_SHOW_SBOX_AREAS;
+ break;
+ case 'p':
+ flags |= SI_SHOW_SAMPLED_AREA;
+ break;
+ default:
+ usage();
+ }
+ na++;
+ }
+
+ /* Output file name */
+ } else if (argv[fa][1] == 'O') {
+ fa = nfa;
+ if (na == NULL) usage();
+ strncpy(datout_name,na,MAXNAMEL); datout_name[MAXNAMEL] = '\000';
+
+ } else
+ usage();
+ } else
+ break;
+ }
+
+ /* TIFF Raster input file name */
+ if (fa >= argc || argv[fa][0] == '-') usage();
+ strncpy(tiffin_name,argv[fa],MAXNAMEL); tiffin_name[MAXNAMEL] = '\000';
+
+ if (datout_name[0] == '\000' /* Not been overridden */
+ && (flags & SI_BUILD_REF) == 0
+ && repl == 0 && colm == 0) { /* Not generate ref or replacing .ti3 dev */
+ char *xl;
+ strncpy(datout_name,argv[fa],MAXNAMEL); datout_name[MAXNAMEL] = '\000';
+ if ((xl = strrchr(datout_name, '.')) == NULL) /* Figure where extention is */
+ xl = datout_name + strlen(datout_name);
+ if (outo == 0) /* Creating scan calib data */
+ strcpy(xl,".ti3");
+ else /* Just outputing values for some other purpose */
+ strcpy(xl,".val");
+ }
+
+ /* .cht Reference file in or out */
+ if (++fa >= argc || argv[fa][0] == '-') usage();
+ strncpy(recog_name,argv[fa],MAXNAMEL); recog_name[MAXNAMEL] = '\000';
+
+ if (colm > 0) {
+ if (++fa >= argc || argv[fa][0] == '-') usage();
+ strncpy(prof_name,argv[fa],MAXNAMEL); prof_name[MAXNAMEL] = '\000';
+ }
+
+ /* CGATS Data file input/output */
+ if ((flags & SI_BUILD_REF) == 0 && outo == 0) { /* Not generate ref or just outputing */
+ if (++fa >= argc || argv[fa][0] == '-') usage();
+ if (outo == 0) { /* Creating scan calib data */
+ /* Data file */
+ strncpy(datin_name,argv[fa],MAXNAMEL); datin_name[MAXNAMEL] = '\000';
+ }
+ if (repl != 0 || colm > 0) { /* Color from image or replacing .ti3 device data */
+ strcpy(datin_name,argv[fa]);
+ strcat(datin_name,".ti2");
+ strcpy(datout_name,argv[fa]);
+ strcat(datout_name,".ti3");
+ }
+ }
+
+ /* optional diagnostic file */
+ if (++fa < argc) {
+ if (argv[fa][0] == '-')
+ usage();
+ strncpy(diag_name,argv[fa],MAXNAMEL); diag_name[MAXNAMEL] = '\000';
+ } else { /* Provide a default name */
+ strcpy(diag_name,"diag.tif");
+ }
+
+ if (stricmp(diag_name, tiffin_name) == 0) {
+ error("Diagnostic output '%s' might overwrite the input '%s'!",diag_name,tiffin_name);
+ }
+
+ /* ----------------------------------------- */
+ /* Open up input tiff file ready for reading */
+ /* Got arguments, so setup to process the file */
+ if ((rh = TIFFOpen(tiffin_name, "r")) == NULL)
+ error("error opening read file '%s'",tiffin_name);
+
+ TIFFGetField(rh, TIFFTAG_IMAGEWIDTH, &width);
+ TIFFGetField(rh, TIFFTAG_IMAGELENGTH, &height);
+
+ TIFFGetField(rh, TIFFTAG_BITSPERSAMPLE, &bps);
+ if (bps != 8 && bps != 16)
+ error("TIFF Input file '%s' must be 8 or 16 bits/channel",tiffin_name);
+
+ /* See if there are alpha planes */
+ TIFFGetFieldDefaulted(rh, TIFFTAG_EXTRASAMPLES, &rextrasamples, &rextrainfo);
+
+ TIFFGetField(rh, TIFFTAG_SAMPLESPERPIXEL, &depth);
+
+ if (rextrasamples > 0 && verb)
+ printf("%d extra (alpha ?) samples will be ignored\n",rextrasamples);
+
+ tdepth = depth;
+ depth = tdepth - rextrasamples;
+
+ if (depth != 1 && depth != 3 && depth != 4)
+ error("Input '%s' must be a Grey, RGB or CMYK tiff file",tiffin_name);
+
+ TIFFGetField(rh, TIFFTAG_PHOTOMETRIC, &photometric);
+ if (depth == 1 && photometric != PHOTOMETRIC_MINISBLACK
+ && photometric != PHOTOMETRIC_MINISWHITE)
+ error("1 chanel input '%s' must be a Grey tiff file",tiffin_name);
+ else if (depth == 3 && photometric != PHOTOMETRIC_RGB)
+ error("3 chanel input '%s' must be an RGB tiff file",tiffin_name);
+ else if (depth == 4 && photometric != PHOTOMETRIC_SEPARATED)
+ error("4 chanel input '%s' must be a CMYK tiff file",tiffin_name);
+
+ if (depth == 1)
+ tiffs = icSigGrayData;
+ else if (depth == 3)
+ tiffs = icSigRgbData;
+ else if (depth == 4)
+ tiffs = icSigCmykData;
+
+ TIFFGetField(rh, TIFFTAG_PLANARCONFIG, &pconfig);
+ if (pconfig != PLANARCONFIG_CONTIG)
+ error("TIFF Input file '%s' must be planar",tiffin_name);
+
+
+ if (TIFFGetField(rh, TIFFTAG_RESOLUTIONUNIT, &resunits) != 0) {
+ TIFFGetField(rh, TIFFTAG_XRESOLUTION, &resx);
+ TIFFGetField(rh, TIFFTAG_YRESOLUTION, &resy);
+
+ if (resunits == RESUNIT_NONE /* If it looks valid */
+ || resunits == RESUNIT_INCH
+ || resunits == RESUNIT_CENTIMETER)
+ gotres = 1;
+ }
+
+ /* -------------------------- */
+ /* setup the diag output file */
+ if (flags & SI_SHOW_FLAGS) {
+ if ((wh = TIFFOpen(diag_name, "w")) == NULL)
+ error("Can\'t create TIFF file '%s'!",diag_name);
+
+ TIFFSetField(wh, TIFFTAG_IMAGEWIDTH, width);
+ TIFFSetField(wh, TIFFTAG_IMAGELENGTH, height);
+ TIFFSetField(wh, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
+ TIFFSetField(wh, TIFFTAG_SAMPLESPERPIXEL, 3);
+ TIFFSetField(wh, TIFFTAG_BITSPERSAMPLE, 8);
+ TIFFSetField(wh, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+ TIFFSetField(wh, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
+ TIFFSetField(wh, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
+ if (gotres) {
+ TIFFSetField(wh, TIFFTAG_RESOLUTIONUNIT, resunits);
+ TIFFSetField(wh, TIFFTAG_XRESOLUTION, resx);
+ TIFFSetField(wh, TIFFTAG_YRESOLUTION, resy);
+ }
+ TIFFSetField(wh, TIFFTAG_IMAGEDESCRIPTION, "Scanin diagnosis output");
+ }
+
+ /* -------------------------- */
+ if (verb >= 2) {
+ printf("Input file '%s': w=%d, h=%d, d = %d, bpp = %d\n",
+ tiffin_name, width, height, depth, bps);
+ if (flags & SI_BUILD_REF)
+ printf("Build Scan Chart reference file '%s'\n",recog_name);
+ else {
+ printf("Data input file '%s'\n",datin_name);
+ printf("Data output file '%s'\n",datout_name);
+ printf("Chart reference file '%s'\n",recog_name);
+ }
+ if (flags & SI_SHOW_FLAGS)
+ printf("Creating diagnostic tiff file '%s'\n",diag_name);
+ }
+
+ /* -------------------------- */
+ /* Do the operation */
+
+ if ((sr = do_scanrd(
+ flags, /* option flags */
+ verb, /* verbosity level */
+
+ gamma,
+ sfid, /* Specified fiducuals, if any */
+ width, height, depth, tdepth, bps, /* Width, Height and Depth of input in pixels */
+ read_line, /* Read line function */
+ (void *)rh, /* Opaque data for read_line */
+
+ recog_name, /* reference file name */
+
+ write_line, /* Write line function */
+ (void *)wh /* Opaque data for write_line */
+ )) == NULL) {
+ if (flags & SI_SHOW_FLAGS)
+ TIFFClose(wh);
+ error("Unable to allocate scanrd object");
+ }
+
+ if ((err = sr->error(sr, &errm)) != 0) {
+ if ((flags & SI_SHOW_FLAGS) && err != SI_DIAG_WRITE_ERR)
+ TIFFClose(wh); /* Close diagnostic file */
+ error("Scanin failed with code 0x%x, %s",err,errm);
+ }
+
+ /* Read an output the values */
+ if ((flags & SI_BUILD_REF) == 0) { /* Not generate ref */
+
+ /* -------------------------------------------------- */
+ if (outo != 0) { /* Just output the values */
+ /* Note value range is raw 0..255, */
+ /* while all others output formats are out of 100 */
+ cgats *ocg; /* output cgats structure */
+ time_t clk = time(0);
+ struct tm *tsp = localtime(&clk);
+ char *atm = asctime(tsp); /* Ascii time */
+
+ /* Setup output cgats file */
+ ocg = new_cgats(); /* Create a CGATS structure */
+ ocg->add_other(ocg, "VALS"); /* Dummy type */
+ ocg->add_table(ocg, tt_other, 0); /* Start the first table */
+
+ ocg->add_kword(ocg, 0, "DESCRIPTOR", "Argyll Calibration raster values",NULL);
+ ocg->add_kword(ocg, 0, "ORIGINATOR", "Argyll scanin", NULL);
+ atm[strlen(atm)-1] = '\000'; /* Remove \n from end */
+ ocg->add_kword(ocg, 0, "CREATED",atm, NULL);
+
+ ocg->add_field(ocg, 0, "SAMPLE_ID", nqcs_t);
+ if (depth == 1) {
+ ocg->add_field(ocg, 0, "GREY", r_t);
+ } else if (depth == 3) {
+ ocg->add_field(ocg, 0, "RGB_R", r_t);
+ ocg->add_field(ocg, 0, "RGB_G", r_t);
+ ocg->add_field(ocg, 0, "RGB_B", r_t);
+ } else if (depth == 4) {
+ ocg->add_field(ocg, 0, "CMYK_C", r_t);
+ ocg->add_field(ocg, 0, "CMYK_M", r_t);
+ ocg->add_field(ocg, 0, "CMYK_Y", r_t);
+ ocg->add_field(ocg, 0, "CMYK_K", r_t);
+ }
+
+ /* Initialise, ready to read out all the values */
+ for (j = 0; ; j++) {
+ char id[100]; /* Input patch id */
+ double P[4]; /* Robust/true mean values */
+ int pixcnt; /* PIxel count */
+
+ if (tmean) {
+ if (sr->read(sr, id, NULL, P, NULL, &pixcnt) != 0)
+ break;
+ } else {
+ if (sr->read(sr, id, P, NULL, NULL, &pixcnt) != 0)
+ break;
+ }
+
+ if (pixcnt == 0)
+ pnotscan++;
+
+ if (depth == 1) {
+ ocg->add_set( ocg, 0, id, P[0]);
+ } else if (depth == 3) {
+ ocg->add_set( ocg, 0, id, P[0], P[1], P[2]);
+ } else if (depth == 4) {
+ ocg->add_set( ocg, 0, id, P[0], P[1], P[2], P[3]);
+ }
+ }
+
+ if (verb)
+ printf("Writing output values to file '%s'\n",datout_name);
+
+ if (ocg->write_name(ocg, datout_name))
+ error("Write error to '%s' : %s",datout_name,ocg->err);
+
+ ocg->del(ocg); /* Clean up */
+
+ /* -------------------------------------------------- */
+ } else if (repl != 0) { /* Replace .ti3 device values */
+ cgats *icg; /* input .ti2 cgats structure */
+ cgats *ocg; /* input/output .ti3 cgats structure */
+ int npat; /* Number of test patches */
+ int dim = 0; /* Dimenstionality of device space */
+ int fi; /* Field index */
+ int isi, ili; /* Input file sample and location indexes */
+ char *dfnames[5][4] = { /* Device colorspace names */
+ { "" },
+ { "GRAY_W" },
+ { "" },
+ { "RGB_R", "RGB_G", "RGB_B" },
+ { "CMYK_C", "CMYK_M", "CMYK_Y", "CMYK_K" }
+ };
+ int odim = 0; /* Output file device dimensionality */
+ int dfi[5][4]; /* Output file device colorspace indexes */
+ int osi; /* Output file sample id index */
+
+ /* Setup input .ti2 file */
+ icg = new_cgats(); /* Create a CGATS structure */
+ icg->add_other(icg, "CTI2"); /* Calibration Target Information 2 */
+ if (icg->read_name(icg, datin_name))
+ error("CGATS file '%s' read error : %s",datin_name,icg->err);
+
+ if (icg->t[0].tt != tt_other || icg->t[0].oi != 0)
+ error("Input file '%s' isn't a CTI2 format file",datin_name);
+
+ if (icg->ntables < 1)
+ error("Input file '%s' doesn't contain at least one table",datin_name);
+
+ if ((npat = icg->t[0].nsets) <= 0)
+ error("Input file '%s' doesn't contain any data sets",datin_name);
+
+ /* Figure out the color space */
+ if ((fi = icg->find_kword(icg, 0, "COLOR_REP")) < 0)
+ error("Input file '%s' doesn't contain keyword COLOR_REP",datin_name);
+
+ if (strcmp(icg->t[0].kdata[fi],"CMYK") == 0) {
+ dim = 4;
+ } else if (strcmp(icg->t[0].kdata[fi],"RGB") == 0) {
+ dim = 3;
+ } else if (strcmp(icg->t[0].kdata[fi],"W") == 0) {
+ dim = 1;
+ } else
+ error("Input file '%s' keyword COLOR_REP has unknown value",datin_name);
+
+ /* Find fields we want in the input file */
+ if ((isi = icg->find_field(icg, 0, "SAMPLE_ID")) < 0)
+ error("Input file '%s' doesn't contain field SAMPLE_ID",datin_name);
+ if (icg->t[0].ftype[isi] != nqcs_t)
+ error("Input file '%s' Field SAMPLE_ID is wrong type",datin_name);
+
+ if ((ili = icg->find_field(icg, 0, "SAMPLE_LOC")) < 0)
+ error("Input file '%s' doesn't contain field SAMPLE_LOC",datin_name);
+ if (icg->t[0].ftype[ili] != cs_t
+ && icg->t[0].ftype[ili] != nqcs_t)
+ error("Input file '%s' Field SAMPLE_LOC is wrong type",datin_name);
+
+ /* Setup input/output .ti3 file */
+ ocg = new_cgats(); /* Create a CGATS structure */
+ ocg->add_other(ocg, "CTI3"); /* Calibration Target Information 3 */
+ if (ocg->read_name(ocg, datout_name))
+ error("CGATS file '%s' read error : %s",datout_name,ocg->err);
+
+ if (ocg->t[0].tt != tt_other || ocg->t[0].oi != 0)
+ error("Input file '%s' isn't a CTI3 format file",datout_name);
+
+ if (ocg->ntables < 1)
+ error("Input file '%s' doesn't contain at least one table",datout_name);
+
+ if (npat != ocg->t[0].nsets)
+ error("Input file '%s' doesn't contain same number of data sets",datout_name);
+
+
+ /* Find the fields we want in the output file */
+
+ /* Figure out the color space */
+ if ((fi = ocg->find_kword(ocg, 0, "COLOR_REP")) < 0)
+ error("Input file '%s' doesn't contain keyword COLOR_REP",datout_name);
+
+ if (strncmp(ocg->t[0].kdata[fi],"CMYK",4) == 0) {
+ odim = 4;
+ } else if (strncmp(ocg->t[0].kdata[fi],"RGB",3) == 0) {
+ odim = 3;
+ } else if (strncmp(ocg->t[0].kdata[fi],"W",1) == 0) {
+ odim = 1;
+ } else
+ error("Input file '%s' keyword COLOR_REP has unknown value",datout_name);
+
+ if (odim != dim)
+ error("File '%s' has different device space to '%s'",datin_name, datout_name);
+
+ if ((osi = ocg->find_field(ocg, 0, "SAMPLE_ID")) < 0)
+ error("Input file '%s' doesn't contain field SAMPLE_ID",datout_name);
+ if (ocg->t[0].ftype[osi] != nqcs_t)
+ error("Input file '%s' Field SAMPLE_ID is wrong type",datout_name);
+
+ for (i = 0; i < dim; i++) {
+ if ((dfi[dim][i] = ocg->find_field(ocg, 0, dfnames[dim][i])) < 0)
+ error("Input '%s' file doesn't contain field %s", datout_name, dfnames[dim][i]);
+ if (ocg->t[0].ftype[dfi[dim][i]] != r_t)
+ error("Input '%s' Field %s is wrong type",datout_name,dfnames[dim][i]);
+ }
+
+ /* Initialise, ready to read out all the values */
+ for (i = sr->reset(sr); i > 0; i--) { /* For all samples in .tiff file */
+ char loc[100]; /* Target patch location */
+ double P[4]; /* Robust/raw mean values */
+ int pixcnt; /* Pixel count */
+ int k, e;
+
+ if (tmean)
+ sr->read(sr, loc, NULL, P, NULL, &pixcnt);
+ else
+ sr->read(sr, loc, P, NULL, NULL, &pixcnt);
+
+ if (pixcnt == 0)
+ pnotscan++;
+
+ /* Search for this location in the .ti2 file */
+ for (j = 0; j < npat; j++) {
+ if (strcmp(loc, (char *)icg->t[0].fdata[j][ili]) == 0) {
+ char *sidp = (char *)icg->t[0].fdata[j][isi];
+
+ /* Search for this sample id in .ti3 file */
+ for (k = 0; k < npat; k++) {
+ if (strcmp(sidp, (char *)ocg->t[0].fdata[k][osi]) == 0) {
+ /* Update the device values */
+ for (e = 0; e < dim; e++) {
+ double vv = 100.0 * P[e]/255.0;
+ *((double *)ocg->t[0].fdata[k][dfi[dim][e]]) = vv;
+ }
+ break;
+ }
+ }
+ if (k >= npat && verb >= 1)
+ printf("Warning: Couldn't find sample '%s' in '%s'\n",sidp,datout_name);
+ break;
+ }
+ }
+ if (j >= npat && verb >= 1)
+ printf("Warning: Couldn't find location '%s' in '%s'\n",loc,datin_name);
+ }
+
+ /* Flush our changes */
+ if (verb)
+ printf("Writing output values to file '%s'\n",datout_name);
+
+ if (ocg->write_name(ocg, datout_name))
+ error("Write error to file '%s' : %s",datout_name,ocg->err);
+
+ ocg->del(ocg); /* Clean up */
+ icg->del(icg); /* Clean up */
+
+ /* ---------------------------------------------------------- */
+ } else if (colm > 0) { /* Using the image to measure color */
+ /* All this needs to track the code in spectro/printread.c */
+ cgats *icg; /* input .ti2 cgats structure */
+ cgats *ocg; /* input/output .ti3 cgats structure */
+ icmFile *rd_fp = NULL; /* Image to CIE lookup */
+ icc *rd_icco = NULL;
+ icmLuBase *luo;
+ mpp *mlu = NULL;
+ time_t clk = time(0);
+ struct tm *tsp = localtime(&clk);
+ char *atm = asctime(tsp); /* Ascii time */
+ int nmask = 0; /* Device colorant mask */
+ int nchan = 0; /* Number of device chanels */
+ int npat; /* Number of input patches (inc. padding) */
+ int nopat = 0; /* Number of output patches */
+ int si; /* Sample id index */
+ int li; /* Location id index */
+ int ti; /* Temp index */
+ int fi; /* Colorspace index */
+
+ icg = new_cgats(); /* Create a CGATS structure */
+ icg->add_other(icg, "CTI2"); /* special type Calibration Target Information 2 */
+ icg->add_other(icg, "CAL"); /* There may be a calibration too */
+
+ if (icg->read_name(icg, datin_name))
+ error("CGATS file '%s' read error : %s",datin_name,icg->err);
+
+ if (icg->t[0].tt != tt_other || icg->t[0].oi != 0)
+ error("Input file '%s' isn't a CTI2 format file",datin_name);
+ if (icg->ntables < 1)
+ error("Input file '%s' doesn't contain at least one table",datin_name);
+
+ if ((npat = icg->t[0].nsets) <= 0)
+ error("Input file '%s' has no sets of data",datin_name);
+
+ /* Setup output cgats file */
+ ocg = new_cgats(); /* Create a CGATS structure */
+ ocg->add_other(ocg, "CTI3"); /* special type Calibration Target Information 3 */
+
+ if (colm > 1) { /* Appending information to .ti3 */
+
+ if (ocg->read_name(ocg, datout_name))
+ error("CGATS file read error on '%s': %s",datout_name, ocg->err);
+
+ if (ocg->t[0].tt != tt_other || ocg->t[0].oi != 0)
+ error("Input file '%s' isn't a CTI3 format file",datout_name);
+ if (ocg->ntables < 1)
+ error("Input file '%s' doesn't at least exactly one table",datout_name);
+ if ((nopat = ocg->t[0].nsets) <= 0)
+ error("Input file '%s' has no existing sets of data",datout_name);
+
+ } else { /* Creating .ti3 */
+
+ ocg->add_table(ocg, tt_other, 0); /* Start the first table */
+
+ ocg->add_kword(ocg, 0, "DESCRIPTOR", "Argyll Calibration Target chart information 3",NULL);
+ ocg->add_kword(ocg, 0, "ORIGINATOR", "Argyll printread", NULL);
+ atm[strlen(atm)-1] = '\000'; /* Remove \n from end */
+ ocg->add_kword(ocg, 0, "CREATED",atm, NULL);
+ ocg->add_kword(ocg, 0, "DEVICE_CLASS","OUTPUT", NULL); /* What sort of device this is */
+ if ((ti = icg->find_kword(icg, 0, "SINGLE_DIM_STEPS")) >= 0)
+ ocg->add_kword(ocg, 0, "SINGLE_DIM_STEPS",icg->t[0].kdata[ti], NULL);
+
+ if ((ti = icg->find_kword(icg, 0, "COMP_GREY_STEPS")) >= 0)
+ ocg->add_kword(ocg, 0, "COMP_GREY_STEPS",icg->t[0].kdata[ti], NULL);
+
+ if ((ti = icg->find_kword(icg, 0, "MULTI_DIM_STEPS")) >= 0)
+ ocg->add_kword(ocg, 0, "MULTI_DIM_STEPS",icg->t[0].kdata[ti], NULL);
+
+ if ((ti = icg->find_kword(icg, 0, "FULL_SPREAD_PATCHES")) >= 0)
+ ocg->add_kword(ocg, 0, "FULL_SPREAD_PATCHES",icg->t[0].kdata[ti], NULL);
+
+ if ((ti = icg->find_kword(icg, 0, "TOTAL_INK_LIMIT")) >= 0)
+ ocg->add_kword(ocg, 0, "TOTAL_INK_LIMIT",icg->t[0].kdata[ti], NULL);
+
+ /* See if there is a calibration in the .ti2, and copy it if there is */
+ {
+ int oi, tab;
+
+ oi = icg->get_oi(icg, "CAL");
+
+ for (tab = 0; tab < icg->ntables; tab++) {
+ if (icg->t[tab].tt == tt_other && icg->t[tab].oi == oi) {
+ break;
+ }
+ }
+ if (tab < icg->ntables) {
+ xcal *cal = NULL;
+
+ if (verb)
+ printf("Copying .cal from '%s' to '%s'\n",datin_name,datout_name);
+
+ if ((cal = new_xcal()) == NULL) {
+ error("new_xcal failed");
+ }
+ if (cal->read_cgats(cal, icg, tab, datin_name) != 0) {
+ error("%s",cal->err);
+ }
+
+ if (cal->write_cgats(cal, ocg)) {
+ error("%s",cal->err);
+ }
+
+ cal->del(cal);
+ }
+ }
+ }
+
+ if ((si = icg->find_field(icg, 0, "SAMPLE_ID")) < 0)
+ error("Input file '%s' doesn't contain field SAMPLE_ID",datin_name);
+ if (icg->t[0].ftype[si] != nqcs_t)
+ error("Input file '%s' Field SAMPLE_ID is wrong type",datin_name);
+
+ /* Fields we want */
+ if (colm > 1) { /* Appending information to .ti3 */
+ if ((ti = ocg->find_field(ocg, 0, "SAMPLE_ID")) != 0)
+ error("Input file '%s' field SAMPLE_ID (%d) not in expected location (%d)",
+ datout_name, ti, 0);
+ } else {
+ ocg->add_field(ocg, 0, "SAMPLE_ID", nqcs_t);
+ }
+
+ if ((li = icg->find_field(icg, 0, "SAMPLE_LOC")) < 0)
+ error("Input file '%s' doesn't contain field SAMPLE_LOC",datin_name);
+ if (icg->t[0].ftype[li] != cs_t)
+ error("Input file '%s' field SAMPLE_LOC is wrong type",datin_name);
+
+ /* Figure out the color space */
+ if ((fi = icg->find_kword(icg, 0, "COLOR_REP")) < 0)
+ error("Input file '%s' doesn't contain keyword COLOR_REPS",datin_name);
+
+ if ((nmask = icx_char2inkmask(icg->t[0].kdata[fi])) != 0) {
+ int i, j, ii;
+ int chix[ICX_MXINKS]; /* Device chanel indexes */
+ int xyzix[3]; /* XYZ chanel indexes */
+ char *ident, *bident;
+ char *xyzfname[3] = { "XYZ_X", "XYZ_Y", "XYZ_Z" };
+
+ nchan = icx_noofinks(nmask);
+ ident = icx_inkmask2char(nmask, 1);
+ bident = icx_inkmask2char(nmask, 0);
+
+ /* Device channels */
+ for (j = 0; j < nchan; j++) {
+ int imask;
+ char fname[100];
+
+ imask = icx_index2ink(nmask, j);
+ sprintf(fname,"%s_%s",nmask == ICX_W || nmask == ICX_K ? "GRAY" : bident,
+ icx_ink2char(imask));
+
+ if ((ii = icg->find_field(icg, 0, fname)) < 0)
+ error("Input file '%s' doesn't contain field %s",datin_name,fname);
+ if (icg->t[0].ftype[ii] != r_t)
+ error("Field %s is wrong type",fname);
+
+ if (colm > 1) { /* Appending information to .ti3 */
+ if (ocg->find_field(ocg, 0, fname) != 1 + j)
+ error("Input file '%s' field %s not in expected location",datout_name,fname);
+ } else {
+ ocg->add_field(ocg, 0, fname, r_t);
+ }
+ chix[j] = ii;
+ }
+
+ /* Approximate XYZ and real XYZ */
+ for (j = 0; j < 3; j++) {
+ if ((ii = icg->find_field(icg, 0, xyzfname[j])) >= 0) {
+
+ if (icg->t[0].ftype[ii] != r_t)
+ error("Input file '%s' field %s is wrong type",datin_name,xyzfname[j]);
+ }
+
+ if (colm > 1) { /* Appending information to .ti3 */
+ if (ocg->find_field(ocg, 0, xyzfname[j]) != 1 + nchan + j)
+ error("Input file '%s' field %s not in expected location",
+ datout_name,xyzfname[j]);
+ } else {
+ ocg->add_field(ocg, 0, xyzfname[j], r_t);
+ }
+ xyzix[j] = ii;
+ }
+
+ if (colm <= 1) { /* Creating .ti3 */
+ char fname[100];
+ sprintf(fname, "%s_XYZ", ident);
+ ocg->add_kword(ocg, 0, "COLOR_REP", fname, NULL);
+ }
+
+ if (colm > 1) { /* Appending .ti3 data */
+
+ /* Check that all the patches match */
+ for (ii = i = 0; i < npat; i++) {
+
+ if (strcmp(((char *)icg->t[0].fdata[i][si]), "0") == 0)
+ continue; /* Padding, so skip it */
+
+ /* Id's */
+ if (strcmp (((char *)icg->t[0].fdata[i][si]),
+ ((char *)ocg->t[0].fdata[ii][si])) != 0)
+ error("'%s' and '%s' field id's don't match at patch %d\n",datin_name,datout_name,i+1);
+
+ /* device values */
+ for (j = 0; j < nchan; j++) {
+ double ival, oval;
+ ival = *((double *)icg->t[0].fdata[i][chix[j]]);
+ oval = *((double *)ocg->t[0].fdata[ii][1 + j]);
+ if (fabs(ival - oval) > 0.001)
+ error("'%s' and '%s' device values (%f %f) don't match at patch %d %d\n",datin_name,datout_name,ival, oval, i+1, ii+1);
+ }
+ ii++;
+ }
+ if (ii != nopat)
+ error("Different number of patches in '%s' (%d) to expected(%d)",datout_name,nopat,ii);
+
+ } else { /* Read all the test patches in, and create output slots */
+ cgats_set_elem *setel; /* Array of set value elements */
+
+ if ((setel = (cgats_set_elem *)malloc(sizeof(cgats_set_elem) * (1 + nchan + 3))) == NULL)
+ error("Malloc failed!");
+
+
+ for (ii = i = 0; i < npat; i++) {
+ int k = 0;
+
+ if (strcmp(((char *)icg->t[0].fdata[i][si]), "0") == 0)
+ continue; /* Padding, so skip it */
+
+ /* Id */
+ setel[k++].c = ((char *)icg->t[0].fdata[i][si]);
+
+ /* device values */
+ for (j = 0; j < nchan; j++) {
+ setel[k++].d = *((double *)icg->t[0].fdata[i][chix[j]]);
+ }
+
+ /* Unset XYZ values */
+ setel[k++].d = -1.0;
+ setel[k++].d = -1.0;
+ setel[k++].d = -1.0;
+
+ ocg->add_setarr(ocg, 0, setel);
+
+ ii++;
+ }
+ nopat = ii;
+ free(setel);
+ }
+ free(ident);
+ free(bident);
+
+ } else
+ error("Input file '%s' keyword COLOR_REPS has unknown value",datin_name);
+
+ /* Setup RGB to XYZ conversion */
+ {
+ int inn, outn; /* Chanels for input and output spaces */
+ icColorSpaceSignature ins, outs; /* Type of input and output spaces */
+ int rv;
+
+ /* Open up the file for reading */
+ if ((rd_fp = new_icmFileStd_name(prof_name,"r")) == NULL)
+ error("Write: Can't open file '%s'",prof_name);
+
+ if ((rd_icco = new_icc()) == NULL)
+ error("Read: Creation of ICC object failed");
+
+ /* Read the header and tag list */
+ if ((rv = rd_icco->read(rd_icco,rd_fp,0)) == 0) {
+
+ /* Get the Fwd table, absolute with XYZ override */
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icAbsoluteColorimetric,
+ icSigXYZData, icmLuOrdNorm)) == NULL) {
+ error("%d, %s",rd_icco->errc, rd_icco->err);
+ }
+
+ /* Get details of conversion */
+ luo->spaces(luo, &ins, &inn, &outs, &outn, NULL, NULL, NULL, NULL, NULL);
+
+ /* Check that it matches what we expect */
+
+ } else { /* Not a valid ICC */
+ inkmask cnv_nmask = 0; /* Conversion input nmask */
+
+ /* Close out the ICC profile */
+ rd_icco->del(rd_icco);
+ rd_icco = NULL;
+ rd_fp->del(rd_fp);
+ rd_fp = NULL;
+
+ /* If we don't have an ICC lookup object, look for an MPP */
+
+ if ((mlu = new_mpp()) == NULL)
+ error ("Creation of MPP object failed");
+
+ if ((rv = mlu->read_mpp(mlu, prof_name)) == 0) {
+
+ /* mlu defaults to absolute XYZ lookup */
+ mlu->get_info(mlu, &cnv_nmask, &inn, NULL, NULL, NULL, NULL, NULL, NULL);
+
+ outn = 3;
+ outs = icSigXYZData;
+
+ if ((ins = icx_colorant_comb_to_icc(cnv_nmask)) == 0)
+ error ("Couldn't match MPP mask to valid ICC colorspace");
+
+ } else {
+ mlu->del(mlu);
+ mlu = NULL;
+ error("File '%s' failed to read as ICC or MPP profile",prof_name);
+ }
+ }
+ if (inn != depth || tiffs != ins)
+ error("%s profile '%s' doesn't match TIFF file type",luo != NULL ? "ICC" : "MPP", prof_name);
+ }
+
+ /* Initialise, ready to read out all the values */
+ for (i = sr->reset(sr); i > 0; i--) { /* For all samples in .tiff file */
+ char loc[100]; /* Target patch location */
+ double P[ICX_MXINKS]; /* Robust/true mean values */
+ double xyz[3]; /* profile XYZ value */
+ int pixcnt; /* Pixel count */
+ int k, e;
+
+ if (tmean)
+ sr->read(sr, loc, NULL, P, NULL, &pixcnt);
+ else
+ sr->read(sr, loc, P, NULL, NULL, &pixcnt);
+
+ if (pixcnt == 0)
+ pnotscan++;
+
+ /* Search for this location in the .ti2 file */
+ for (j = 0; j < npat; j++) {
+ if (strcmp(loc, (char *)icg->t[0].fdata[j][li]) == 0) { /* Got location */
+ char *sidp = (char *)icg->t[0].fdata[j][si]; /* Get id */
+
+ if (strcmp(sidp, "0") == 0)
+ break; /* Padding, so ignore it */
+
+ /* Search for this sample id in .ti3 file */
+ for (k = 0; k < nopat; k++) {
+ if (strcmp(sidp, (char *)ocg->t[0].fdata[k][si]) == 0) {
+
+//printf("Loc %s, ID %s got RGB value %f %f %f\n",sidp, loc, P[0], P[1], P[2]);
+
+ /* Convert RGB to XYZ */
+ for (e = 0; e < depth; e++)
+ P[e] /= 255.0; /* Convert to 0.0 .. 1.0 range */
+
+ /* Convert to XYZ */
+ if (luo != NULL)
+ luo->lookup(luo, xyz, P);
+ else
+ mlu->lookup(mlu, xyz, P);
+
+ /* Sanity check XYZ ? */
+ // ~~~99
+
+ /* Update the XYZ values */
+ for (e = 0; e < 3; e++) {
+ double ev = *((double *)ocg->t[0].fdata[k][1 + nchan + e]);
+
+ if (ev != -1.0)
+ error("Found an existing value in '%s' file (%f)",datout_name,ev);
+
+ *((double *)ocg->t[0].fdata[k][1 + nchan + e]) = 100.0 * xyz[e];
+ }
+ break;
+ }
+ }
+ if (k >= nopat)
+ error("Couldn't find sample '%s' in '%s'\n",sidp,datout_name);
+ break;
+ }
+ }
+ if (j >= npat && verb >= 1)
+ error("Couldn't find location '%s' in '%s'\n",loc, datin_name);
+ }
+
+ /* Warn if not all patch values have been filled */
+ if (verb) {
+ int e, k;
+ for (k = 0; k < nopat; k++) {
+ for (e = 0; e < 3; e++) {
+ double ev = *((double *)ocg->t[0].fdata[k][1 + nchan + e]);
+
+ if (ev == -1.0)
+ break;
+ }
+ if (e < 3)
+ break;
+ }
+ if (k < nopat)
+ printf("Not all sample values have been filled\n");
+ else
+ printf("All sample values have been filled\n");
+ }
+
+ if (verb)
+ printf("Writing output values to file '%s'\n",datout_name);
+
+ if (ocg->write_name(ocg, datout_name))
+ error("File '%s' write error : %s",datout_name,ocg->err);
+
+ if (luo != NULL)
+ luo->del(luo);
+ if (rd_icco != NULL)
+ rd_icco->del(rd_icco);
+ if (rd_fp != NULL)
+ rd_fp->del(rd_fp);
+ if (mlu != NULL)
+ mlu->del(mlu);
+
+ ocg->del(ocg); /* Clean up */
+ icg->del(icg); /* Clean up */
+
+ /* ----------------------------------- */
+ } else { /* Normal scan calibration */
+ cgats *icg; /* input cgats structure */
+ cgats *ocg; /* output cgats structure */
+ time_t clk = time(0);
+ struct tm *tsp = localtime(&clk);
+ char *atm = asctime(tsp); /* Ascii time */
+ int ti; /* Temp index */
+ int sx; /* Sample id index */
+ int isLab = 0; /* D50 Lab reference */
+ int Xx, Yx, Zx; /* XYZ_X, XYZ_Y, XYZ_Z index */
+ int spec_n = 0; /* Number of spectral bands */
+ double spec_wl_short;/* First reading wavelength in nm (shortest) */
+ double spec_wl_long; /* Last reading wavelength in nm (longest) */
+ int spi[XSPECT_MAX_BANDS]; /* CGATS indexes for each wavelength */
+ int npat; /* Number of test patches in it8 chart */
+ int nsetel = 0; /* Number of output set elements */
+ cgats_set_elem *setel; /* Array of set value elements */
+
+ icg = new_cgats(); /* Create a CGATS structure */
+ icg->add_other(icg, ""); /* Accept any type */
+ if (icg->read_name(icg, datin_name))
+ error("CGATS file '%s' read error : %s",datin_name,icg->err);
+
+ /* ~~ should accept ti2 file and convert RGB to XYZ using */
+ /* device cal., to make W/RGB/CMYK ->XYZ reading chart ~~ */
+ if (icg->ntables < 1)
+ error("Input file '%s' doesn't contain at least one table",datin_name);
+
+ if ((npat = icg->t[0].nsets) <= 0)
+ error("File '%s' no sets of data in first table",datin_name);
+
+ /* Setup output cgats file */
+ ocg = new_cgats(); /* Create a CGATS structure */
+ ocg->add_other(ocg, "CTI3"); /* our special type is Calibration Target Information 3 */
+ ocg->add_table(ocg, tt_other, 0); /* Start the first table */
+
+ ocg->add_kword(ocg, 0, "DESCRIPTOR", "Argyll Calibration Target chart information 3",NULL);
+ ocg->add_kword(ocg, 0, "ORIGINATOR", "Argyll target", NULL);
+ atm[strlen(atm)-1] = '\000'; /* Remove \n from end */
+ ocg->add_kword(ocg, 0, "CREATED",atm, NULL);
+
+ ocg->add_kword(ocg, 0, "DEVICE_CLASS","INPUT", NULL); /* What sort of device this is */
+ ocg->add_kword(ocg, 0, "COLOR_REP","XYZ_RGB", NULL);
+
+ /* Fields we want from input chart reference file */
+ if ((sx = icg->find_field(icg, 0, "Sample_Name")) < 0) {
+ if ((sx = icg->find_field(icg, 0, "SAMPLE_NAME")) < 0) {
+ if ((sx = icg->find_field(icg, 0, "SAMPLE_LOC")) < 0) {
+ if ((sx = icg->find_field(icg, 0, "SAMPLE_ID")) < 0) {
+ error("Input file '%s' doesn't contain field SAMPLE_ID, Sample_Name or SAMPLE_NAME",datin_name);
+ }
+ }
+ }
+ }
+ if (icg->t[0].ftype[sx] != nqcs_t && icg->t[0].ftype[sx] != cs_t)
+ error("Input file '%s' field %s is wrong type", datin_name, icg->t[0].fsym[sx]);
+
+ if ((Xx = icg->find_field(icg, 0, "XYZ_X")) < 0) {
+ if ((Xx = icg->find_field(icg, 0, "LAB_L")) < 0)
+ error("Input file '%s' doesn't contain field XYZ_X or LAB_L",datin_name);
+
+ isLab = 1;
+ if (icg->t[0].ftype[Xx] != r_t)
+ error("Input file '%s' field LAB_L is wrong type",datin_name);
+ if ((Yx = icg->find_field(icg, 0, "LAB_A")) < 0)
+ error("Input file doesn't contain field LAB_A",datin_name);
+ if (icg->t[0].ftype[Yx] != r_t)
+ error("Input file '%s' field LAB_A is wrong type",datin_name);
+ if ((Zx = icg->find_field(icg, 0, "LAB_B")) < 0)
+ error("Input file '%s' doesn't contain field LAB_B",datin_name);
+ if (icg->t[0].ftype[Zx] != r_t)
+ error("Input file '%s' field LAB_B is wrong type",datin_name);
+ } else {
+ if (icg->t[0].ftype[Xx] != r_t)
+ error("Input file '%s' field XYZ_X is wrong type",datin_name);
+ if ((Yx = icg->find_field(icg, 0, "XYZ_Y")) < 0)
+ error("Input file '%s' doesn't contain field XYZ_Y",datin_name);
+ if (icg->t[0].ftype[Yx] != r_t)
+ error("Input file '%s' field XYZ_Y is wrong type",datin_name);
+ if ((Zx = icg->find_field(icg, 0, "XYZ_Z")) < 0)
+ error("Input file '%s' doesn't contain field XYZ_Z",datin_name);
+ if (icg->t[0].ftype[Zx] != r_t)
+ error("Input file '%s' field XYZ_Z is wrong type",datin_name);
+ }
+
+ /* Find possible spectral fields in reference */
+ if ((ti = icg->find_kword(icg, 0, "SPECTRAL_BANDS")) >= 0) {
+ spec_n = atoi(icg->t[0].kdata[ti]);
+ if ((ti = icg->find_kword(icg, 0, "SPECTRAL_START_NM")) < 0)
+ error ("Input file '%s' doesn't contain keyword SPECTRAL_START_NM",datin_name);
+ spec_wl_short = atof(icg->t[0].kdata[ti]);
+ if ((ti = icg->find_kword(icg, 0, "SPECTRAL_END_NM")) < 0)
+ error ("Input file '%s' doesn't contain keyword SPECTRAL_END_NM",datin_name);
+ spec_wl_long = atof(icg->t[0].kdata[ti]);
+
+ /* Find the fields for spectral values */
+ for (i = 0; i < spec_n; i++) {
+ char buf[100];
+ int nm;
+
+ /* Compute nearest integer wavelength */
+ nm = (int)(spec_wl_short + ((double)i/(spec_n-1.0))
+ * (spec_wl_long - spec_wl_short) + 0.5);
+
+ sprintf(buf,"SPEC_%03d",nm);
+
+ if ((spi[i] = icg->find_field(icg, 0, buf)) < 0)
+ error("Input file doesn't contain field %s",datin_name);
+ }
+ }
+
+ ocg->add_field(ocg, 0, "SAMPLE_ID", nqcs_t);
+ nsetel += 1;
+ ocg->add_field(ocg, 0, "XYZ_X", r_t);
+ ocg->add_field(ocg, 0, "XYZ_Y", r_t);
+ ocg->add_field(ocg, 0, "XYZ_Z", r_t);
+ nsetel += 3;
+
+ /* If we have spectral information, output it too */
+ if (spec_n > 0) {
+ char buf[100];
+
+ nsetel += spec_n; /* Spectral values */
+ sprintf(buf,"%d", spec_n);
+ ocg->add_kword(ocg, 0, "SPECTRAL_BANDS",buf, NULL);
+ sprintf(buf,"%f", spec_wl_short);
+ ocg->add_kword(ocg, 0, "SPECTRAL_START_NM",buf, NULL);
+ sprintf(buf,"%f", spec_wl_long);
+ ocg->add_kword(ocg, 0, "SPECTRAL_END_NM",buf, NULL);
+
+ /* Generate fields for spectral values */
+ for (i = 0; i < spec_n; i++) {
+ int nm;
+
+ /* Compute nearest integer wavelength */
+ nm = (int)(spec_wl_short + ((double)i/(spec_n-1.0))
+ * (spec_wl_long - spec_wl_short) + 0.5);
+
+ sprintf(buf,"SPEC_%03d",nm);
+ ocg->add_field(ocg, 0, buf, r_t);
+ }
+ }
+
+ if (depth == 1) {
+ ocg->add_field(ocg, 0, "GREY", r_t);
+ ocg->add_field(ocg, 0, "STDEV_GREY", r_t);
+ } else if (depth == 3) {
+ ocg->add_field(ocg, 0, "RGB_R", r_t);
+ ocg->add_field(ocg, 0, "RGB_G", r_t);
+ ocg->add_field(ocg, 0, "RGB_B", r_t);
+ ocg->add_field(ocg, 0, "STDEV_R", r_t);
+ ocg->add_field(ocg, 0, "STDEV_G", r_t);
+ ocg->add_field(ocg, 0, "STDEV_B", r_t);
+ } else if (depth == 4) {
+ ocg->add_field(ocg, 0, "CMYK_C", r_t);
+ ocg->add_field(ocg, 0, "CMYK_M", r_t);
+ ocg->add_field(ocg, 0, "CMYK_Y", r_t);
+ ocg->add_field(ocg, 0, "CMYK_K", r_t);
+ ocg->add_field(ocg, 0, "STDEV_C", r_t);
+ ocg->add_field(ocg, 0, "STDEV_M", r_t);
+ ocg->add_field(ocg, 0, "STDEV_Y", r_t);
+ ocg->add_field(ocg, 0, "STDEV_K", r_t);
+ }
+ nsetel += 2 * depth;
+
+ if ((setel = (cgats_set_elem *)malloc(sizeof(cgats_set_elem) * nsetel)) == NULL)
+ error("Malloc failed!");
+
+ /* Initialise, ready to read out all the values */
+ for (j = 0; j < npat; j++) {
+ char id[100]; /* Input patch id */
+
+ /* Normalise labels */
+ fix_it8(id,((char *)icg->t[0].fdata[j][sx])); /* Copy and fix */
+
+ /* Search for matching id */
+ for (i = sr->reset(sr); i > 0; i--) {
+ char tod[100]; /* Output patch id */
+ char od[100]; /* Output patch id */
+ double P[4]; /* Robust/true mean values */
+ double sdP[4]; /* Standard deviation */
+ int pixcnt; /* Pixel count */
+
+ if (tmean)
+ sr->read(sr, tod, NULL, P, sdP, &pixcnt);
+ else
+ sr->read(sr, tod, P, NULL, sdP, &pixcnt);
+
+ if (pixcnt == 0)
+ pnotscan++;
+
+ fix_it8(od,tod);
+
+ if (strcmp(id,od) == 0) {
+ int k = 0, m;
+ double XYZ[3];
+
+ setel[k++].c = id;
+
+ XYZ[0] = *((double *)icg->t[0].fdata[j][Xx]);
+ XYZ[1] = *((double *)icg->t[0].fdata[j][Yx]);
+ XYZ[2] = *((double *)icg->t[0].fdata[j][Zx]);
+ if (isLab) {
+ icmLab2XYZ(&icmD50, XYZ, XYZ);
+ XYZ[0] *= 100.0;
+ XYZ[1] *= 100.0;
+ XYZ[2] *= 100.0;
+ }
+
+ setel[k++].d = XYZ[0];
+ setel[k++].d = XYZ[1];
+ setel[k++].d = XYZ[2];
+
+ if (spec_n > 0) {
+ for (m = 0; m < spec_n; m++) {
+ setel[k++].d = *((double *)icg->t[0].fdata[j][spi[m]]);
+ }
+ }
+
+ for (m = 0; m < depth; m++)
+ setel[k++].d = P[m] * 100.0/255.0;
+ for (m = 0; m < depth; m++)
+ setel[k++].d = sdP[m] * 100.0/255.0;
+
+ ocg->add_setarr(ocg, 0, setel);
+
+ break;
+ }
+ }
+ if (i <= 0 && verb >= 1)
+ printf("Warning: Couldn't match field '%s'\n",id);
+ }
+
+ if (verb)
+ printf("Writing output values to file '%s'\n",datout_name);
+
+ if (ocg->write_name(ocg, datout_name))
+ error("Output file '%s' write error : %s",datout_name, ocg->err);
+
+ free(setel);
+
+ ocg->del(ocg); /* Clean up */
+ icg->del(icg); /* Clean up */
+
+ }
+ }
+
+ if (pnotscan > 0)
+ warning("A total of %d patches had no value set!",pnotscan);
+
+ /* Clean up */
+ sr->free(sr);
+
+ TIFFClose(rh);
+
+ if (flags & SI_SHOW_FLAGS)
+ TIFFClose(wh);
+
+ return 0;
+}
+
+/* Fix IT8 chart labels */
+void fix_it8(char *o, char *i) {
+ if (strcmp(i,"Dmin")==0) {
+ strcpy(o,"GS00");
+ return;
+ }
+ if (strcmp(i,"Dmax")==0) {
+ strcpy(o,"GS23");
+ return;
+ }
+ while (!isdigit(*i) && *i != '\000') /* Skip non-numbers */
+ *o++ = *i++;
+ if (i[0] != '\000' && i[1] == '\000') /* Single last digit */
+ *o++ = '0'; /* Add leading zero */
+ strcpy(o, i); /* Copy remainder */
+}
+
+/********************************************************************************/
diff --git a/scanin/scanrd.c b/scanin/scanrd.c
new file mode 100644
index 0000000..a915e44
--- /dev/null
+++ b/scanin/scanrd.c
@@ -0,0 +1,4659 @@
+
+/*
+ * Raster Color Target Scan Input module
+ * This is the core chart recognition code.
+ *
+ * Author: Graeme Gill
+ *
+ * Copyright 1995 - 2008 Graeme W. Gill, All right reserved.
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+/*
+ * To Do:
+ * Add option to output a raster file made from the .cht and example values.
+ *
+ * Fix sboxes parameters/digitization to fix "droop" in box areas.
+ * Scale parameters with image size.
+ * To handle high res, introduce automatic sub-sampler.
+ * Change reference parser to make it more forgiving - use cgats parser ?
+ */
+
+#undef DEBUG
+
+#define VERSION "1.0"
+
+/* Behaviour defines */
+#undef DIAGN /* Allow diagonal connectivity of groups */
+#define AA_LINES /* Plot diagnostics using anti-aliased lines */
+
+#define MATCHCC 0.3 /* Match correlation threshold - reject any match under this */
+ /* (Might want to be able to override this in command line) */
+
+#define ALT_ROT_TH 0.7 /* Correlation threshold of alternate rotations to be greater than this */
+
+#define TH (20.0 * 20.0) /* Initial color change threshhold */
+
+#undef DBG
+#define dbgo stdout
+#define DBG(aaa) fprintf aaa, fflush(dbgo)
+
+#include <stdio.h>
+/* #include <fcntl.h> */ /* In case DOS binary stuff is needed */
+#include <string.h>
+#include <math.h>
+
+#include <stdlib.h>
+#include <sys/stat.h>
+/* #include <fname.h> */
+
+#include "numlib.h"
+#include "scanrd_.h"
+
+/* ------------------------------------------------- */
+/* Implementations of public functions */
+static void free_scanrd(scanrd *s);
+static int scanrd_reset(scanrd *s);
+static int scanrd_read(scanrd *ps, char *id, double *P, double *mP,
+ double *sdP, int *cnt);
+static unsigned int scanrd_error(scanrd *s, char **errm);
+
+/* Forward internal function declaration */
+static scanrd_ *new_scanrd(int flags, int verb, double gammav,
+ int (*write_line)(void *ddata, int y, char *src), void *ddata,
+ int w, int h, int d, int td, int p,
+ int (*read_line)(void *fdata, int y, char *dst), void *fdata,
+ char *refname);
+static int read_input(scanrd_ *s);
+static int calc_lines(scanrd_ *s);
+static int show_lines(scanrd_ *s);
+static int calc_perspective(scanrd_ *s);
+static int calc_rotation(scanrd_ *s);
+static int calc_elists(scanrd_ *s, int ref);
+static int write_elists(scanrd_ *s);
+static int read_relists(scanrd_ *s);
+static int do_match(scanrd_ *s);
+static int compute_ptrans(scanrd_ *s);
+static int compute_man_ptrans(scanrd_ *s, double *sfids);
+static int improve_match(scanrd_ *s);
+static int setup_sboxes(scanrd_ *s);
+static int do_value_scan(scanrd_ *s);
+static int compute_xcc(scanrd_ *s);
+//static int restore_best(scanrd_ *s);
+static int show_sbox(scanrd_ *s);
+static int show_groups(scanrd_ *s);
+static int scanrd_write_diag(scanrd_ *s);
+static void toRGB(unsigned char *dst, unsigned char *src, int depth, int bpp);
+static void XYZ2Lab(double *out, double *in);
+static void pval2Lab(double *out, double *in, int depth);
+/* ------------------------------------------------- */
+
+/* Read in a chart, and either create a reference or make values available, */
+/* by using reset() and read() to get values read */
+scanrd *do_scanrd(
+int flags, /* option flags */
+int verb, /* verbosity level */
+
+double gammav, /* Apprimate gamma encoding of image (0.0 = default 2.2) */
+double *sfid, /* Specified four fiducials x1, y1 .. x4, y4, NULL if auto recognition */
+ /* Typical clockwise from top left */
+
+int w, int h, /* Width and Height of input raster in pixels */
+int d, int td, int p, /* Useful plane depth, Total depth, Bit presision of input pixels */
+int (*read_line)(void *fdata, int y, char *dst), /* Read RGB line of source file */
+void *fdata, /* Opaque data for read_line */
+
+char *refname, /* reference file name */
+
+int (*write_line)(void *ddata, int y, char *src), /* Write RGB line of diag file */
+void *ddata /* Opaque data for write_line */
+) {
+ scanrd_ *s;
+
+ /* allocate the basic object */
+ if (verb >= 2)
+ DBG((dbgo,"About to allocate scanrd_ object\n"));
+ if ((s = new_scanrd(flags, verb, gammav, write_line, ddata, w, h, d, td, p, read_line, fdata, refname)) == NULL)
+ return NULL;
+
+ if (s->errv != 0) /* Some other error from new_scanrd() */
+ return (scanrd *)s;
+
+ if (s->verb >= 2)
+ DBG((dbgo,"About to read input tiff file and discover groups\n"));
+ if (read_input(s))
+ goto sierr; /* Error */
+
+ if (s->flags & SI_SHOW_GROUPS)
+ if (show_groups(s))
+ goto sierr; /* Error */
+
+ if (s->verb >= 2)
+ DBG((dbgo,"About to calculate edge lines\n"));
+ if (calc_lines(s))
+ goto sierr; /* Error */
+ if (s->verb >= 2)
+ DBG((dbgo,"%d useful edges out of %d\n",s->novlines, s->noslines));
+
+ if (s->flags & SI_PERSPECTIVE) {
+ if (s->verb >= 2)
+ DBG((dbgo,"About to calculate perspective correction\n"));
+ if (calc_perspective(s)) {
+ if (s->flags & SI_SHOW_LINES) {
+ s->flags &= ~SI_SHOW_PERS; /* Calc perspective failed! */
+ s->flags &= ~SI_SHOW_ROT; /* Calc rotation not done! */
+ show_lines(s);
+ }
+ goto sierr; /* Error */
+ }
+ }
+
+ if (s->verb >= 2)
+ DBG((dbgo,"About to calculate rotation\n"));
+ if (calc_rotation(s)) {
+ if (s->flags & SI_SHOW_LINES) {
+ s->flags &= ~SI_SHOW_ROT; /* Calc rotation failed! */
+ show_lines(s);
+ }
+ goto sierr; /* Error */
+ }
+
+ if (s->flags & SI_BUILD_REF) { /* If generating a chart reference file */
+ /* Calculate the edge lists and write it to the file */
+ if (s->verb >= 2)
+ DBG((dbgo,"About to build feature information\n"));
+ if (calc_elists(s, 1)) /* reference */
+ goto sierr; /* Error */
+
+ if (s->verb >= 2)
+ DBG((dbgo,"About to write feature reference information\n"));
+ if (write_elists(s))
+ goto sierr; /* Error */
+ } else {
+ /* If we are matching to the reference and generating an output data file */
+ int rv;
+
+ /* Calculate the edge lists read for a match */
+ if (s->verb >= 2)
+ DBG((dbgo,"About to calculate feature information\n"));
+ if (calc_elists(s, 0)) /* match */
+ goto sierr; /* Error */
+
+ if (s->verb >= 2)
+ DBG((dbgo,"About to read reference feature information\n"));
+ if (read_relists(s))
+ goto sierr; /* Error */
+ if (s->verb >= 2)
+ DBG((dbgo,"Read of chart reference file succeeded\n"));
+
+ if (sfid != NULL) { /* Manual matching */
+ if (s->verb >= 2)
+ DBG((dbgo,"Using manual matching\n"));
+
+ if (s->havefids == 0) {
+ s->errv = SI_NO_FIDUCIALS_ERR;
+ sprintf(s->errm,"Chart recognition definition file doesn't contain fiducials");
+ goto sierr; /* Error */
+ }
+ if (compute_man_ptrans(s, sfid))
+ goto sierr;
+
+ /* Do the actual scan given out manual transformation matrix */
+ if (s->verb >= 2)
+ DBG((dbgo,"About to setup value scanrdg boxes\n"));
+ if (setup_sboxes(s))
+ goto sierr;
+ if (s->verb >= 2)
+ DBG((dbgo,"About to read raster values\n"));
+ if (do_value_scan(s))
+ goto sierr;
+
+ } else { /* Automatic matching */
+
+ /* Attempt to match input file with reference */
+ if (s->verb >= 2)
+ DBG((dbgo,"About to match features\n"));
+ if ((rv = do_match(s)) != 0) {
+ if (rv == 1) { /* No reasonable rotation found */
+ s->errv = SI_POOR_MATCH;
+ sprintf(s->errm,"Pattern match wasn't good enough");
+ }
+ goto sierr;
+ }
+
+ /* If there is patch matching data and more than one */
+ /* feasible matching rotation, try and discriminate between them. */
+ if (s->xpt && s->norots > 1) {
+ int i, j;
+ int flags = s->flags;
+
+ s->flags &= ~SI_SHOW_SAMPLED_AREA; /* Don't show areas for trials */
+
+ /* For each candidate rotation, scan in the pixel values */
+ for (s->crot = 0; s->crot < s->norots; s->crot++) {
+
+ /* Compute transformation from reference to input file */
+ if (s->verb >= 2)
+ DBG((dbgo,"About to compute match transform for rotation %f deg.\n",
+ DEG(s->rots[s->crot].irot)));
+ if (compute_ptrans(s))
+ goto sierr;
+
+ /* Setup the input boxes ready for scanning in the input values */
+ if (s->verb >= 2)
+ DBG((dbgo,"About to setup value scanrdg boxes\n"));
+ if (setup_sboxes(s))
+ goto sierr;
+
+ /* Scan in the pixel values */
+ if (s->verb >= 2)
+ DBG((dbgo,"About to read raster values\n"));
+ if (do_value_scan(s))
+ goto sierr;
+
+ /* Copy to this rotation values so that the best can be restored */
+ if (s->xpt != 0) { /* Got expected patch values to compare with */
+ if (s->verb >= 2)
+ DBG((dbgo,"About to compute expected value correlation\n"));
+ if (compute_xcc(s))
+ goto sierr;
+ }
+ }
+
+ /* Pick the best from the candidate rotation */
+ if (s->verb >= 2) {
+ DBG((dbgo,"Expected value distance values are:\n"));
+ for (i = 0; i < s->norots; i++) {
+ DBG((dbgo,"%d, rot %f: %f\n", i, DEG(s->rots[i].irot), s->rots[i].xcc));
+ }
+ }
+
+ for (j = 0, i = 1; i < s->norots; i++) {
+ if (s->rots[i].xcc < s->rots[j].xcc)
+ j = i;
+ }
+
+ if (s->verb >= 2)
+ DBG((dbgo,"Chosen rotation %f deg. as best\n",DEG(s->rots[j].irot)));
+
+ s->crot = j;
+ s->flags = flags; /* Restore flags */
+ }
+
+ /* Setup transformation to be that for chosen rotation for diagnostics */
+ if (s->verb >= 2)
+ DBG((dbgo,"About to compute final match transform\n"));
+ if (compute_ptrans(s))
+ goto sierr;
+
+ if (s->verb >= 2)
+ DBG((dbgo,"Improve match\n"));
+ if (improve_match(s))
+ goto sierr;
+
+ /* After choosing rotation of improving the fit, rescan the values */
+ if (s->verb >= 2)
+ DBG((dbgo,"About to setup value scanrdg boxes\n"));
+ if (setup_sboxes(s))
+ goto sierr;
+ if (s->verb >= 2)
+ DBG((dbgo,"About to read raster values\n"));
+ if (do_value_scan(s))
+ goto sierr;
+ }
+
+ if (s->flags & SI_SHOW_SBOX) {
+ show_sbox(s); /* Draw sample box outlines on diagnostic raster */
+ }
+ }
+ if (s->flags & SI_SHOW_LINES)
+ if(show_lines(s))
+ goto sierr; /* Error */
+
+sierr:;
+ if (s->verb >= 2)
+ DBG((dbgo,"About to write diag file\n"));
+ if (scanrd_write_diag(s))
+ return (scanrd *)s; /* Error */
+
+ return (scanrd *)s;
+}
+
+
+/********************************************************************************/
+
+/* Allocate the basic scanrd object */
+/* Return NULL on failure to allocate */
+/* Need to check errv for other problems */
+static scanrd_
+*new_scanrd(
+ int flags, /* option flags */
+ int verb, /* verbosity level */
+ double gammav, /* Approximate gamma encoding of image (0.0 = default 2.2) */
+ int (*write_line)(void *ddata, int y, char *src), /* Write RGB line of diag file */
+ void *ddata, /* Opaque data for write_line() */
+ int w, int h, /* Width and Height of input raster in pixels */
+ int d, int td, int p, /* Useful plane Depth, Total depth, Bit presision of input pixels */
+ int (*read_line)(void *fdata, int y, char *dst), /* Read RGB line of source file */
+ void *fdata, /* Opaque data for read_line() */
+
+ char *refname /* reference file name */
+) {
+ scanrd_ *s;
+
+ if ((s = (scanrd_ *)calloc(1, sizeof(scanrd_))) == NULL)
+ return NULL;
+
+ /* Public functions */
+ s->public.reset = scanrd_reset;
+ s->public.read = scanrd_read;
+ s->public.error = scanrd_error;
+ s->public.free = free_scanrd;
+
+ if (flags & (SI_SHOW_ROT | SI_SHOW_PERS | SI_SHOW_IMPL | SI_SHOW_ALL_LINES))
+ flags |= SI_SHOW_LINES; /* Key all line stuff off SI_SHOW_LINES */
+
+ if (flags & (SI_SHOW_SBOX_OUTLINES | SI_SHOW_SBOX_NAMES | SI_SHOW_SBOX_AREAS))
+ flags |= SI_SHOW_SBOX;; /* Key all sample box stuff off SI_SHOW_SBOX */
+
+ if (write_line == NULL)
+ flags &= ~SI_SHOW_FLAGS; /* If no diag file, turn off show flags */
+
+ s->flags = flags;
+ s->verb = verb;
+
+ s->errv = 0;
+ s->errm[0] = '\0';
+
+ if (gammav <= 0.0)
+ gammav = 2.2; /* default */
+ s->gammav = gammav;
+ s->width = w;
+ s->height = h;
+ s->depth = d;
+ s->tdepth = td;
+ s->bpp = p;
+
+ if (d > MXDE) {
+ s->errv = SI_PIX_DEPTH_ERR;
+ sprintf(s->errm,"scanrd: Pixel depth is too large");
+ return s;
+ }
+
+ if (p != 8 && p != 16) {
+ s->errv = SI_BIT_DEPTH_ERR;
+ sprintf(s->errm,"scanrd: Pixel bits/pixel is not 8 or 16");
+ return s;
+ }
+ if (p == 8)
+ s->bypp = 1;
+ else
+ s->bypp = 2;
+
+ if (verb >= 2)
+ DBG((dbgo,"Verbosity = %d, flags = 0x%x\n",verb, flags));
+
+ /* RGB Diagnostic output raster array requested */
+ if ((flags & SI_SHOW_FLAGS) && write_line != NULL) {
+ if ((s->out = malloc(3 * w * h)) == NULL) {
+ s->errv = SI_MALLOC_DIAG_RAST;
+ sprintf(s->errm,"scanrd: Diagnostic output raster array malloc failed");
+ return s;
+ }
+ }
+
+ s->noslines = 0;
+ s->novlines = 0;
+ s->gdone = NULL;
+ s->irot = 0.0;
+ s->norots = 0;
+
+ s->ppc[0] = 0.0;
+ s->ppc[1] = 0.0;
+ s->ppc[2] = 0.0;
+ s->ppc[3] = 0.0;
+
+ /* Set overall perspective transform to null */
+ s->ptrans[0] = 1.0;
+ s->ptrans[1] = 0.0;
+ s->ptrans[2] = 0.0;
+ s->ptrans[3] = 0.0;
+ s->ptrans[4] = 1.0;
+ s->ptrans[5] = 0.0;
+ s->ptrans[6] = 0.0;
+ s->ptrans[7] = 0.0;
+
+ INIT_ELIST(s->xelist);
+ INIT_ELIST(s->yelist);
+ INIT_ELIST(s->ixelist);
+ INIT_ELIST(s->iyelist);
+ INIT_ELIST(s->rxelist);
+ INIT_ELIST(s->ryelist);
+ s->rbox_shrink = 0.9;
+ s->xpt = 0;
+
+ s->nsbox = 0;
+ s->sboxes = NULL;
+ s->sbstart = NULL;
+ s->sbend = NULL;
+ s->csi = 0;
+ s->cei = 0;
+ s->alist = NULL;
+
+ s->next_read = 0;
+
+ s->refname = refname;
+
+ s->inited = 0;
+ s->vrego = s->vregn = NULL;
+ s->no_vo = s->no_vn = 0;
+ s->hrego = s->hregn = NULL;
+ s->no_ho = s->no_hn = 0;
+ s->th = TH;
+ s->divval = 0.25;
+ s->adivval = 0.0;
+ s->divc = 0;
+
+ /* aa line init */
+ s->aa_inited = 0; /* Let line init do the rest */
+ s->coverage = NULL;
+
+ /* Callbacks */
+ s->read_line = read_line;
+ s->fdata = fdata;
+
+ s->write_line = write_line;
+ s->ddata = ddata;
+
+ return s;
+}
+
+static void free_elist_array(elist *el);
+
+/* Free the object up */
+static void
+free_scanrd(
+scanrd *ps
+) {
+ scanrd_ *s = (scanrd_ *)ps; /* Cast public to private */
+ points *tp;
+
+ free_elist_array(&s->xelist);
+ free_elist_array(&s->yelist);
+ free_elist_array(&s->ixelist);
+ free_elist_array(&s->iyelist);
+ free_elist_array(&s->rxelist);
+ free_elist_array(&s->ryelist);
+
+ if (s->sboxes != NULL)
+ free(s->sboxes);
+ if (s->sbstart != NULL)
+ free(s->sbstart);
+ if (s->sbend != NULL)
+ free(s->sbend);
+ s->alist = NULL;
+
+ /* Free up done line list */
+ tp = s->gdone;
+ FOR_ALL_ITEMS(points, tp)
+ if (tp->r != NULL)
+ free(tp->r);
+ free(tp);
+ END_FOR_ALL_ITEMS(tp);
+ s->gdone = NULL;
+
+ /* Points were deleted with gdone ??? */
+ if(s->vrego != NULL)
+ free(s->vrego);
+ if(s->vregn)
+ free(s->vregn);
+ if(s->hrego != NULL)
+ free(s->hrego);
+ if(s->hregn != NULL)
+ free(s->hregn);
+ s->inited = 1;
+
+ /* Free up output diag array */
+ if (s->out != NULL)
+ free(s->out);
+
+ /* Free up aa line array */
+ if (s->coverage != NULL)
+ free(s->coverage);
+ free(s);
+}
+
+
+/* Return the error flag, and set the message pointer */
+static unsigned int
+scanrd_error(scanrd *ps, char **errm) {
+ scanrd_ *s = (scanrd_ *)ps; /* Cast public to private */
+ *errm = s->errm;
+ return s->errv;
+}
+
+/********************************************************************************/
+static int analize(scanrd_ *s, unsigned char *inp[6], int y);
+
+/* Read in and process the input file */
+/* Return non-zero on error */
+static int
+read_input(scanrd_ *s) {
+ unsigned char *in[6]; /* Pointer to six input buffers */
+ int w = s->width; /* Raster width */
+ int h = s->height; /* Raster height */
+ int i, y;
+
+ /* Allocate input line buffers */
+ for (i = 0; i < 6; i++) {
+ if ((in[i] = malloc(s->tdepth * w * s->bypp)) == NULL) {
+ s->errv = SI_MALLOC_INPUT_BUF;
+ sprintf(s->errm,"scanrd: Failed to malloc input line buffers");
+ return 1;
+ }
+ }
+
+ /* Prime the input buffers with 5 lines */
+ for (y = 0; y < 5; y++) {
+ if (s->read_line(s->fdata, y, (char *)in[y])) {
+ s->errv = SI_RAST_READ_ERR;
+ sprintf(s->errm,"scanrd: read_line() returned error");
+ return 1;
+ }
+ }
+ /* Process the tiff file line by line (Assume at least 6 lines in total raster) */
+ for (; y < h; ++y) {
+ unsigned char *tt;
+ if (s->read_line(s->fdata, y, (char *)in[5])) {
+ s->errv = SI_RAST_READ_ERR;
+ sprintf(s->errm,"scanrd: read_line() returned error");
+ return 1;
+ }
+
+ if (analize(s, in, y)) {
+ return 1;
+ }
+
+ tt = in[0]; /* Shuffle buffers about */
+ in[0] = in[1];
+ in[1] = in[2];
+ in[2] = in[3];
+ in[3] = in[4];
+ in[4] = in[5];
+ in[5] = tt;
+
+ }
+ s->adivval /= (double)s->divc; /* Average divider value, 1.0 = 0 degrees, 0.0 = 45 degrees */
+ if (s->adivval < 0.0)
+ s->adivval = 0.0;
+ else if (s->adivval > 1.0)
+ s->adivval = 1.0;
+
+ if (s->verb >= 2)
+ DBG((dbgo,"adivval = %f\n",s->adivval));
+
+ /* Free the input line buffers */
+ for (i = 0; i < 6; i++)
+ free(in[i]);
+
+ return 0;
+}
+
+/********************************************************************************/
+
+#ifdef NEVER /* Before 22/5/2004 */
+#define THRN 1.0 /* Threshold above average ratio - numerator */
+#define THRD 2.0 /* Threshold above average ratio - denominator */
+
+#define THAWF 1.0 /* Threshold average adaptation filter weight, fixed value (TH) */
+#define THAWP 4.0 /* Threshold average adaptation filter weight, previous value */
+#define THAWN 1.0 /* Threshold average adaptation filter weight, new value */
+
+#else /* Current values */
+
+#define THRN 1.0 /* Threshold above average ratio - numerator */
+#define THRD 1.5 /* Threshold above average ratio - denominator */
+
+#define THAWF 1.0 /* Threshold average adaptation filter weight, fixed value (TH) */
+#define THAWP 5.0 /* Threshold average adaptation filter weight, previous value */
+#define THAWN 1.0 /* Threshold average adaptation filter weight, new value */
+
+#endif
+
+/* ~~~ minimum raster size needs to be specified/checked ~~~~ */
+#define MIN_NO_LINES 16 /* Minimum number of valid fitted lines to estimate rotation */
+
+/* Criteria for accepting lines for angle calculation (valid lines) */
+#define MAX_MWID_TO_LEN 0.1
+#define MIN_POINT_TO_AREA 0.9 /* Minimum point desity over the lines area */
+#define SD_WINDOW 1.5 /* Allow += 1.5 of a standard deviation for robust angle calc. */
+#define ELISTCDIST 800 /* 1/ELISTCDIST = portion of refence edge list legth to coalesce over */
+
+/* Criteria for accepting lines for improring final fit */
+#define IMP_MATCH 0.10 /* Proportion of average tick spacing */
+
+/* The following should be scaled to the resolution of the image ? */
+#define MIN_POINTS 10 /* Minimum points to calculate line */
+#define MIN_LINE_LENGTH 10.0
+#define CUT_CHUNKS 128 /* cut groups along diagonals - must be power of 2 */
+
+static int add_region(scanrd_ *s, region *rego, int no_o, region *regn, int no_n, int y);
+
+/* Process a line of the TIFF file */
+/* return non-zero on error */
+static int
+analize(
+scanrd_ *s,
+unsigned char *inp[6], /* current and previous 5 lines */
+int y /* Current line y */
+) {
+ int w = s->width;
+ int stride = s->tdepth * s->width; /* In pixels */
+ unsigned short *gamma = s->gamma;
+ int x,i;
+ unsigned short *inp2[6]; /* current and previous 5 lines (16bpp) equivalent of inp[] */
+ unsigned char *in[6]; /* six input lines (8bpp) */
+ unsigned short *in2[6]; /* six input lines (16bpp) */
+ region *tr;
+ double tdh,tdv; /* Horizontal/virtical detect levels */
+ double tdmag;
+ double atdmag = 0.0; /* Average magnitude over a line */
+ int atdmagc = 0; /* Average magnitude over a line count */
+ double linedv = 0.0; /* Lines average divider value */
+ int linedc = 0; /* Lines average count */
+ int xo3 = s->tdepth * 3; /* Xoffset by 3 pixels */
+ int xo2 = s->tdepth * 2; /* Xoffset by 2 pixels */
+ int xo1 = s->tdepth * 1; /* Xoffset by 1 pixels */
+
+ for (x = 0; x < 6; x++) /* Create 16 bpp version of line pointers */
+ inp2[x] = (unsigned short *)inp[x];
+
+ if (s->inited == 0) {
+ /* Init gamma conversion lookup and region tracking. */
+ /* The assumption is that a typical chart has an approx. visually */
+ /* uniform distribution of samples, so that a typically gamma */
+ /* encoded scan image will have an average pixel value of 50%. */
+ /* If a the chart has a different gamma encoding (ie. linear), */
+ /* then we convert it to gamma 2.2 encoded to (hopefuly) enhance */
+ /* the patch contrast. */
+ if (s->bpp == 8)
+ for (i = 0; i < 256; i++) {
+ int byteb1;
+
+ byteb1 = (int)(0.5 + 255 * pow( i / 255.0, s->gammav/2.2 ));
+ gamma[i] = byteb1;
+ }
+ else
+ for (i = 0; i < 65536; i++) {
+ int byteb1;
+
+ byteb1 = (int)(0.5 + 65535 * pow( i / 65535.0, s->gammav/2.2 ));
+ gamma[i] = byteb1;
+ }
+
+ if ((s->vrego = (region *) malloc(sizeof(region) * (w+1)/2)) == NULL) {
+ s->errv = SI_MALLOC_VREGION;
+ sprintf(s->errm,"vreg malloc failed");
+ return 1;
+ }
+ s->no_vo = 0;
+ if ((s->vregn = (region *) malloc(sizeof(region) * (w+1)/2)) == NULL) {
+ s->errv = SI_MALLOC_VREGION;
+ sprintf(s->errm,"vreg malloc failed");
+ return 1;
+ }
+ s->no_vn = 0;
+ if ((s->hrego = (region *) malloc(sizeof(region) * (w+1)/2)) == NULL) {
+ s->errv = SI_MALLOC_VREGION;
+ sprintf(s->errm,"vreg malloc failed");
+ return 1;
+ }
+ s->no_ho = 0;
+ if ((s->hregn = (region *) malloc(sizeof(region) * (w+1)/2)) == NULL) {
+ s->errv = SI_MALLOC_VREGION;
+ sprintf(s->errm,"vreg malloc failed");
+ return 1;
+ }
+ s->no_hn = 0;
+ INIT_LIST(s->gdone);
+ s->inited = 1;
+ }
+
+ /* Un-gamma correct the latest input line */
+ if (s->bpp == 8)
+ for (x = 0; x < stride; x++)
+ inp[5][x] = (unsigned char)gamma[inp[5][x]];
+ else
+ for (x = 0; x < stride; x++)
+ inp2[5][x] = gamma[inp2[5][x]];
+
+ /* Compute difference output for line y-3 */
+ atdmagc = w - 5; /* Magnitude count (to compute average) */
+ for (x = 3; x < (w-2); x++) { /* Allow for -3 to +2 from x */
+ unsigned char *out = s->out;
+ int e;
+ int ss;
+ int idx = ((y-2) * w + x) * 3; /* Output raster index in bytes */
+
+ if (s->bpp == 8)
+ for (i = 0; i < 6; i++)
+ in[i] = inp[i] + x * s->tdepth; /* Strength reduce */
+ else
+ for (i = 0; i < 6; i++) {
+ in2[i] = inp2[i] + x * s->tdepth; /* Strength reduce */
+ in[i] = (unsigned char *)in2[i]; /* track 8bpp pointers */
+ }
+
+ if (s->flags & SI_SHOW_IMAGE) { /* Create B&W image */
+ toRGB(out + idx, in[2], s->depth, s->bpp); /* Convert to RGB */
+ out[idx] = out[idx+1] = out[idx+2] = (2 * out[idx] + 7 * out[idx+1] + out[idx+2])/10;
+ }
+
+ ss = 0; /* Sign of cross components the same vote */
+ tdh = tdv = 0.0;
+
+ if (s->bpp == 8)
+ for (e = 0; e < s->depth; e++) {
+ int d1,d2;
+ /* Compute Gxp */
+ d1 = -in[0][-xo3+e] + -in[0][-xo2+e] + -in[0][-xo1+e]
+ + -in[0][ 0+e] + -in[0][ xo1+e] + -in[0][ xo2+e]
+ + -in[1][-xo3+e] + -in[1][-xo2+e] + -in[1][-xo1+e]
+ + -in[1][ 0+e] + -in[1][ xo1+e] + -in[1][ xo2+e]
+ + -in[2][-xo3+e] + -in[2][-xo2+e] + -in[2][-xo1+e]
+ + -in[2][ 0+e] + -in[2][ xo1+e] + -in[2][ xo2+e]
+ + in[3][-xo3+e] + in[3][-xo2+e] + in[3][-xo1+e]
+ + in[3][ 0+e] + in[3][ xo1+e] + in[3][ xo2+e]
+ + in[4][-xo3+e] + in[4][-xo2+e] + in[4][-xo1+e]
+ + in[4][ 0+e] + in[4][ xo1+e] + in[4][ xo2+e]
+ + in[5][-xo3+e] + in[5][-xo2+e] + in[5][-xo1+e]
+ + in[5][ 0+e] + in[5][ xo1+e] + in[5][ xo2+e];
+ /* Compute Gyp */
+ d2 = -in[0][-xo3+e] + -in[1][-xo3+e] + -in[2][-xo3+e]
+ + -in[3][-xo3+e] + -in[4][-xo3+e] + -in[5][-xo3+e]
+ + -in[0][-xo2+e] + -in[1][-xo2+e] + -in[2][-xo2+e]
+ + -in[3][-xo2+e] + -in[4][-xo2+e] + -in[5][-xo2+e]
+ + -in[0][-xo1+e] + -in[1][-xo1+e] + -in[2][-xo1+e]
+ + -in[3][-xo1+e] + -in[4][-xo1+e] + -in[5][-xo1+e]
+ + in[0][ 0+e] + in[1][ 0+e] + in[2][ 0+e]
+ + in[3][ 0+e] + in[4][ 0+e] + in[5][ 0+e]
+ + in[0][+xo1+e] + in[1][+xo1+e] + in[2][+xo1+e]
+ + in[3][+xo1+e] + in[4][+xo1+e] + in[5][+xo1+e]
+ + in[0][+xo2+e] + in[1][+xo2+e] + in[2][+xo2+e]
+ + in[3][+xo2+e] + in[4][+xo2+e] + in[5][+xo2+e];
+
+ if ((d1 >= 0 && d2 >=0)
+ || (d1 < 0 && d2 < 0))
+ ss++; /* Sign was the same */
+ tdh += d1/4.5 * d1/4.5; /* (4.5 = 6x6/4x2, to scale original tuned values) */
+ tdv += d2/4.5 * d2/4.5;
+ }
+ else
+ for (e = 0; e < s->depth; e++) {
+ int d1,d2;
+ /* Compute Gxp */
+ d1 = -in2[0][-xo3+e] + -in2[0][-xo2+e] + -in2[0][-xo1+e]
+ + -in2[0][ 0+e] + -in2[0][ xo1+e] + -in2[0][ xo2+e]
+ + -in2[1][-xo3+e] + -in2[1][-xo2+e] + -in2[1][-xo1+e]
+ + -in2[1][ 0+e] + -in2[1][ xo1+e] + -in2[1][ xo2+e]
+ + -in2[2][-xo3+e] + -in2[2][-xo2+e] + -in2[2][-xo1+e]
+ + -in2[2][ 0+e] + -in2[2][ xo1+e] + -in2[2][ xo2+e]
+ + in2[3][-xo3+e] + in2[3][-xo2+e] + in2[3][-xo1+e]
+ + in2[3][ 0+e] + in2[3][ xo1+e] + in2[3][ xo2+e]
+ + in2[4][-xo3+e] + in2[4][-xo2+e] + in2[4][-xo1+e]
+ + in2[4][ 0+e] + in2[4][ xo1+e] + in2[4][ xo2+e]
+ + in2[5][-xo3+e] + in2[5][-xo2+e] + in2[5][-xo1+e]
+ + in2[5][ 0+e] + in2[5][ xo1+e] + in2[5][ xo2+e];
+ /* Compute Gyp */
+ d2 = -in2[0][-xo3+e] + -in2[1][-xo3+e] + -in2[2][-xo3+e]
+ + -in2[3][-xo3+e] + -in2[4][-xo3+e] + -in2[5][-xo3+e]
+ + -in2[0][-xo2+e] + -in2[1][-xo2+e] + -in2[2][-xo2+e]
+ + -in2[3][-xo2+e] + -in2[4][-xo2+e] + -in2[5][-xo2+e]
+ + -in2[0][-xo1+e] + -in2[1][-xo1+e] + -in2[2][-xo1+e]
+ + -in2[3][-xo1+e] + -in2[4][-xo1+e] + -in2[5][-xo1+e]
+ + in2[0][ 0+e] + in2[1][ 0+e] + in2[2][ 0+e]
+ + in2[3][ 0+e] + in2[4][ 0+e] + in2[5][ 0+e]
+ + in2[0][+xo1+e] + in2[1][+xo1+e] + in2[2][+xo1+e]
+ + in2[3][+xo1+e] + in2[4][+xo1+e] + in2[5][+xo1+e]
+ + in2[0][+xo2+e] + in2[1][+xo2+e] + in2[2][+xo2+e]
+ + in2[3][+xo2+e] + in2[4][+xo2+e] + in2[5][+xo2+e];
+
+ if ((d1 >= 0 && d2 >=0)
+ || (d1 < 0 && d2 < 0))
+ ss++; /* Sign was the same */
+
+ tdh += d1/(4.5 * 257) * d1/(4.5 * 257); /* Scale to 0..255 range */
+ tdv += d2/(4.5 * 257) * d2/(4.5 * 257);
+ }
+
+ tdmag = tdh + tdv;
+
+ if (tdmag < (32.0 * s->th))
+ atdmag += tdmag; /* Average magnitude over a line */
+ else
+ atdmag += 32.0 * s->th;
+
+ /* if over threshold */
+ /* (Cut long lines up to prevent long lines being */
+ /* (thrown away due to attached blobs) */
+ if (tdmag >= s->th
+ && (x & (CUT_CHUNKS-1)) != (y & (CUT_CHUNKS-1))) {
+ double tt;
+ double av; /* Angle value of current pixel */
+ tt = (tdv - tdh)/(tdh + tdv); /* Partial angle */
+ linedv += fabs(tt);
+ linedc++;
+
+ if (ss >= (s->depth/2+1)) /* Assume signs are the same if clear majority */
+ av = 3.0 + tt;
+ else
+ av = 1.0 - tt;
+
+ /* Separate the orthogonal elements */
+ if (av >= s->divval && av < (s->divval + 2.0)) {
+ if (s->flags & SI_SHOW_DIFFSH)
+ out[idx] = (char)255; /* Red */
+ /* Add point to new region */
+ /* See if we can add to last region */
+ if (s->no_hn > 0 && x == s->hregn[s->no_hn-1].hx)
+ s->hregn[s->no_hn-1].hx++;
+ else { /* Add another */
+ if (s->no_hn >= (w+1)/2) {
+ s->errv = SI_INTERNAL;
+ sprintf(s->errm,"Internal, no_hn is too large");
+ return 1;
+ }
+ s->hregn[s->no_hn].lx = x;
+ s->hregn[s->no_hn].hx = x+1;
+ s->hregn[s->no_hn].p = NULL;
+ s->no_hn++;
+ }
+ } else {
+ if (s->flags & SI_SHOW_DIFFSV)
+ out[idx+1] = (char)255; /* Green */
+ /* Add point to new region */
+ /* See if we can add to last region */
+ if (s->no_vn > 0 && x == s->vregn[s->no_vn-1].hx)
+ s->vregn[s->no_vn-1].hx++;
+ else { /* Add another */
+ if (s->no_vn >= (w+1)/2) {
+ s->errv = SI_INTERNAL;
+ sprintf(s->errm,"Internal, no_vn is too large");
+ return 1;
+ }
+ s->vregn[s->no_vn].lx = x;
+ s->vregn[s->no_vn].hx = x+1;
+ s->vregn[s->no_vn].p = NULL;
+ s->no_vn++;
+ }
+ }
+ }
+ }
+
+ if (linedc != 0) { /* Adapt divider value to line */
+ linedv /= (double)linedc; /* Compute average over the line */
+ linedv = (linedv * linedv); /* Square to even out linedv vs angle */
+ linedv = (1.65 * (linedv - 0.12)); /* Compensate for random offsets */
+ s->adivval += linedv;
+ s->divc++;
+ s->divval = (7.0 * s->divval + linedv)/8.0; /* Average over 8 lines */
+ if (s->divval < 0.0)
+ s->divval = 0.0;
+ else if (s->divval > 1.0)
+ s->divval = 1.0;
+ if (s->verb >= 5)
+ DBG((dbgo,"linedv = %f, divval = %f\n",linedv,s->divval));
+ }
+
+ /* Adjust the threshold */
+ atdmag /= (double)atdmagc; /* compute average magnitude over the line */
+ s->th = (s->th * THRD)/(THRN + s->divval);/* Convert threshold to average */
+ s->th = ((THAWF * TH) + (THAWP * s->th) + (THAWN * atdmag))/(THAWF + THAWP + THAWN);
+ s->th = (s->th * (THRN + s->divval))/THRD; /* Convert average back to threshold */
+
+ /* Add vertical regions */
+ if (add_region(s,s->vrego,s->no_vo,s->vregn,s->no_vn,y-2))
+ return 1;
+
+ /* Add horizontal regions */
+ if (add_region(s,s->hrego,s->no_ho,s->hregn,s->no_hn,y-2))
+ return 1;
+
+ /* shuffle them along */
+ tr = s->vrego;
+ s->vrego = s->vregn; /* move new to old */
+ s->vregn = tr; /* old to new */
+ s->no_vo = s->no_vn;
+ s->no_vn = 0;
+
+ tr = s->hrego;
+ s->hrego = s->hregn; /* move new to old */
+ s->hregn = tr; /* old to new */
+ s->no_ho = s->no_hn;
+ s->no_hn = 0;
+
+ return 0;
+}
+
+/********************************************************************************/
+/* Point list code */
+
+/* allocate a new (empty) points structure */
+/* return NULL on error */
+static points *
+new_points(
+scanrd_ *s
+) {
+ points *ps;
+ static int pn = 0;
+ if ((ps = (points *) malloc(sizeof(points))) == NULL) {
+ s->errv = SI_MALLOC_POINTS;
+ sprintf(s->errm,"new_points: malloc failed");
+ return NULL;
+ }
+ ps->mxno = 0;
+ ps->no = 0;
+ ps->nop = 0;
+ ps->r = NULL;
+ ps->pn = pn;
+ pn++;
+ return ps;
+}
+
+/* destroy a points structure */
+static void
+destroy_points(
+scanrd_ *s,
+points *ps) {
+ if (ps->r != NULL) /* Free any array pointed to */
+ free(ps->r);
+ free (ps);
+}
+
+/* Add another run to a points object */
+/* return non-zero on error */
+static int
+add_run(
+scanrd_ *s,
+points *ps,
+int lx,
+int hx,
+int y)
+ {
+ if (ps->no == ps->mxno) { /* Need some more space */
+ ps->mxno = (2 * ps->mxno) + 5; /* New size */
+ if ((ps->r = (run *) realloc(ps->r, sizeof(run) * ps->mxno)) == NULL) {
+ s->errv = SI_REALLOC_POINTS;
+ sprintf(s->errm,"add_run: realloc failed");
+ return 1;
+ }
+ }
+ ps->r[ps->no].lx = lx;
+ ps->r[ps->no].hx = hx;
+ ps->r[ps->no].y = y;
+ ps->no++; /* One more run */
+ ps->nop += hx - lx; /* Total of pixels */
+ return 0;
+}
+
+/* copy src points to dest */
+/* Return non-zero on error */
+static int
+copy_points(
+scanrd_ *s,
+points *dst,
+points *src
+) {
+ int i;
+ for (i = 0; i < src->no; i++) {
+ if (add_run(s,dst,src->r[i].lx,src->r[i].hx,src->r[i].y))
+ return 1;
+ }
+ return 0;
+}
+
+/********************************************************************************/
+
+/* Add a new region of points to the line points lists */
+/* Note that regions are assumed to be non-overlapping x sorted */
+/* Return non-zero on error */
+static int
+add_region(
+scanrd_ *s,
+region *rego, /* Old regions */
+int no_o, /* No of old region */
+region *regn, /* New regions */
+int no_n, /* No of new region */
+int y /* Y value */
+) {
+ int osp,op,np; /* Old/new pointers */
+
+ osp = 0;
+ for (np = 0; np < no_n; np++) { /* Process all new runs */
+ /* Advance start pointer until we get to runs that may touch */
+#ifdef DIAGN
+ while (osp < no_o && rego[osp].hx < regn[np].lx)
+#else
+ while (osp < no_o && rego[osp].hx <= regn[np].lx)
+#endif
+ osp++;
+ /* For all old runs that may touch new */
+#ifdef DIAGN
+ for(op = osp; op < no_o && rego[op].lx <= regn[np].hx; op++) {
+#else
+ for(op = osp; op < no_o && rego[op].lx < regn[np].hx; op++) {
+#endif
+
+#ifdef DIAGN
+ if (rego[op].hx >= regn[np].lx && rego[op].lx <= regn[np].hx) {
+#else
+ if (rego[op].hx > regn[np].lx && rego[op].lx < regn[np].hx) {
+#endif
+ /* Old region touches new */
+ if (regn[np].p == NULL) { /* No group for new yet */
+ regn[np].p = rego[op].p; /* Make part of the same group */
+ if (add_run(s, regn[np].p,regn[np].lx,regn[np].hx,y)) /* add new run to group */
+ return 1;
+ } else if (regn[np].p != rego[op].p) { /* Touches different group */
+ int j;
+ points *tp = rego[op].p; /* Old region to be renamed/merged */
+ if (copy_points(s,regn[np].p,tp)) /* Merge old with current new */
+ return 1; /* Error */
+ DEL_LINK(s->gdone,tp); /* Don't need other any more */
+ for (j = 0; j < no_o; j++) /* Fix all references to this group */
+ if (rego[j].p == tp)
+ rego[j].p = regn[np].p;
+ for (j = 0; j < no_n; j++)
+ if (regn[j].p == tp)
+ regn[j].p = regn[np].p;
+ destroy_points(s,tp);
+ }
+ }
+ }
+ /* Finished all relevant old runs */
+ if (regn[np].p == NULL) { /* No old touched, so start new group */
+ if ((regn[np].p = new_points(s)) == NULL)
+ return 1; /* Error */
+ ADD_ITEM_TO_TOP(s->gdone,regn[np].p); /* Stash it in points list */
+ if (add_run(s, regn[np].p,regn[np].lx,regn[np].hx,y)) /* add new run to group */
+ return 1; /* Error */
+ }
+ }
+ return 0;
+}
+
+/********************************************************************************/
+
+/* Apply partial perspective to an xy point */
+/* (We omit the two offset parameters, since we don't need them) */
+void ppersp(scanrd_ *s, double *xx, double *yy, double x, double y, double *ppc) {
+ double den;
+
+ /* Offset the partial perspective transform */
+ x -= ppc[2];
+ y -= ppc[3];
+
+ den = ppc[0] * x + ppc[1] * y + 1.0;
+
+ if (fabs(den) < 1e-6) {
+ if (den < 0.0)
+ den = -1e-6;
+ else
+ den = 1e-6;
+ }
+ *xx = x/den + ppc[2];
+ *yy = y/den + ppc[3];
+}
+
+
+/* Apply inverse partial perspective to an xy point */
+void invppersp(scanrd_ *s, double *x, double *y, double xx, double yy, double *ppc) {
+ double den;
+
+ /* Offset the partial perspective transform */
+ xx -= ppc[2];
+ yy -= ppc[3];
+
+ den = - ppc[0] * xx - ppc[1] * yy + 1.0;
+
+ if (fabs(den) < 1e-6) {
+ if (den < 0.0)
+ den = -1e-6;
+ else
+ den = 1e-6;
+ }
+ *x = xx/den + ppc[2];
+ *y = yy/den + ppc[3];
+}
+
+/********************************************************************************/
+
+/* Compute the least squares best line fit for a group */
+/* Return non-zero if failed */
+static int
+points_to_line(
+scanrd_ *s,
+points *ps) {
+ int i,j;
+ point *vv; /* Point vectors */
+ int nop = ps->nop; /* Number of points */
+ double sx,sy; /* Sum */
+ double mx,my; /* Mean */
+ double a; /* Angle, Clockwise from 12o'clock */
+ double mw,len; /* mean width, length */
+ double x1,y1,x2,y2; /* Start/end point of fitted line */
+
+ ps->flag = 0;
+
+ if (nop < MIN_POINTS) /* Don't bother if too few pixels */
+ return 0;
+
+ /* Convert runs to individual points, and compute mean */
+ if ((vv = (point *) malloc(sizeof(point) * nop)) == NULL) {
+ s->errv = SI_MALLOC_POINT2LINE;
+ sprintf(s->errm,"scanrd: points_to_line: malloc failed");
+ return 1;
+ }
+
+ sx = sy = 0.0;
+ for (j = i = 0; i < ps->no; i++) { /* For all runs */
+ int x,y;
+ int hx = ps->r[i].hx, lx = ps->r[i].lx;
+
+ y = ps->r[i].y;
+ sy += (hx - lx) * y;
+ for (x = lx; x < hx; x++, j++) { /* Convert to points */
+ sx += x;
+ vv[j].x = x;
+ vv[j].y = y;
+ }
+ }
+ mx = sx/(double)nop; /* Centroid (mean) of points */
+ my = sy/(double)nop;
+
+ /* Offset points to centroid */
+ for (i=0; i < nop; i++) {
+ vv[i].x -= mx;
+ vv[i].y -= my;
+ }
+
+ /* Compute ad and bd, then A, B, C */
+ /* From Graphics Gems V, pp 91-97, */
+ /* "The Best Least-Squares Line Fit" */
+ /* by David Alciatore and Rick Miranda. */
+ {
+ double ad, bd; /* a' and b' values */
+ double xd, yd; /* temp x' and y' */
+ double A, B; /* line equation */
+ double abn; /* A & B normalizer */
+
+ xd = yd = bd = 0.0;
+ for (i = 0; i < nop; i++) {
+ double x, y;
+
+ x = vv[i].x;
+ y = vv[i].y;
+ xd += x * x;
+ yd += y * y;
+ bd += x * y;
+ }
+ ad = xd - yd;
+
+ /* Equation of best fit line is Ax + By = C */
+ A = 2 * bd;
+ B = -(ad + sqrt(ad * ad + 4.0 * bd * bd));
+ /* C = A * mx + B * my; */
+
+ /* Compute angle */
+ /* A = abn * cos(a), B = -abn * sin(a) */
+
+ abn = sqrt(A * A + B * B); /* Normalize A & B */
+ if (fabs(abn) < 1e-6) { /* No dominant direction */
+ a = 0.0;
+ } else {
+ a = acos(A/abn);
+ }
+ /* Make angle +ve */
+ while (a < 0.0) a += M_PI;
+ }
+
+ /* Now figure out the bounding box for the line + other stats */
+ {
+ double s,c;
+ double pl,nl; /* Positive length, negative length */
+ s = sin(a);
+ c = cos(a);
+ for (mw = 0.0, pl = 0.0, nl = 0.0, i = 0; i < nop; i++)
+ {
+ double npj; /* Projection onto normal */
+ double lpj; /* Projection onto line */
+ npj = -c * vv[i].x + s * vv[i].y;
+ if (npj < 0)
+ mw -= npj;
+ else
+ mw += npj;
+ lpj = s * vv[i].x + c * vv[i].y;
+ if (lpj > pl)
+ pl = lpj;
+ if (lpj < nl)
+ nl = lpj;
+ }
+ mw = 2.0 * mw/(double)nop; /* Mean width */
+
+ x1 = mx + s * nl;
+ y1 = my + c * nl;
+ x2 = mx + s * pl;
+ y2 = my + c * pl;
+ len = pl - nl;
+ }
+
+ ps->mx = mx; /* Mean point */
+ ps->my = my;
+ ps->a = a; /* Angle */
+ ps->mw = mw; /* Mean width */
+ ps->len = len; /* Mean length */
+ ps->x1 = x1; /* Start/end point of fitted line */
+ ps->y1 = y1;
+ ps->x2 = x2;
+ ps->y2 = y2;
+ ps->flag = F_LINESTATS; /* Line stats valid */
+
+ /* Compute the Constrained to 90 degrees angle */
+ /* We use the adivval to figure out where to split angles */
+ /* Split at 0 if adivval == 0.0, split at 45 if adivval == 1.0 */
+ if (a >= (M_PI * (1.0 - s->adivval/4.0)))
+ ps->ca = a - M_PI;
+ else if (a >= (M_PI * (0.5 - s->adivval/4.0)))
+ ps->ca = a - M_PI_2;
+ else
+ ps->ca = a;
+
+ if (s->verb >= 5)
+ DBG((dbgo,"Angle %f, CA = %f, length = %f, mean width = %f, Line %f,%f to %f,%f\n",
+ DEG(a),DEG(ps->ca),len,mw,x1,y1,x2,y2));
+ free(vv);
+
+/* printf("~~stats: mw = %f, len = %f, mw/len = %f, area = %f\n",
+ mw, len, mw/len, ((double)nop/(len * (mw + 0.01)))); */
+ /* Look at stats to see what lines are acceptable for further processing */
+ if ( len >= MIN_LINE_LENGTH
+ && mw/len <= MAX_MWID_TO_LEN
+ && ((double)nop/(len * (mw + 0.01))) >= MIN_POINT_TO_AREA) {
+ ps->flag |= F_VALID; /* Line stats valid to use */
+/* printf("~~set valid\n"); */
+ }
+ return 0;
+}
+
+static int
+calc_lines(
+scanrd_ *s
+) {
+ points *tp;
+ s->noslines = 0;
+ s->novlines = 0;
+ tp = s->gdone;
+ FOR_ALL_ITEMS(points, tp)
+ if (points_to_line(s,tp))
+ return 1; /* Error */
+ if (tp->flag & F_LINESTATS) /* Line stats valid */
+ s->noslines++;
+ if (tp->flag & F_VALID) /* Valid for angle calcs */
+ s->novlines++;
+
+ /* Save orininal raster (non partial perspective corrected) values */
+ if (tp->flag & F_VALID) {
+ tp->pmx = tp->mx;
+ tp->pmy = tp->my;
+ tp->px1 = tp->x1;
+ tp->py1 = tp->y1;
+ tp->px2 = tp->x2;
+ tp->py2 = tp->y2;
+ }
+ END_FOR_ALL_ITEMS(tp);
+ return 0;
+}
+
+static int show_line(scanrd_ *s, int x1, int y1, int x2, int y2, unsigned long c);
+
+/* Show the edge detected lines */
+static int
+show_lines(
+scanrd_ *s
+) {
+ points *tp;
+ int outw = s->width;
+ int outh = s->height;
+ /* For SI_SHOW_ROT */
+ double cirot,sirot; /* cos and sin of -irot */
+ cirot = cos(-s->irot);
+ sirot = sin(-s->irot);
+
+ tp = s->gdone;
+ FOR_ALL_ITEMS(points, tp)
+ if ((s->flags & SI_SHOW_ALL_LINES) || (tp->flag & F_VALID))
+ {
+ unsigned long col = 0xffffff; /* default color is white */
+ double x1 = tp->px1, y1 = tp->py1, x2 = tp->px2, y2 = tp->py2;
+ /* For SI_SHOW_ROT */
+
+ /* Show partial perspective corrected lines */
+ if (s->flags & (SI_SHOW_ROT | SI_SHOW_PERS)) {
+ invppersp(s, &x1, &y1, x1, y1, s->ppc);
+ invppersp(s, &x2, &y2, x2, y2, s->ppc);
+ col = 0xffff00; /* cyan */
+ }
+
+ /* Show rotation correction of lines + color coding yellow and red */
+ if (s->flags & SI_SHOW_ROT) {
+ double tx1, ty1, tx2, ty2;
+ double a = tp->a - s->irot;
+
+ tx1 = x1;
+ ty1 = y1;
+ tx2 = x2;
+ ty2 = y2;
+
+ /* Rotate about center of raster */
+ x1 = (tx1-outw/2.0) * cirot + (ty1-outh/2.0) * sirot;
+ y1 = -(tx1-outw/2.0) * sirot + (ty1-outh/2.0) * cirot;
+ x2 = (tx2-outw/2.0) * cirot + (ty2-outh/2.0) * sirot;
+ y2 = -(tx2-outw/2.0) * sirot + (ty2-outh/2.0) * cirot;
+
+ x1 += outw/2.0; /* Rotate about center of raster */
+ y1 += outh/2.0;
+ x2 += outw/2.0;
+ y2 += outh/2.0;
+ if ((a >= -0.08 && a <= 0.08) || (a >= (M_PI-0.08) && a <= (M_PI+0.08))
+ || (a >= (M_PI_2-0.08) && a <= (M_PI_2+0.08)))
+ col = 0x00ffff; /* yellow */
+ else
+ col = 0x0000ff; /* Red */
+ }
+ /* Show just lines used for fit improvement in blue */
+ if (s->flags & SI_SHOW_IMPL) {
+ if (tp->flag & F_IMPROVE)
+ col = 0xff4040; /* blue */
+ }
+ show_line(s,(int)(x1+0.5),(int)(y1+0.5),(int)(x2+0.5),(int)(y2+0.5),col);
+ }
+ END_FOR_ALL_ITEMS(tp);
+ return 0;
+}
+
+
+/********************************************************************************/
+
+/* Definition of the optimization function handed to powell() */
+static double
+pfunc(void *ss, double p[]) {
+ scanrd_ *s = (scanrd_ *)ss;
+ points *tp;
+ double aa; /* Average angle */
+ double va, rva; /* Variance */
+ double wt; /* Total weighting = sum of line lengths */
+ double pw;
+ double dw; /* Discrimination width */
+
+//printf("~1 %f %f %f %f %f %f\n", p[0],p[1],p[2],p[3],p[4],p[5]);
+
+ /* Correct the perspective of all the edge lines using the parameters */
+ /* and compute the mean angle */
+ aa = 0.0; /* Average constrained angle */
+ wt = 0.0; /* Total weighting = sum of line lengths */
+ tp = s->gdone;
+ FOR_ALL_ITEMS(points, tp)
+ if (tp->flag & F_LONGENOUGH) {
+ double a, ca;
+ invppersp(s, &tp->x1, &tp->y1, tp->px1, tp->py1, p);
+ invppersp(s, &tp->x2, &tp->y2, tp->px2, tp->py2, p);
+
+ /* Compute the angle */
+ a = atan2(tp->x2 - tp->x1,tp->y2 - tp->y1);
+
+ /* Make angle +ve */
+ while (a < 0.0)
+ a += M_PI;
+
+ /* Compute the Constrained to 90 degrees angle */
+ /* We use the adivval to figure out where to split angles */
+ /* Split at 0 if adivval == 0.0, split at 45 if adivval == 1.0 */
+ if (a >= (M_PI * (1.0 - s->adivval/4.0)))
+ ca = a - M_PI;
+ else if (a >= (M_PI * (0.5 - s->adivval/4.0)))
+ ca = a - M_PI_2;
+ else
+ ca = a;
+
+ tp->a = a;
+ tp->ca = ca;
+
+ aa += tp->len * ca;
+ wt += tp->len;
+ }
+ END_FOR_ALL_ITEMS(tp);
+ aa /= wt;
+
+ /* Calculate the angle variance */
+ va = 0.0;
+ tp = s->gdone;
+ wt = 0.0;
+ FOR_ALL_ITEMS(points, tp)
+ if (tp->flag & F_LONGENOUGH) {
+ double tt;
+ tt = tp->ca - aa;
+ va += tp->len * tt * tt;
+ wt += tp->len;
+ }
+ END_FOR_ALL_ITEMS(tp);
+ va = va/wt;
+
+ /* Calculate the a robust angle variance */
+ rva = 0.0;
+ wt = 0.0;
+ dw = sqrt(va) * 3.1; /* Allow += 0.5 of a standard deviation */
+ if (dw < 0.0001) /* A perfect chart may have dw of zero */
+ dw = 0.0001;
+ tp = s->gdone;
+ FOR_ALL_ITEMS(points, tp)
+ if (tp->flag & F_LONGENOUGH && fabs(tp->ca - aa) <= dw) {
+ double tt;
+ tt = tp->ca - aa;
+ rva += tp->len * tt * tt;
+ wt += tp->len;
+ }
+ END_FOR_ALL_ITEMS(tp);
+ if (wt > 0.0) {
+ rva = rva/wt;
+ va = rva;
+ }
+
+ /* Add some regularization to stop it going crazy */
+ pw = 0.0;
+ pw += 0.01 * (fabs(p[0]) + fabs(p[1]));
+ pw += 0.0001 * (fabs(p[2]/s->width - 0.5) + fabs(p[3]/s->height - 0.5));
+ va += pw;
+
+ return va;
+}
+
+/* Calculate the partial perspective correction factors */
+/* Return non-zero if failed */
+static int
+calc_perspective(
+scanrd_ *s
+) {
+ points *tp;
+ int nl; /* Number of lines used */
+ double ml; /* Minimum length */
+ double pc[4]; /* Perspective factors */
+ double ss[4]; /* Initial search distance */
+ double rv; /* Return value */
+ int rc = 0; /* Return code */
+
+ if (s->novlines < MIN_NO_LINES) {
+ s->errv = SI_FIND_PERSPECTIVE_FAILED;
+ sprintf(s->errm,"Not enough valid lines to compute perspective");
+ return 1;
+ }
+
+ /* Find the longest line */
+ ml = 0.0;
+ tp = s->gdone;
+ FOR_ALL_ITEMS(points, tp)
+ if (tp->flag & F_VALID) {
+ if (tp->len > ml)
+ ml = tp->len;
+ }
+ END_FOR_ALL_ITEMS(tp);
+
+ /* Make minimum line length to be included in angle */
+ /* calculation 1% of longest line */
+ ml *= 0.01;
+
+ /* Mark lines long enough to participate in angle calculation */
+ tp = s->gdone;
+ FOR_ALL_ITEMS(points, tp)
+ if (tp->flag & F_VALID && tp->len >= ml)
+ tp->flag |= F_LONGENOUGH;
+ END_FOR_ALL_ITEMS(tp);
+
+ /* Locate the perspective correction factors that minimze the */
+ /* variance of the mean angle. */
+
+ pc[0] = 0.0;
+ pc[1] = 0.0;
+ pc[2] = 0.5 * s->width;
+ pc[3] = 0.5 * s->height;
+
+ ss[0] = 0.0001;
+ ss[1] = 0.0001;
+ ss[2] = 1.0001;
+ ss[3] = 1.0001;
+ rc = powell(&rv, 4, pc,ss,1e-8,2000,pfunc,s, NULL, NULL);
+
+ if (rc == 0) {
+ points *tp;
+
+ DBG((dbgo,"Perspective correction factors = %f %f %f %f\n",
+ pc[0],pc[1],pc[2],pc[3]));
+
+ s->ppc[0] = pc[0];
+ s->ppc[1] = pc[1];
+ s->ppc[2] = pc[2];
+ s->ppc[3] = pc[3];
+
+ /* Implement the perspective correction */
+ tp = s->gdone;
+ FOR_ALL_ITEMS(points, tp)
+ if (tp->flag & F_LONGENOUGH) {
+ double a, ca;
+ invppersp(s, &tp->x1, &tp->y1, tp->px1, tp->py1, s->ppc);
+ invppersp(s, &tp->x2, &tp->y2, tp->px2, tp->py2, s->ppc);
+ tp->mx = 0.5 * (tp->x2 + tp->x1);
+ tp->my = 0.5 * (tp->y2 + tp->y1);
+ tp->len = sqrt((tp->x2 - tp->x1) * (tp->x2 - tp->x1)
+ + (tp->y2 - tp->y1) * (tp->y2 - tp->y1));
+
+ /* Compute the angle */
+ a = atan2(tp->x2 - tp->x1,tp->y2 - tp->y1);
+
+ /* Make angle +ve */
+ while (a < 0.0)
+ a += M_PI;
+
+ /* Compute the Constrained to 90 degrees angle */
+ /* We use the adivval to figure out where to split angles */
+ /* Split at 0 if adivval == 0.0, split at 45 if adivval == 1.0 */
+ if (a >= (M_PI * (1.0 - s->adivval/4.0)))
+ ca = a - M_PI;
+ else if (a >= (M_PI * (0.5 - s->adivval/4.0)))
+ ca = a - M_PI_2;
+ else
+ ca = a;
+
+ tp->a = a;
+ tp->ca = ca;
+ }
+ END_FOR_ALL_ITEMS(tp);
+ }
+
+ return 0;
+}
+
+/********************************************************************************/
+/* Calculate the image rotation */
+/* Return non-zero if failed */
+static int
+calc_rotation(
+scanrd_ *s
+) {
+ points *tp;
+ int nl; /* Number of lines used */
+ double ml; /* Minimum length */
+ double aa; /* Average angle */
+ double sd,dw; /* Standard deviation, deviation window */
+ double wt; /* Total weighting = sum of line lengths */
+
+ if (s->novlines < MIN_NO_LINES) {
+ s->errv = SI_FIND_ROTATION_FAILED;
+ sprintf(s->errm,"Not enough valid lines to compute rotation angle");
+ return 1;
+ }
+
+ /* Find the longest line */
+ tp = s->gdone;
+ ml = 0.0;
+ FOR_ALL_ITEMS(points, tp)
+ if (tp->flag & F_VALID) {
+ if (tp->len > ml)
+ ml = tp->len;
+ }
+ END_FOR_ALL_ITEMS(tp);
+
+ /* Make minimum line length to be included in angle */
+ /* calculation 1% of longest line */
+ ml *= 0.01;
+
+ /* Calculate the mean angle */
+ aa = 0.0;
+ wt = 0.0; /* Total weighting = sum of line lengths */
+ tp = s->gdone;
+ FOR_ALL_ITEMS(points, tp)
+ if (tp->flag & F_VALID && tp->len >= ml) {
+ aa += tp->len * tp->ca;
+ wt += tp->len;
+ }
+ END_FOR_ALL_ITEMS(tp);
+ aa /= wt;
+
+ if (s->verb >= 2)
+ DBG((dbgo,"Mean angle = %f\n",DEG(aa)));
+
+ /* Calculate the angle standard deviation */
+ tp = s->gdone;
+ sd = 0.0;
+ FOR_ALL_ITEMS(points, tp)
+ if (tp->flag & F_VALID && tp->len >= ml) {
+ double tt;
+ tt = tp->ca - aa;
+ sd += tp->len * tt * tt;
+ }
+ END_FOR_ALL_ITEMS(tp);
+
+ sd = sqrt(sd/wt);
+
+ if (s->verb >= 2)
+ DBG((dbgo,"Standard deviation = %f\n",DEG(sd)));
+
+ /* Now re-compute the angle while rejecting any that fall outside one standard deviation */
+ s->irot = 0.0;
+ wt = 0.0; /* Total weighting = sum of line lengths */
+ nl = 0;
+ dw = sd * SD_WINDOW; /* Allow += 0.5 of a standard deviation */
+ if (dw < 0.01) /* A perfect chart may have dw of zero */
+ dw = 0.01;
+ tp = s->gdone;
+ FOR_ALL_ITEMS(points, tp)
+ if (tp->flag & F_VALID && tp->len >= ml && fabs(tp->ca - aa) <= dw) {
+ s->irot += tp->len * tp->ca;
+ wt += tp->len;
+ nl++;
+ }
+ END_FOR_ALL_ITEMS(tp);
+ if (nl < (MIN_NO_LINES/2)) {
+ s->errv = SI_FIND_ROTATION_FAILED;
+ sprintf(s->errm,"%d consistent lines is not enough to compute rotation angle",nl);
+ return 1;
+ }
+ s->irot /= wt;
+
+ if (s->verb >= 2)
+ DBG((dbgo,"Robust mean angle = %f from %d lines\n",DEG(s->irot),nl));
+
+ return 0;
+}
+
+/********************************************************************************/
+/* Coalesce close entries of an edge list */
+/* return non-zero on error */
+static int
+coalesce_elist(
+scanrd_ *s,
+elist *el,
+int close /* Closeness factor, smaller = coarser */
+) {
+ double r; /* Margin for coalescence */
+ int i,k;
+
+ if (el->c < 2) /* Need at least 2 entries */
+ return 0;
+
+ r = (el->a[el->c-1].pos - el->a[0].pos)/(double)close;
+ for (k = 0, i = 1; i < el->c; i++) {
+ if ((el->a[i].pos - el->a[k].pos) <= r) {
+ /* Merge the two */
+ double lk = el->a[k].len;
+ double li = el->a[i].len;
+ el->a[k].pos = (el->a[k].pos * lk + el->a[i].pos * li)/(lk + li);
+ el->a[k].len = lk + li;
+ if (el->a[k].p1 > el->a[i].p1) /* Track overall start/end points */
+ el->a[k].p1 = el->a[i].p1;
+ if (el->a[k].p2 < el->a[i].p2)
+ el->a[k].p2 = el->a[i].p2;
+ continue;
+ }
+ k++; /* Inc destination pointer */
+ if (k != i)
+ el->a[k] = el->a[i]; /* shuffle data down */
+ }
+ k++; /* one past last out entry */
+ el->c = k;
+ return 0;
+}
+
+static int invert_elist(scanrd_ *s, elist *dl, elist *sl);
+static void debug_elist(scanrd_ *s, elist *el);
+
+/* Make up the x and y edge lists */
+/* Return non-zero if failed */
+static int
+calc_elists(
+scanrd_ *s,
+int ref /* 1 if generating reference lists */
+) {
+ int outw = s->width;
+ int outh = s->height;
+ points *tp;
+ int i,j;
+ double cirot,sirot; /* cos and sin of -irot */
+ elist xl, yl; /* Temporary X and Y edge lists array */
+ elist tl; /* temporary crossing list */
+
+ /* Allocate structures for edge lists */
+ if ((xl.a = (epoint *) malloc(sizeof(epoint) * s->novlines)) == NULL) {
+ s->errv = SI_MALLOC_ELIST;
+ sprintf(s->errm,"scanrd: calc_elist: malloc failed - novlines = %d",s->novlines);
+ return 1;
+ }
+ xl.c = 0;
+ if ((yl.a = (epoint *) malloc(sizeof(epoint) * s->novlines)) == NULL) {
+ s->errv = SI_MALLOC_ELIST;
+ sprintf(s->errm,"scanrd: calc_elist: malloc failed - novlines = %d",s->novlines);
+ return 1;
+ }
+ yl.c = 0;
+
+ /* Put valid lines into one of the two edge list arrays */
+ cirot = cos(-s->irot);
+ sirot = sin(-s->irot);
+ tp = s->gdone;
+ FOR_ALL_ITEMS(points, tp)
+ if (tp->flag & F_VALID) {
+ /* Rotate the point about 0,0 by angle -irot */
+ double x,y,a;
+ double mx = tp->mx, my = tp->my;
+
+ if (ref) { /* Rotate about center of raster for reference generation */
+ mx -= outw/2.0; /* Rotate about center of raster */
+ my -= outh/2.0;
+ x = mx * cirot + my * sirot + outw/2.0;
+ y = -mx * sirot + my * cirot + outh/2.0;
+ } else { /* Rotate about 0,0 for matching */
+ x = mx * cirot + my * sirot;
+ y = -mx * sirot + my * cirot;
+ }
+ a = tp->a - s->irot;
+ if ((a >= -0.08 && a <= 0.08) || (a >= (M_PI-0.08) && a <= (M_PI+0.08))) {
+ xl.a[xl.c].pos = x;
+ xl.a[xl.c].len = tp->len;
+ xl.a[xl.c].p1 = y - tp->len/2.0;
+ xl.a[xl.c].p2 = y + tp->len/2.0;
+ xl.c++;
+ } else if (a >= (M_PI_2-0.08) && a <= (M_PI_2+0.08)) {
+ yl.a[yl.c].pos = y;
+ yl.a[yl.c].len = tp->len;
+ yl.a[yl.c].p1 = x - tp->len/2.0;
+ yl.a[yl.c].p2 = x + tp->len/2.0;
+ yl.c++;
+ }
+ }
+ END_FOR_ALL_ITEMS(tp);
+
+ /* ~~~~ need to check that lists have a reasonable number of entries ~~~~~ */
+
+ /* now sort the lists */
+#define HEAP_COMPARE(A,B) (A.pos < B.pos)
+ HEAPSORT(epoint,xl.a,xl.c);
+ HEAPSORT(epoint,yl.a,yl.c);
+#undef HEAP_COMPARE
+
+ /* Copy the temporary lists to the real lists */
+ if ((s->xelist.a = (epoint *) malloc(sizeof(epoint) * xl.c)) == NULL) {
+ s->errv = SI_MALLOC_ELIST;
+ sprintf(s->errm,"scanrd: calc_elist: malloc failed, xl.c = %d",xl.c);
+ return 1;
+ }
+ s->xelist.c = xl.c;
+ for (i=0; i < xl.c; i++)
+ s->xelist.a[i] = xl.a[i];
+ if ((s->yelist.a = (epoint *) malloc(sizeof(epoint) * yl.c)) == NULL) {
+ s->errv = SI_MALLOC_ELIST;
+ sprintf(s->errm,"scanrd: calc_elist: malloc failed, yl.c = %d",yl.c);
+ return 1;
+ }
+ s->yelist.c = yl.c;
+ for (i=0; i < yl.c; i++)
+ s->yelist.a[i] = yl.a[i];
+
+ /* Coalese close entries of the final lists */
+ if (coalesce_elist(s, &s->xelist,ELISTCDIST))
+ return 1;
+ if (coalesce_elist(s, &s->yelist,ELISTCDIST))
+ return 1;
+
+ /* Calculate crossing count for lines in the X and y lists */
+ if ((tl.a = (epoint *) malloc(sizeof(epoint) * (xl.c > yl.c ? xl.c : yl.c))) == NULL) {
+ s->errv = SI_MALLOC_ELIST;
+ sprintf(s->errm,"scanrd: calc_elist: malloc failed, xl.c = %d, yl.c = %d",xl.c,yl.c);
+ return 1;
+ }
+ /* X list */
+ for (i = 0; i < s->xelist.c; i++) {
+ double ppos = s->xelist.a[i].pos;
+ double pp,np; /* Previous and next pos */
+ if ((i-1) >= 0)
+ pp = (ppos + s->xelist.a[i-1].pos)/2.0; /* Half distance to next line */
+ else
+ pp = -1e6;
+ if ((i+1) < s->xelist.c)
+ np = (ppos + s->xelist.a[i+1].pos)/2.0; /* Half distance to next line */
+ else
+ np = 1e6;
+
+ /* For all the lines in the Y list */
+ for (tl.c = j = 0; j < yl.c; j++) {
+ double pos = yl.a[j].pos;
+ double p1 = yl.a[j].p1;
+ double p2 = yl.a[j].p2;
+ if (p1 <= pp)
+ p1 = pp;
+ if (p2 >= np)
+ p2 = np;
+ /* If crosses on this lines X within +-0.5 of line each side */
+ if (p1 <= np && p2 >= pp) {
+ tl.a[tl.c].pos = pos;
+ tl.a[tl.c].len = p2 - p1;
+ tl.a[tl.c].p1 = p1;
+ tl.a[tl.c].p2 = p2;
+ tl.c++;
+ }
+ }
+ /* now coalesce the crossings */
+ if (coalesce_elist(s,&tl,200))
+ return 1;
+ /* Put count in line we're working on */
+ s->xelist.a[i].ccount = (double)tl.c;
+ pp = ppos;
+ }
+
+ /* Y list */
+ for (i = 0; i < s->yelist.c; i++) {
+ double ppos = s->yelist.a[i].pos;
+ double pp,np; /* Previous and next pos */
+ if ((i-1) >= 0)
+ pp = (ppos + s->yelist.a[i-1].pos)/2.0; /* Half distance to next line */
+ else
+ pp = -1e6;
+ if ((i+1) < s->xelist.c)
+ np = (ppos + s->yelist.a[i+1].pos)/2.0; /* Half distance to next line */
+ else
+ np = 1e6;
+
+ for (tl.c = j = 0; j < xl.c; j++) {
+ double pos = xl.a[j].pos;
+ double p1 = xl.a[j].p1;
+ double p2 = xl.a[j].p2;
+ if (p1 <= pp)
+ p1 = pp;
+ if (p2 >= np)
+ p2 = np;
+ /* If crosses on this lines Y within +-0.5 of line each side */
+ if (p1 <= np && p2 >= pp) {
+ tl.a[tl.c].pos = pos;
+ tl.a[tl.c].len = p2 - p1;
+ tl.a[tl.c].p1 = p1;
+ tl.a[tl.c].p2 = p2;
+ tl.c++;
+ }
+ }
+ /* now coalesce the crossings */
+ if (coalesce_elist(s,&tl,200))
+ return 1;
+ /* Put count in line we're working on */
+ s->yelist.a[i].ccount = (double)tl.c;
+ pp = ppos;
+ }
+
+ /* Normalize the length and ccount */
+ {
+ double tlen; /* Total length maximum */
+ double tcmax; /* Total count maximum */
+ for (tlen = tcmax = 0.0, i=0; i < s->xelist.c; i++) {
+ if (tlen < s->xelist.a[i].len)
+ tlen = s->xelist.a[i].len;
+ if (tcmax < s->xelist.a[i].ccount)
+ tcmax = s->xelist.a[i].ccount;
+ }
+ for (i=0; i < s->xelist.c; i++) {
+ s->xelist.a[i].len /= tlen;
+ s->xelist.a[i].ccount /= tcmax;
+ }
+ for (tlen = tcmax = 0.0, i=0; i < s->yelist.c; i++) {
+ if (tlen < s->yelist.a[i].len)
+ tlen = s->yelist.a[i].len;
+ if (tcmax < s->yelist.a[i].ccount)
+ tcmax = s->yelist.a[i].ccount;
+ }
+ for (i=0; i < s->yelist.c; i++) {
+ s->yelist.a[i].len /= tlen;
+ s->yelist.a[i].ccount /= tcmax;
+ }
+ }
+
+ /* Create the inverted lists for any rotation matching */
+ if (invert_elist(s, &s->ixelist, &s->xelist))
+ return 1;
+ if (invert_elist(s, &s->iyelist, &s->yelist))
+ return 1;
+
+ if (s->verb >= 3) {
+ DBG((dbgo,"\nxelist:\n"));
+ debug_elist(s,&s->xelist);
+ DBG((dbgo,"\nixelist:\n"));
+ debug_elist(s,&s->ixelist);
+ DBG((dbgo,"\nyelist:\n"));
+ debug_elist(s,&s->yelist);
+ DBG((dbgo,"\niyelist:\n"));
+ debug_elist(s,&s->iyelist);
+ }
+
+ /* Clean up */
+ free(xl.a);
+ free(yl.a);
+ free(tl.a);
+ return 0;
+}
+
+/********************************************************************************/
+/* Write the elists out to a file */
+
+/* Increment a string counter */
+static void
+strinc(
+char *s
+) {
+ int i,n,c; /* Length of string and carry flag */
+ n = strlen(s);
+ for (c = 1, i = n-1; i >= 0 && c != 0; i--) {
+ char sval = ' ';
+ if (s[i] == '9') {
+ s[i] = '0';
+ sval = '1';
+ c = 1;
+ } else if (s[i] == 'z') {
+ s[i] = 'a';
+ sval = 'a';
+ c = 1;
+ } else if (s[i] == 'Z') {
+ s[i] = 'A';
+ sval = 'A';
+ c = 1;
+ } else {
+ s[i]++;
+ c = 0;
+ }
+ if (i == 0 && c != 0) {
+ /* Assume there is some more space */
+ for (i = n; i >= 0; i--)
+ s[i+1] = s[i];
+ s[0] = sval;
+ break;
+ }
+ }
+}
+
+/* Write out the match reference information */
+/* Return non-zero on error */
+static int
+write_elists(
+scanrd_ *s
+) {
+ char *fname = s->refname; /* Path of file to write to */
+ FILE *elf;
+ int i;
+
+ if ((elf=fopen(fname,"w"))==NULL) {
+ s->errv = SI_REF_WRITE_ERR;
+ sprintf(s->errm,"write_elists: error opening match reference file '%s'",fname);
+ return 1;
+ }
+
+ fprintf(elf,"REF_ROTATION %f\n\n",DEG(s->irot));
+
+ fprintf(elf,"XLIST %d\n",s->xelist.c);
+ for (i = 0; i < s->xelist.c; i++)
+ fprintf(elf," %f %f %f\n",s->xelist.a[i].pos, s->xelist.a[i].len, s->xelist.a[i].ccount);
+ fprintf(elf,"\n");
+
+ fprintf(elf,"YLIST %d\n",s->yelist.c);
+ for (i = 0; i < s->yelist.c; i++)
+ fprintf(elf," %f %f %f\n",s->yelist.a[i].pos, s->yelist.a[i].len, s->yelist.a[i].ccount);
+ fprintf(elf,"\n");
+
+ if ((fclose(elf)) == EOF) {
+ s->errv = SI_REF_WRITE_ERR;
+ error("write_elists: Unable to close match reference file '%s'\n",fname);
+ return 1;
+ }
+ return 0;
+}
+
+/* Read in an elist reference file */
+/* return non-zero on error */
+/* (~~~ the line counting is rather broken ~~~) */
+static int
+read_relists(
+scanrd_ *s
+) {
+ char *fname = s->refname; /* Path of file to read from */
+ FILE *elf;
+ int i,l = 1;
+ int rv;
+ char *em; /* Read error message */
+
+ if ((elf=fopen(fname,"r"))==NULL) {
+ s->errv = SI_REF_READ_ERR;
+ sprintf(s->errm,"read_elists: error opening match reference file '%s'",fname);
+ return 1;
+ }
+
+ s->fid[0] = s->fid[1] = 0.0;
+ s->fid[2] = s->fid[3] = 0.0;
+ s->fid[4] = s->fid[5] = 0.0;
+ s->fid[6] = s->fid[7] = 0.0;
+
+ /* BOXES */
+ for(;;) {
+ if((rv = fscanf(elf,"BOXES %d",&s->nsbox)) == 1) {
+ l++;
+ break;
+ }
+ if (rv == EOF) {
+ em = "Didn't find BOXES before end of file";
+ goto read_error;
+ }
+ if (rv == 0) {
+ while ((rv = getc(elf)) != '\n' && rv != EOF);
+ l++;
+ }
+ }
+
+ /* Allocate structures for boxes */
+ if ((s->sboxes = (sbox *) calloc(s->nsbox, sizeof(sbox))) == NULL) {
+ s->errv = SI_MALLOC_REFREAD;
+ sprintf(s->errm,"read_elist, malloc failed");
+ return 1;
+ }
+ for (i = 0; i < s->nsbox;) {
+ char xfix1[20], xfix2[20], yfix1[20],yfix2[20];
+ char xfirst[20];
+ double ox,oy,w,h,xi,yi;
+ char xf[20];
+ double x;
+
+ if(fscanf(elf," %19s %19s %19s %19s %19s %lf %lf %lf %lf %lf %lf",xfirst ,xfix1, xfix2, yfix1, yfix2, &w, &h, &ox, &oy, &xi, &yi) != 11) {
+ em = "Read of BOX failed";
+ goto read_error;
+ }
+ l++;
+
+ /* If Fiducial. Typically top left, top right, botton right, bottom left. */
+ if (xfirst[0] == 'F') {
+ s->fid[0] = atof(yfix1);
+ s->fid[1] = atof(yfix2);
+ s->fid[2] = w;
+ s->fid[3] = h;
+ s->fid[4] = ox;
+ s->fid[5] = oy;
+ s->fid[6] = xi;
+ s->fid[7] = yi;
+ s->fidsize = fabs(s->fid[2] - s->fid[0]) + fabs(s->fid[5] - s->fid[3]);
+ s->fidsize /= 80.0;
+ s->havefids = 1;
+
+//printf("~1 fiducials %f %f, %f %f %f, %f\n",w, h, ox,oy, xi, yi);
+ continue;
+ }
+ for(;;) { /* Do Y increment */
+ x = ox;
+ strcpy(xf,xfix1);
+ for(;;) { /* Do X increment */
+ if (i >= s->nsbox) {
+ em = "More BOXes that declared";
+ goto read_error;
+ }
+ /* '_' is used as a null string marker for single character single cells */
+ if (xf[0] == '_')
+ sprintf(s->sboxes[i].name,"%s",yfix1);
+ else if (yfix1[0] == '_')
+ sprintf(s->sboxes[i].name,"%s",xf);
+ else { /* Y indicates Y name comes first */
+ if (xfirst[0] == 'Y')
+ sprintf(s->sboxes[i].name,"%s%s",yfix1,xf);
+ else /* X or D */
+ sprintf(s->sboxes[i].name,"%s%s",xf,yfix1);
+ }
+ if (xfirst[0] == 'D')
+ s->sboxes[i].diag = 1; /* Diagnostic box - don't print name or read pixels */
+ else
+ s->sboxes[i].diag = 0;
+ s->sboxes[i].x1 = x;
+ s->sboxes[i].y1 = oy;
+ s->sboxes[i].x2 = x + w;
+ s->sboxes[i].y2 = oy + h;
+
+ /* Misc. init. of new sbox */
+ s->sboxes[i].xpt[0] = -1.0; /* No default expected value */
+
+ i++;
+ x += xi;
+ if (strcmp(xf,xfix2) == 0)
+ break;
+ strinc(xf);
+ }
+ if (strcmp(yfix1,yfix2) == 0)
+ break;
+ oy += yi;
+ strinc(yfix1);
+ }
+ }
+
+ /* BOX_SHRINK */
+ for(;;) {
+ if((rv = fscanf(elf,"BOX_SHRINK %lf ",&s->rbox_shrink)) == 1) {
+ l++;
+ break;
+ }
+ if (rv == EOF) {
+ em = "Didn't find BOX_SHRINK before end of file";
+ goto read_error;
+ }
+ if (rv == 0) {
+ while ((rv = getc(elf)) != '\n' && rv != EOF);
+ l++;
+ }
+ }
+
+ /* XLIST */
+ for(;;) {
+ if((rv = fscanf(elf,"XLIST %d ",&s->rxelist.c)) == 1) {
+ l++;
+ break;
+ }
+ if (rv == EOF) {
+ em = "Didn't find XLIST before end of file";
+ goto read_error;
+ }
+ if (rv == 0) {
+ while ((rv = getc(elf)) != '\n' && rv != EOF);
+ l++;
+ }
+ }
+ /* Allocate structures for ref edge lists */
+ if ((s->rxelist.a = (epoint *) malloc(sizeof(epoint) * s->rxelist.c)) == NULL) {
+ s->errv = SI_MALLOC_REFREAD;
+ sprintf(s->errm,"read_elist, malloc failed");
+ return 1;
+ }
+ for (i = 0; i < s->rxelist.c; i++) {
+ if (fscanf(elf," %lf %lf %lf ",
+ &s->rxelist.a[i].pos, &s->rxelist.a[i].len, &s->rxelist.a[i].ccount) != 3) {
+ em = "Failed to read an XLIST line";
+ goto read_error;
+ }
+ l++;
+ }
+
+ /* YLIST */
+ for(;;) {
+ if ((rv = fscanf(elf,"YLIST %d ",&s->ryelist.c)) == 1) {
+ l++;
+ break;
+ }
+ if (rv == EOF) {
+ em = "Didn't find YLIST before end of file";
+ goto read_error;
+ }
+ if (rv == 0) {
+ while ((rv = getc(elf)) != '\n' && rv != EOF);
+ l++;
+ }
+ }
+ if ((s->ryelist.a = (epoint *) malloc(sizeof(epoint) * s->ryelist.c)) == NULL) {
+ s->errv = SI_MALLOC_REFREAD;
+ sprintf(s->errm,"read_elist, malloc failed");
+ return 1;
+ }
+ for (i = 0; i < s->ryelist.c; i++) {
+ if (fscanf(elf," %lf %lf %lf ",
+ &s->ryelist.a[i].pos, &s->ryelist.a[i].len, &s->ryelist.a[i].ccount) != 3)
+ {
+ em = "Failed to read an YLIST line";
+ goto read_error;
+ }
+ l++;
+ }
+
+ /* EXPECTED */
+ {
+ int j;
+ int isxyz = 0;
+ int nxpt = 0;
+ char csps[20];
+
+ for(;;) {
+ if ((rv = fscanf(elf,"EXPECTED %19s %d ",csps, &nxpt)) == 2) {
+ l++;
+ if (strcmp(csps, "XYZ") == 0) {
+ isxyz = 1;
+ break;
+ } else if (strcmp(csps, "LAB") == 0) {
+ isxyz = 0;
+ break;
+ } else {
+ em = "Unknown EXPECTED colorespace";
+ goto read_error;
+ }
+ }
+ if (rv == EOF) {
+ break;
+ }
+ if (rv == 0) {
+ while ((rv = getc(elf)) != '\n' && rv != EOF);
+ l++;
+ }
+ }
+ for (j = 0; j < nxpt; j++) {
+ char name[20];
+ double val[3];
+ if (fscanf(elf," %19s %lf %lf %lf ",
+ name, &val[0], &val[1], &val[2]) != 4)
+ {
+ em = "Failed to read an EXPECTED line";
+ goto read_error;
+ }
+ l++;
+ /* Now locate the matching box */
+ for (i = 0; i < s->nsbox; i++) {
+ if (strcmp(s->sboxes[i].name, name) == 0) { /* Found it */
+ if (isxyz) {
+ XYZ2Lab(s->sboxes[i].xpt, val);
+ } else {
+ s->sboxes[i].xpt[0] = val[0];
+ s->sboxes[i].xpt[1] = val[1];
+ s->sboxes[i].xpt[2] = val[2];
+ }
+ s->xpt = 1;
+ break;
+ }
+ }
+ if (i >= s->nsbox) {
+ em = "Failed to locate matching sample box in EXPECTED list";
+ goto read_error;
+ }
+ }
+ }
+
+ if ((fclose(elf)) == EOF) {
+ s->errv = SI_REF_WRITE_ERR;
+ error("read_elists: Unable to close match reference file '%s'\n",fname);
+ return 1;
+ }
+
+ /* Generate length normalization factor */
+ {
+ double tlen; /* Total of normalized length */
+ for (tlen = 0.0, i=0; i < s->rxelist.c; i++)
+ tlen += s->rxelist.a[i].len;
+ s->rxelist.lennorm = tlen;
+ for (tlen = 0.0, i=0; i < s->ryelist.c; i++)
+ tlen += s->ryelist.a[i].len;
+ s->ryelist.lennorm = tlen;
+ }
+
+ if (s->verb >= 3) {
+ DBG((dbgo,"\nrxelist:\n"));
+ debug_elist(s, &s->rxelist);
+ DBG((dbgo,"\nryelist:\n"));
+ debug_elist(s, &s->ryelist);
+ }
+
+ return 0;
+
+read_error:;
+ s->errv = SI_REF_FORMAT_ERR;
+ sprintf(s->errm,"read_relist failed at line %d in file %s: %s\n",l,fname,em);
+ return 1;
+}
+
+/********************************************************************************/
+/* Create an inverted direction elist */
+/* return non-zero on error */
+static int
+invert_elist(
+scanrd_ *s,
+elist *dl, /* Destination list */
+elist *sl /* Source list */
+) {
+ int i,j, rc = sl->c;
+
+ *dl = *sl; /* Copy all the structure elements */
+
+ /* Allocate space in the destination list */
+ if ((dl->a = (epoint *) malloc(sizeof(epoint) * rc)) == NULL) {
+ s->errv = SI_MALLOC_ELIST;
+ sprintf(s->errm,"invert_elist: malloc failed");
+ return 1;
+ }
+
+ /* Copy the array data and reverse its order */
+ for (i = 0, j = rc-1; i < rc; i++,j--) {
+ dl->a[j] = sl->a[i]; /* Copy array element */
+ dl->a[j].pos = -dl->a[j].pos; /* Invert position */
+ }
+ return 0;
+}
+
+/* Print out elist */
+static void
+debug_elist(
+scanrd_ *s,
+elist *el
+) {
+ int i, rc = el->c;
+
+ DBG((dbgo,"Elist has %d entries allocated at 0x%p\n",el->c,el->a));
+ DBG((dbgo,"lennorm = %f\n",el->lennorm));
+ for (i = 0; i < rc; i++)
+ DBG((dbgo," [%d] = %f %f %f\n",i,el->a[i].pos,el->a[i].len,el->a[i].ccount));
+}
+
+/* Free the array data in an elist */
+static void
+free_elist_array(elist *el) {
+ free(el->a);
+ el->c = 0;
+}
+
+/********************************************************************************/
+/* !!!!!!! */
+/* NEED TO RESOLVE WHY current code is better in some cases, but */
+/* not in others. */
+
+#ifndef NEVER /* Current code */
+
+/* Compute a correlation between two elists */
+static double
+elist_correl(
+scanrd_ *s,
+elist *r, /* Reference list */
+elist *t, /* Target list */
+double off, double scale, /* Offset and scale of target to ref */
+int verb /* Verbose mode */
+) {
+ int i, j, rc = r->c;
+ double cc = 0.0; /* Correlation */
+ double marg = (r->a[rc-1].pos - r->a[0].pos)/150.0; /* determines sharpness of pos. match */
+ double marg2 = marg * 3.0; /* Don't contribute anything outside this distance */
+
+ for (i = j = 0; i < t->c; i++) {
+ int ri; /* Reference index */
+ double dd,d1,d2; /* Distance to nearest reference */
+ double pos = (t->a[i].pos + off) * scale;
+ double len = t->a[i].len;
+ double cnt = t->a[i].ccount;
+ while (pos > r->a[j+1].pos && j < (r->c-2)) j++;
+ d1 = fabs(pos - r->a[j].pos);
+ d2 = fabs(r->a[j+1].pos - pos);
+ if (d1 < d2) {
+ dd = d1;
+ ri = j;
+ } else {
+ dd = d2;
+ ri = j+1;
+ }
+ if (dd <= marg2) { /* If close enough to reference */
+ double ccf, rcnt = r->a[ri].ccount;
+ double llf, rlen = r->a[ri].len;
+ double df = marg/(marg + dd);
+ df *= df;
+ ccf = 1.0 - (rcnt > cnt ? rcnt-cnt : cnt-rcnt);
+ llf = 1.0 - (rlen > len ? rlen-len : len-rlen);
+ /* The weighting gives slightly more emphasis on matching long lines */
+ cc += (1.0 + rlen) * (df * llf * ccf);
+ if (verb) {
+ DBG((dbgo,"---- t[%d] %f %f %f this cc = %f, running total cc = %f\n r[%d] %f %f %f, df = %f, llf = %f, ccf = %f\n",
+ i,pos,len,cnt,df * llf * ccf,cc,j,r->a[ri].pos,r->a[ri].len,rcnt,df, llf, ccf));
+ }
+ }
+ }
+ return cc/(r->lennorm + (double)r->c); /* Normalize */
+}
+
+#else /* New test code */
+
+/* Compute a correlation between two elists */
+static double
+elist_correl(
+scanrd_ *s,
+elist *r, /* Reference list */
+elist *t, /* Target list */
+double off, double scale, /* Offset and scale of target to ref */
+int verb /* Verbose mode */
+) {
+ int i, rc = r->c;
+ double cc = 0.0; /* Correlation */
+ double marg = (r->a[rc-1].pos - r->a[0].pos)/100.0; /* determines sharpness of pos. match */
+ double marg2 = marg * marg; /* marg squared */
+
+//printf("~1 doing elist_correl\n");
+ /* For each reference edge */
+ for (i = 0; i < rc; i++) {
+ int j[3], jj, bj, tc = t->c;
+ double dd, pos, bdd;
+
+ /* Find the closest target edge using binary search. */
+ for(bdd = 1e6, j[2] = tc-1, j[0] = 0; j[2] > (j[0]+1);) {
+ double dist;
+ j[1] = (j[2] + j[0])/2; /* Trial point */
+ dist = r->a[i].pos - (t->a[j[1]].pos + off) * scale;
+
+//printf("~1 j1 = %d, j1 = %d, j0 = %d, dist = %f\n",j[2], j[1], j[0], dist);
+ if (dist > 0) {
+ j[0] = j[1];
+ } else {
+ j[2] = j[1];
+ }
+ }
+
+ /* Locate best out of 3 remaining points */
+ for (jj = 0; jj < 3; jj++) {
+ double dist;
+ pos = (t->a[j[jj]].pos + off) * scale;
+ dist = r->a[i].pos - pos;
+ dd = dist * dist; /* Distance squared */
+ if (dd < bdd) { /* New closest */
+ bdd = dd;
+ bj = j[jj];
+ }
+ }
+
+//printf("~1 best j = %d, bdd = %f, marg2 = %f\n",bj,bdd,marg2);
+ /* Compute correlation */
+ if (bdd < marg2) { /* Within our margine */
+ double df = (marg2 - bdd)/marg2; /* Distance factor */
+ double llf, rlen = r->a[i].len, len = t->a[i].len;
+ double ccf, rcnt = r->a[i].ccount, cnt = t->a[i].ccount;
+ double tcc;
+ llf = 1.0 - (rlen > len ? rlen-len : len-rlen);
+ ccf = 1.0 - (rcnt > cnt ? rcnt-cnt : cnt-rcnt);
+
+ /* The weighting gives slightly more emphasis on matching long lines */
+ /* Not using crossing count */
+ tcc = (1.0 + rlen) * (df * llf);
+ cc += tcc;
+ if (verb) {
+ DBG((dbgo,"---- targ[%d] %f %f %f this cc = %f, running total cc = %f\n",
+ bj,pos,t->a[bj].len,t->a[bj].ccount, tcc,cc));
+ DBG((dbgo," ref[%d] %f %f %f, df = %f, llf = %f, ccf = %f\n",
+ i,r->a[i].pos,r->a[i].len,r->a[i].ccount, df, llf, ccf));
+ }
+ }
+
+ }
+ return cc/(r->lennorm + (double)r->c); /* Normalize */
+}
+
+#endif /* NEVER */
+
+/* Structure to hold data for optimization function */
+struct _edatas {
+ scanrd_ *s; /* scanrd object */
+ elist *r; /* Reference list */
+ elist *t; /* Target list */
+ int verb; /* Verbose mode */
+ }; typedef struct _edatas edatas;
+
+/* Definition of the optimization function handed to powell() */
+static double
+efunc(void *edata, double p[]) {
+ edatas *e = (edatas *)edata;
+ double rv = 2.0 - elist_correl(e->s,e->r,e->t,p[0],p[1],e->verb);
+ return rv;
+}
+
+/* return non-zero on error */
+static int
+best_match(
+scanrd_ *s,
+elist *r, /* Reference list */
+elist *t, /* Target list */
+ematch *rv /* Return values */
+) {
+ int r0,r1,rw,t0,t1;
+ double rwidth;
+ double cc;
+ double bcc = 0.0, boff = 0.0, bscale = 0.0; /* best values */
+
+ /* The target has been rotated, and we go through all reasonable */
+ /* translations and scales to see if we can match it to the */
+ /* reference. */
+ r0 = 0;
+ r1 = r->c-1;
+ rw = r->c/2; /* Minimum number of target line to match all of reference */
+ if (t->c/2 < rw)
+ rw = t->c/2;
+ rwidth = r->a[r1].pos - r->a[r0].pos;
+
+ for (t0 = 0; t0 < t->c-1; t0++) {
+ double off;
+ for (t1 = t->c-1; t1 > (t0+rw); t1--) {
+ double scale;
+
+ scale = rwidth/(t->a[t1].pos - t->a[t0].pos);
+ if (scale < 0.001 || scale > 100.0) {
+ break; /* Don't bother with silly scale factors */
+ }
+
+ /* Have to compenate the offset for the scale since it is scaled from 0 */
+ off = r->a[r0].pos/scale - t->a[t0].pos;
+ cc = elist_correl(s,r,t,off,scale,0);
+
+ if (s->verb >= 7) {
+ DBG((dbgo,"Matching target [%d]-[%d] to ref [%d]-[%d] = %f-%f to %f-%f\n",
+ t0,t1,r0,r1,t->a[t0].pos,t->a[t1].pos,r->a[r0].pos,r->a[r1].pos));
+ DBG((dbgo,"Initial off %f, scale %f, cc = %f\n",off,scale,cc));
+ }
+ if (cc > 0.20) { /* Looks promising, try optimizing solution */
+ double cp[2]; /* Start point/improved point */
+ double rv; /* Return value */
+ int rc; /* Return code */
+ edatas dd; /* Data structure */
+ double ss[2] = { 0.1, 0.1}; /* Initial search distance */
+
+ dd.s = s; /* scanrd object */
+ dd.r = r; /* Reference list */
+ dd.t = t; /* Target list */
+ dd.verb = 0; /* Verbose mode */
+
+ /* Set search start point */
+ cp[0] = off;
+ cp[1] = scale;
+ /* Set search distance */
+ ss[0] = (0.01 * rwidth/ELISTCDIST)/scale; /* Search distance */
+ ss[1] = scale * 0.01 * rwidth/ELISTCDIST;
+
+ /* Find minimum */
+ rc = powell(&rv, 2,cp,ss,0.0001,400,efunc,&dd, NULL, NULL);
+
+ if (rc == 0 /* Powell converged */
+ && cp[1] > 0.001 && cp[1] < 100.0) { /* and not ridiculous */
+ cc = 2.0 - rv;
+ off = cp[0];
+ scale = cp[1];
+ }
+ /* Else use unoptimsed values */
+
+ if (s->verb >= 7) {
+ DBG((dbgo,"After optimizing, off %f, scale %f, cc = %f\n",off,scale,cc));
+ }
+ }
+
+ if (s->verb >= 7) {
+ if (cc > 0.25) {
+ DBG((dbgo,"Good correlation::\n"));
+ elist_correl(s,r,t,off,scale,1);
+ }
+ }
+ if (s->verb >= 7)
+ DBG((dbgo,"offset %f, scale %f cc %f\n", off,scale,cc));
+ if (cc > 0.0 && cc > bcc) { /* Keep best */
+ boff = off;
+ bscale = scale;
+ bcc = cc;
+ if (s->verb >= 7)
+ DBG((dbgo,"(New best)\n"));
+ }
+ }
+ }
+ if (s->verb >= 7)
+ DBG((dbgo,"Returning best offset %f, scale %f returns %f\n\n", boff,bscale,bcc));
+
+ /* return best values */
+ rv->cc = bcc;
+ rv->off = boff;
+ rv->scale = bscale;
+ return 0;
+}
+
+/* Find best offset and scale match between reference and target, */
+/* and then from this, compute condidate 90 degree rotations. */
+/* Return 0 if got at least one candidate rotation */
+/* Return 1 if no reasonable candidate rotation found */
+/* Return 2 if some other error */
+static int
+do_match(
+scanrd_ *s
+) {
+ ematch xx, yy, xy, yx, xix, yiy, xiy, yix; /* All 8 matches needed to detect rotations */
+ double r0, r90, r180, r270; /* Correlation for each extra rotation of target */
+
+ /* Check out all the matches */
+ if (s->verb >= 2) DBG((dbgo,"Checking xx\n"));
+ if (best_match(s, &s->rxelist,&s->xelist,&xx))
+ return 2;
+ if (s->verb >= 2) DBG((dbgo,"Checking yy\n"));
+ if (best_match(s, &s->ryelist,&s->yelist,&yy))
+ return 2;
+ if (s->verb >= 2) DBG((dbgo,"Checking xy\n"));
+ if (best_match(s, &s->rxelist,&s->yelist,&xy))
+ return 2;
+ if (s->verb >= 2) DBG((dbgo,"Checking yx\n"));
+ if (best_match(s, &s->ryelist,&s->xelist,&yx))
+ return 2;
+ if (s->verb >= 2) DBG((dbgo,"Checking xix\n"));
+ if (best_match(s, &s->rxelist,&s->ixelist,&xix))
+ return 2;
+ if (s->verb >= 2) DBG((dbgo,"Checking yiy\n"));
+ if (best_match(s, &s->ryelist,&s->iyelist,&yiy))
+ return 2;
+ if (s->verb >= 2) DBG((dbgo,"Checking xiy\n"));
+ if (best_match(s, &s->rxelist,&s->iyelist,&xiy))
+ return 2;
+ if (s->verb >= 2) DBG((dbgo,"Checking yix\n"));
+ if (best_match(s, &s->ryelist,&s->ixelist,&yix))
+ return 2;
+
+ if (s->verb >= 2) {
+ DBG((dbgo,"Axis matches for each possible orientation:\n"));
+ DBG((dbgo," 0: xx = %f, yy = %f, xx.sc = %f, yy.sc = %f\n",
+ xx.cc,yy.cc,xx.scale,yy.scale));
+ DBG((dbgo," 90: xiy = %f, yx = %f, xiy.sc = %f, yx.sc = %f\n",
+ xiy.cc,yx.cc,xiy.scale,yx.scale));
+ DBG((dbgo,"180: xix = %f, yiy = %f, xix.sc = %f, yiy.sc = %f\n",
+ xix.cc,yiy.cc,xix.scale,yiy.scale));
+ DBG((dbgo,"270: xy = %f, yix = %f, xy.sc = %f, yix.sc = %f\n",
+ xy.cc,yix.cc,xy.scale,yix.scale));
+ }
+
+ /* Compute the combined values for the four orientations. */
+ /* add penalty for different scale factors */
+ r0 = sqrt(xx.cc * xx.cc + yy.cc * yy.cc)
+ * (xx.scale > yy.scale ? yy.scale/xx.scale : xx.scale/yy.scale);
+ r90 = sqrt(xiy.cc * xiy.cc + yx.cc * yx.cc)
+ * (xiy.scale > yx.scale ? yx.scale/xiy.scale : xiy.scale/yx.scale);
+ r180 = sqrt(xix.cc * xix.cc + yiy.cc * yiy.cc)
+ * (xix.scale > yiy.scale ? yiy.scale/xix.scale : xix.scale/yiy.scale);
+ r270 = sqrt(xy.cc * xy.cc + yix.cc * yix.cc)
+ * (xy.scale > yix.scale ? yix.scale/xy.scale : xy.scale/yix.scale);
+
+ if (s->verb >= 2)
+ DBG((dbgo,"r0 = %f, r90 = %f, r180 = %f, r270 = %f\n",r0,r90,r180,r270));
+
+ s->norots = 0;
+ if (s->flags & SI_GENERAL_ROT) { /* If general rotation allowed */
+ if (s->xpt == 0) { /* No expected color information to check rotations agaist */
+ /* so choose the single best rotation by the edge matching */
+ DBG((dbgo,"There is no expected color information, so best fit rotations will be used\n"));
+ if (r0 >= MATCHCC && r0 >= r90 && r0 >= r180 && r0 >= r270) {
+ s->rots[0].ixoff = -xx.off;
+ s->rots[0].ixscale = 1.0/xx.scale;
+ s->rots[0].iyoff = -yy.off;
+ s->rots[0].iyscale = 1.0/yy.scale;
+ s->rots[0].irot = s->irot;
+ s->rots[0].cc = r0;
+ s->norots = 1;
+ } else if (r90 >= MATCHCC && r90 >= r180 && r90 >= r270) {
+ s->rots[0].ixoff = -xiy.off;
+ s->rots[0].ixscale = 1.0/xiy.scale;
+ s->rots[0].iyoff = -yx.off;
+ s->rots[0].iyscale = 1.0/yx.scale;
+ s->rots[0].irot = s->irot + M_PI_2;
+ s->rots[0].cc = r90;
+ s->norots = 1;
+ } else if (r180 >= MATCHCC && r180 >= r270) {
+ s->rots[0].ixoff = -xix.off;
+ s->rots[0].ixscale = 1.0/xix.scale;
+ s->rots[0].iyoff = -yiy.off;
+ s->rots[0].iyscale = 1.0/yiy.scale;
+ s->rots[0].irot = s->irot + M_PI;
+ s->rots[0].cc = r180;
+ s->norots = 1;
+ } else if (r270 >= MATCHCC) { /* 270 extra target rotation */
+ s->rots[0].ixoff = -xy.off;
+ s->rots[0].ixscale = 1.0/xy.scale;
+ s->rots[0].iyoff = -yix.off;
+ s->rots[0].iyscale = 1.0/yix.scale;
+ s->rots[0].irot = s->irot + M_PI + M_PI_2;
+ s->rots[0].cc = r270;
+ s->norots = 1;
+ }
+
+ } else { /* Got expected color info, so try reasonable rotations */
+ double bcc; /* Best correlation coeff */
+
+ if (r0 >= r90 && r0 >= r180 && r0 >= r270)
+ bcc = r0;
+ else if (r90 >= r180 && r90 >= r270)
+ bcc = r90;
+ else if (r180 >= r270)
+ bcc = r180;
+ else
+ bcc = r270;
+
+ bcc *= ALT_ROT_TH; /* Threshold for allowing alternate rotation */
+ if (bcc < MATCHCC)
+ bcc = MATCHCC;
+
+ s->norots = 0;
+ if (r0 >= bcc) {
+ s->rots[s->norots].ixoff = -xx.off;
+ s->rots[s->norots].ixscale = 1.0/xx.scale;
+ s->rots[s->norots].iyoff = -yy.off;
+ s->rots[s->norots].iyscale = 1.0/yy.scale;
+ s->rots[s->norots].irot = s->irot;
+ s->rots[s->norots].cc = r0;
+ s->norots++;
+ }
+ if (r90 >= bcc) {
+ s->rots[s->norots].ixoff = -xiy.off;
+ s->rots[s->norots].ixscale = 1.0/xiy.scale;
+ s->rots[s->norots].iyoff = -yx.off;
+ s->rots[s->norots].iyscale = 1.0/yx.scale;
+ s->rots[s->norots].irot = s->irot + M_PI_2;
+ s->rots[s->norots].cc = r90;
+ s->norots++;
+ }
+ if (r180 >= bcc) {
+ s->rots[s->norots].ixoff = -xix.off;
+ s->rots[s->norots].ixscale = 1.0/xix.scale;
+ s->rots[s->norots].iyoff = -yiy.off;
+ s->rots[s->norots].iyscale = 1.0/yiy.scale;
+ s->rots[s->norots].irot = s->irot + M_PI;
+ s->rots[s->norots].cc = r180;
+ s->norots++;
+ }
+ if (r270 >= bcc) {
+ s->rots[s->norots].ixoff = -xy.off;
+ s->rots[s->norots].ixscale = 1.0/xy.scale;
+ s->rots[s->norots].iyoff = -yix.off;
+ s->rots[s->norots].iyscale = 1.0/yix.scale;
+ s->rots[s->norots].irot = s->irot + M_PI + M_PI_2;
+ s->rots[s->norots].cc = r270;
+ s->norots++;
+ }
+ }
+ } else { /* Use only rotation 0 */
+ if (r0 >= MATCHCC) {
+ s->rots[0].ixoff = -xx.off;
+ s->rots[0].ixscale = 1.0/xx.scale;
+ s->rots[0].iyoff = -yy.off;
+ s->rots[0].iyscale = 1.0/yy.scale;
+ s->rots[0].irot = s->irot;
+ s->rots[0].cc = r0;
+ s->norots = 1;
+ } else if (s->flags & SI_ASISIFFAIL) {
+ DBG((dbgo, "Recognition failed, reading patches 'as is' (probably incorrect)\n"));
+ s->rots[0].ixoff = 0.0;
+ s->rots[0].ixscale = 1.0;
+ s->rots[0].iyoff = 0.0;
+ s->rots[0].iyscale = 1.0;
+ s->rots[0].irot = 0.0;
+ s->rots[0].cc = r0;
+ s->norots = 1;
+ }
+ }
+
+ if (s->verb >= 2) {
+ int i;
+ DBG((dbgo,"There are %d candidate rotations:\n",s->norots));
+
+ for (i = 0; i < s->norots; i++) {
+ DBG((dbgo,"cc = %f, irot = %f, xoff = %f, yoff = %f, xscale = %f, yscale = %f\n",
+ s->rots[i].cc, DEG(s->rots[i].irot), s->rots[i].ixoff,s->rots[i].iyoff,s->rots[i].ixscale,s->rots[i].iyscale));
+ }
+ }
+
+ if (s->norots == 0)
+ return 1;
+
+ return 0;
+}
+
+/********************************************************************************/
+/* perspective transformation. */
+/* Transform from raster to reference using iptrans[]. */
+/* Transform from reference to raster using ptrans[]. */
+static void ptrans(double *xx, double *yy, double x, double y, double *ptrans) {
+ double den;
+
+ den = ptrans[6] * x + ptrans[7] * y + 1.0;
+
+ if (fabs(den) < 1e-6) {
+ if (den < 0.0)
+ den = -1e-6;
+ else
+ den = 1e-6;
+ }
+
+ *xx = (ptrans[0] * x + ptrans[1] * y + ptrans[2])/den;
+ *yy = (ptrans[3] * x + ptrans[4] * y + ptrans[5])/den;
+}
+
+/* Convert perspective transfom parameters to inverse */
+/* perspective transform parameters. */
+/* Return nz on error */
+int invert_ptrans(double *iptrans, double *ptrans) {
+ double scale = ptrans[0] * ptrans[4] - ptrans[1] * ptrans[3];
+
+ if (fabs(scale) < 1e-6)
+ return 1;
+
+ scale = 1.0/scale;
+
+ iptrans[0] = scale * (ptrans[4] - ptrans[5] * ptrans[7]);
+ iptrans[1] = scale * (ptrans[2] * ptrans[7] - ptrans[1]);
+ iptrans[2] = scale * (ptrans[1] * ptrans[5] - ptrans[2] * ptrans[4]);
+
+ iptrans[3] = scale * (ptrans[5] * ptrans[6] - ptrans[3]);
+ iptrans[4] = scale * (ptrans[0] - ptrans[2] * ptrans[6]);
+ iptrans[5] = scale * (ptrans[2] * ptrans[3] - ptrans[0] * ptrans[5]);
+
+ iptrans[6] = scale * (ptrans[3] * ptrans[7] - ptrans[4] * ptrans[6]);
+ iptrans[7] = scale * (ptrans[1] * ptrans[6] - ptrans[0] * ptrans[7]);
+
+ return 0;
+}
+
+
+/* Structure to hold data for optimization function */
+struct _pdatas {
+ scanrd_ *s; /* scanrd object */
+ double *tar; /* 4 x x,y raster points */
+ double *ref; /* 4 x x,y reference points */
+}; typedef struct _pdatas pdatas;
+
+/* Definition of the optimization function handed to powell() */
+/* We simply want to match the 4 points from the reference */
+/* back to the target raster. */
+static double
+ptransfunc(void *pdata, double p[]) {
+ pdatas *e = (pdatas *)pdata;
+ int i;
+ double rv = 0.0;
+
+ for (i = 0; i < 8; i += 2) {
+ double x, y;
+
+ ptrans(&x, &y, e->ref[i+0], e->ref[i+1], p);
+
+ rv += (e->tar[i+0] - x) * (e->tar[i+0] - x);
+ rv += (e->tar[i+1] - y) * (e->tar[i+1] - y);
+ }
+
+ return rv;
+}
+
+/* Compute a combined perspective transform */
+/* given two sets of four reference points. */
+/* Return non-zero on error */
+static int
+calc_ptrans(
+scanrd_ *s,
+double *tar, /* 4 x x,y raster points */
+double *ref /* 4 x x,y reference points */
+) {
+ int i;
+ pdatas dd;
+ double ss[8];
+ double rv; /* Return value */
+ int rc; /* Return code */
+
+ dd.s = s;
+ dd.tar = tar;
+ dd.ref = ref;
+
+ s->ptrans[0] = 1.0;
+ s->ptrans[1] = 0.0;
+ s->ptrans[2] = 0.0;
+ s->ptrans[3] = 0.0;
+ s->ptrans[4] = 1.0;
+ s->ptrans[5] = 0.0;
+ s->ptrans[6] = 0.0;
+ s->ptrans[7] = 0.0;
+
+ for (i = 0; i < 8; i++)
+ ss[i] = 0.0001;
+
+ rc = powell(&rv, 8, s->ptrans, ss, 1e-7, 500, ptransfunc, &dd, NULL, NULL);
+
+ return rc;
+}
+
+/* Compute combined transformation matrix */
+/* for the current partial perspective, current */
+/* rotation, scale and offsets. */
+/* Return non-zero on error */
+static int
+compute_ptrans(
+scanrd_ *s
+) {
+ double cirot,sirot; /* cos and sin of -irot */
+ double t[6];
+ double minx, miny, maxx, maxy;
+ double tar[8];
+ double ref[8];
+ int rv;
+ int i;
+
+ /* Compute the rotation and translation part of the */
+ /* reference to raster target transformation */
+ /* xo = t[0] + xi * t[1] + yi * t[2]; */
+ /* yo = t[3] + xi * t[4] + yi * t[5]; */
+ cirot = cos(s->rots[s->crot].irot);
+ sirot = sin(s->rots[s->crot].irot);
+ t[0] = cirot * s->rots[s->crot].ixoff + sirot * s->rots[s->crot].iyoff;
+ t[1] = s->rots[s->crot].ixscale * cirot;
+ t[2] = s->rots[s->crot].iyscale * sirot;
+
+ t[3] = -sirot * s->rots[s->crot].ixoff + cirot * s->rots[s->crot].iyoff;
+ t[4] = s->rots[s->crot].ixscale * -sirot;
+ t[5] = s->rots[s->crot].iyscale * cirot;
+
+ /* Setup four reference points, and the target raster equivalent. */
+ /* Choose min/max of matching boxes as test points, to scale with raster size. */
+ minx = miny = 1e60;
+ maxx = maxy = -1e60;
+ for (i = 0; i < s->nsbox; i++) {
+ if (s->sboxes[i].x1 < minx)
+ minx = s->sboxes[i].x1;
+ if (s->sboxes[i].x2 > maxx)
+ maxx = s->sboxes[i].x2;
+ if (s->sboxes[i].y1 < miny)
+ miny = s->sboxes[i].y1;
+ if (s->sboxes[i].y2 > maxy)
+ maxy = s->sboxes[i].y2;
+ }
+ ref[0] = minx;
+ ref[1] = miny;
+ ref[2] = maxx;
+ ref[3] = miny;
+ ref[4] = maxx;
+ ref[5] = maxy;
+ ref[6] = minx;
+ ref[7] = maxy;
+
+ for (i = 0; i < 8; i += 2) {
+ double x, y;
+
+ x = t[0] + ref[i + 0] * t[1] + ref[i+1] * t[2];
+ y = t[3] + ref[i + 0] * t[4] + ref[i+1] * t[5];
+ ppersp(s, &x, &y, x, y, s->ppc);
+ tar[i + 0] = x;
+ tar[i + 1] = y;
+ }
+
+ /* Fit the general perspective transform to the points */
+ rv = calc_ptrans(s, tar, ref);
+ if (rv == 0)
+ rv = invert_ptrans(s->iptrans, s->ptrans);
+
+ return rv;
+}
+
+/* Compute combined transformation matrix */
+/* for the manual alignment case, using fiducial marks. */
+/* Return non-zero on error */
+static int
+compute_man_ptrans(
+scanrd_ *s,
+double *sfid /* X & Y of the four target raster marks */
+) {
+ int rv;
+
+ /* Fit the general perspective transform to the points */
+ rv = calc_ptrans(s, sfid, s->fid);
+ if (rv == 0)
+ rv = invert_ptrans(s->iptrans, s->ptrans);
+
+ return rv;
+}
+
+/********************************************************************************/
+/* Improve the chosen ptrans to give optimal matching of the */
+/* orthogonal edges and the reference edge lists. */
+
+/* Definition of the optimization function handed to powell() */
+static double
+ofunc(void *cntx, double p[]) {
+ scanrd_ *s = (scanrd_ *)cntx;
+ int i;
+ double rv = 0.0;
+
+ /* First the X list */
+ for (i = 0; i < s->rxelist.c; i++) {
+ points *tp;
+
+ if (s->rxelist.a[i].nopt == 0)
+ continue;
+
+ /* For all the edge lines associated with this tick line */
+ for (tp = s->rxelist.a[i].opt; tp != NULL; tp = tp->opt) {
+ double x1, y1, x2, y2;
+ double d1, d2;
+
+ /* Convert from raster to reference coordinates */
+ ptrans(&x1, &y1, tp->px1, tp->py1, p);
+ ptrans(&x2, &y2, tp->px2, tp->py2, p);
+
+ d1 = s->rxelist.a[i].pos - x1;
+ d2 = s->rxelist.a[i].pos - x2;
+ rv += tp->len * (d1 * d1 + d2 * d2);
+ }
+ }
+
+ /* Then the Y list */
+ for (i = 0; i < s->ryelist.c; i++) {
+ points *tp;
+
+ if (s->ryelist.a[i].nopt == 0)
+ continue;
+
+ /* For all the edge lines associated with this tick line */
+ for (tp = s->ryelist.a[i].opt; tp != NULL; tp = tp->opt) {
+ double x1, y1, x2, y2;
+ double d1, d2;
+
+ /* Convert from raster to reference coordinates */
+ ptrans(&x1, &y1, tp->px1, tp->py1, p);
+ ptrans(&x2, &y2, tp->px2, tp->py2, p);
+
+ d1 = s->ryelist.a[i].pos - y1;
+ d2 = s->ryelist.a[i].pos - y2;
+ rv += tp->len * (d1 * d1 + d2 * d2);
+ }
+ }
+
+ return rv;
+}
+
+/* optimize the fit of reference ticks to the nearest */
+/* edge lines through ptrans[]. */
+/* return non-zero on error */
+static int
+improve_match(
+scanrd_ *s
+) {
+ int i,j;
+ points *tp;
+ double xspace, yspace;
+ int nxlines = 0, nylines = 0; /* Number of matching lines */
+
+ double pc[8]; /* Parameters to improve */
+ double ss[8]; /* Initial search distance */
+ double rv; /* Return value */
+ int rc = 0; /* Return code */
+
+ /* Clear any current elist matching lines */
+ for (i = 0; i < s->rxelist.c; i++) {
+ s->rxelist.a[i].opt = NULL;
+ s->rxelist.a[i].nopt = 0;
+ }
+ for (i = 0; i < s->ryelist.c; i++) {
+ s->ryelist.a[i].opt = NULL;
+ s->ryelist.a[i].nopt = 0;
+ }
+
+ /* Figure out the average tick spacing for each reference edge list. */
+ /* (We're assuming the edge lists are sorted) */
+ xspace = (s->rxelist.a[s->rxelist.c-1].pos - s->rxelist.a[0].pos)/s->rxelist.c;
+ yspace = (s->ryelist.a[s->ryelist.c-1].pos - s->ryelist.a[0].pos)/s->ryelist.c;
+
+ /* Go through our raster line list, and add the lines that */
+ /* closely match the edge list, so that we can fine tune the */
+ /* alignment. */
+ tp = s->gdone;
+ FOR_ALL_ITEMS(points, tp)
+ if (tp->flag & F_VALID) {
+ double x1, y1, x2, y2;
+ elist *el;
+ double v1, v2;
+ double bdist;
+ int bix;
+ double space;
+ int *nlines = NULL;
+ double a;
+
+ /* Convert from raster to reference coordinates */
+ ptrans(&x1, &y1, tp->px1, tp->py1, s->iptrans);
+ ptrans(&x2, &y2, tp->px2, tp->py2, s->iptrans);
+
+ /* Compute the angle */
+ a = atan2(y2 - y1,x2 - x1);
+
+ /* Constrain the angle to be between -PI/4 and 3PI/4 */
+ if (a < -M_PI_4)
+ a += M_PI;
+ if (a > M_PI_3_4)
+ a -= M_PI;
+
+ /* Decide if it is one of the orthogonal lines */
+ if (fabs(a - M_PI_2) > (0.2 * M_PI_2) /* 0.2 == +/- 18 degrees */
+ && fabs(a - 0.0) > (0.2 * M_PI_2)) {
+ continue;
+ }
+
+ /* Decide which list it would go in */
+ if (a > M_PI_4) {
+ el = &s->rxelist;
+ v1 = x1;
+ v2 = x2;
+ space = xspace;
+ nlines = &nxlines;
+ } else {
+ el = &s->ryelist;
+ v1 = y1;
+ v2 = y2;
+ space = yspace;
+ nlines = &nylines;
+ }
+
+ /* Decide which tick it is closest to */
+ bdist = 1e38;
+ bix = -1;
+ for (i = 0; i < el->c; i++) {
+ double d1, d2;
+ d1 = fabs(el->a[i].pos - v1);
+ d2 = fabs(el->a[i].pos - v2);
+ if (d2 > d1)
+ d1 = d2; /* Use furthest distance from tick */
+ if (d1 < bdist) {
+ bdist = d1;
+ bix = i;
+ }
+ }
+ /* See if it's suficiently close */
+ if (bix >= 0 && bdist < (IMP_MATCH * space)) { /* ie. 0.1 */
+ tp->flag |= F_IMPROVE;
+ if (el->a[bix].opt == NULL) {
+ (*nlines)++;
+ }
+ /* Add it to the linked list of matching lines */
+ tp->opt = el->a[bix].opt;
+ el->a[bix].opt = tp;
+ el->a[bix].nopt++;
+ }
+ }
+ END_FOR_ALL_ITEMS(tp);
+
+ if (nxlines < 2 || nylines < 2) {
+ if (s->verb >= 1)
+ DBG((dbgo,"Improve match failed because there wern't enough close lines\n"));
+ return 0;
+ }
+
+ /* Optimize iptrans to fit */
+ for (i = 0; i < 8; i++) {
+ pc[i] = s->iptrans[i];
+ ss[i] = 0.0001;
+ }
+
+ rc = powell(&rv, 8, pc, ss, 0.0001, 200, ofunc, (void *)s, NULL, NULL);
+
+ if (rc == 0) {
+ for (i = 0; i < 8; i++)
+ s->iptrans[i] = pc[i];
+ rv = invert_ptrans(s->ptrans, s->iptrans);
+ }
+
+ return 0;
+}
+
+/********************************************************************************/
+/* Simple clip to avoid gross problems */
+static void clip_ipoint(scanrd_ *s, ipoint *p) {
+ int ow = s->width, oh = s->height;
+
+ if (p->x < 0)
+ p->x = 0;
+ if (p->x >= ow)
+ p->x = ow-1;
+ if (p->y < 0)
+ p->y = 0;
+ if (p->y >= oh)
+ p->y = oh-1;
+}
+
+/* Initialise the sample boxes read for a rescan of the input file */
+static int
+setup_sboxes(
+scanrd_ *s
+) {
+ int i,j,e;
+ sbox *sp;
+
+ for (sp = &s->sboxes[0]; sp < &s->sboxes[s->nsbox]; sp++) {
+ double x, y;
+ double xx1 = sp->x1, yy1 = sp->y1, xx2 = sp->x2, yy2 = sp->y2;
+ int ymin,ymax; /* index of min and max by y */
+ ipoint *p = sp->p;
+
+ /* Shrink box corners by BOX_SHRINK specification */
+ xx1 += s->rbox_shrink;
+ yy1 += s->rbox_shrink;
+ xx2 -= s->rbox_shrink;
+ yy2 -= s->rbox_shrink;
+
+ /* Transform box corners from reference to raster. */
+ /* Box is defined in clockwise direction. */
+ ptrans(&x, &y, xx1, yy1, s->ptrans);
+ p[0].x = (int)(0.5 + x);
+ p[0].y = (int)(0.5 + y);
+ clip_ipoint(s, &p[0]);
+
+ ptrans(&x, &y, xx2, yy1, s->ptrans);
+ p[1].x = (int)(0.5 + x);
+ p[1].y = (int)(0.5 + y);
+ clip_ipoint(s, &p[1]);
+
+ ptrans(&x, &y, xx2, yy2, s->ptrans);
+ p[2].x = (int)(0.5 + x);
+ p[2].y = (int)(0.5 + y);
+ clip_ipoint(s, &p[2]);
+
+ ptrans(&x, &y, xx1, yy2, s->ptrans);
+ p[3].x = (int)(0.5 + x);
+ p[3].y = (int)(0.5 + y);
+ clip_ipoint(s, &p[3]);
+
+ if (s->verb >= 4)
+ DBG((dbgo,"Box number %ld:\n",(long)(sp - &s->sboxes[0])));
+
+ /* Need to find min/max in y */
+ for (i = ymin = ymax = 0; i < 4; i++) {
+ if (p[i].y < p[ymin].y)
+ ymin = i;
+ if (p[i].y > p[ymax].y)
+ ymax = i;
+ }
+ sp->ymin = p[ymin].y;
+ sp->ymax = p[ymax].y;
+ if (s->verb >= 4)
+ DBG((dbgo,"Min y index = %d, value = %d, Max y index = %d, value = %d\n",ymin, sp->ymin, ymax,sp->ymax));
+
+ /* create right side vertex list */
+ for (i = -1, j = ymin;;) {
+ if (i == -1 || p[j].y != p[sp->r.e[i]].y)
+ sp->r.e[++i] = j; /* Write next if first or different y */
+ else if (p[j].x > p[sp->r.e[i]].x)
+ sp->r.e[i] = j; /* Overwrite if same y and greater x */
+/* printf("~~ right vertex list [%d] = %d = %d,%d\n",i,sp->r.e[i],p[j].x,p[j].y); */
+ if (j == ymax) {
+ sp->r.e[++i] = -1; /* mark end */
+/* printf("~~ right vertex list [%d] = %d\n",i,sp->r.e[i]); */
+ break;
+ }
+ j = (j != 3 ? j+1 : 0);/* Advance clockwize */
+ }
+ sp->r.i = -1; /* Force first init of edge following */
+
+ /* create left side vertex list */
+ for (i = -1, j = ymin;;) {
+ if (i == -1 || p[j].y != p[sp->l.e[i]].y)
+ sp->l.e[++i] = j; /* Write next if first or different y */
+ else if (p[j].x < p[sp->l.e[i]].x)
+ sp->l.e[i] = j; /* Overwrite if same y and lesser x */
+/* printf("~~ left vertex list [%d] = %d = %d,%d\n",i,sp->l.e[i],p[j].x,p[j].y); */
+ if (j == ymax) {
+ sp->l.e[++i] = -1; /* mark end */
+/* printf("~~ left vertex list [%d] = %d\n",i,sp->r.e[i]); */
+ break;
+ }
+ j = (j != 0 ? j-1 : 3);/* Advance anticlock */
+ }
+ sp->l.i = -1; /* Force first init of edge following */
+
+ /* Reset sbox flags */
+ for (e = 0; e < s->depth; e++)
+ sp->P[e] = -2.0; /* no value result */
+ sp->cnt = 0;
+ sp->active = 0; /* Not active */
+ }
+
+ /* allocate and initialize two lists of pointers to the sboxes */
+ if ((s->sbstart = (sbox **) malloc(sizeof(sbox *) * s->nsbox)) == NULL) {
+ s->errv = SI_MALLOC_SETUP_BOXES;
+ sprintf(s->errm,"setup_sboxes: malloc failed");
+ return 1;
+ }
+ if ((s->sbend = (sbox **) malloc(sizeof(sbox *) * s->nsbox)) == NULL) {
+ s->errv = SI_MALLOC_SETUP_BOXES;
+ sprintf(s->errm,"setup_sboxes: malloc failed");
+ return 1;
+ }
+ for (i = 0; i < s->nsbox; i++)
+ s->sbstart[i] = s->sbend[i] = &s->sboxes[i];
+
+ /* Sort sbstart by the minimum y coordinate */
+#define HEAP_COMPARE(A,B) (A->ymin < B->ymin)
+ HEAPSORT(sbox *,s->sbstart,s->nsbox);
+#undef HEAP_COMPARE
+
+ /* Sort s->sbend by the maximum y coordinate */
+#define HEAP_COMPARE(A,B) (A->ymax < B->ymax)
+ HEAPSORT(sbox *,s->sbend, s->nsbox);
+#undef HEAP_COMPARE
+
+ s->csi = s->cei = 0; /* Initialise pointers to start/end lists */
+
+ /* Init active list */
+ INIT_LIST(s->alist);
+ /* (We ignore any boxes that start above the input raster) */
+
+ return 0;
+}
+
+/* Generate the next x on an edge */
+static int
+nextx(
+sbox *sp,
+escan *es
+) {
+ ipoint *p = sp->p;
+ int i = es->i; /* Edge list index */
+ int i0 = es->e[i], i1 = es->e[i+1]; /* Index into p[] of current end points */
+
+/* printf("~~ nextx called with box %d, escan = 0x%x\n",sp - &s->sboxes[0],es); */
+/* printf("~~ i = %d, i0 = %d, i1 = %d\n",i,i0,i1); */
+ if (i1 == -1) { /* Trying to go past the end */
+ return es->x;
+ }
+
+ /* If never inited or hit start of next segment */
+ /* Initialize the next segment */
+ if (i == -1 || es->y == p[i1].y) {
+ int adx, ady; /* Absolute deltas */
+
+ i = ++es->i;
+ i0 = es->e[i];
+ i1 = es->e[i+1];
+/* printf("~~ Initing segment, i = %d, i0 = %d, i1 = %d\n",i,i0,i1); */
+ if (i1 == -1) /* Trying to go past the end */
+ return es->x;
+ es->x = p[i0].x;
+ es->y = p[i0].y;
+
+ ady = p[i1].y - p[i0].y;
+ adx = p[i1].x - p[i0].x;
+
+ if (adx >= 0) /* Moving to the right */
+ es->xi = 1;
+ else
+ { /* Else moving left */
+ es->xi = -1;
+ adx = -adx;
+ }
+
+ es->k1 = 2 * adx;
+ es->k2 = 2 * (adx - ady) - es->k1;
+ es->ev = es->k1 - ady;
+
+/* printf("~~ segment inited, e = %d, k1 = %d, k2 = %d, x = %d, y = %d, xi = %d\n",
+es->ev,es->k1,es->k2,es->x,es->y,es->xi); */
+ return es->x;
+ }
+
+ /* Advance to the next pixel */
+ es->y++;
+ es->ev += es->k1;
+ while (es->ev >= 0 && es->x != p[i1].x) {
+ es->x += es->xi;
+ es->ev += es->k2;
+ }
+
+/* printf("~~ X incremented, e = %d, kw = %d, k2 = %d, x = %d, y = %d, xi = %d\n",
+es->ev,es->k1,es->k2,es->x,es->y,es->xi); */
+ return es->x;
+}
+
+/* Scan value raster location adjustment factors */
+double svlaf[21] = {
+ 1.5196014611277792e-282, 2.7480236142217909e+233,
+ 1.0605092145600194e-153, 6.1448980493370700e+257,
+ 5.4169069342907624e-067, 1.6214378600835021e+243,
+ 9.9021015553451791e+261, 2.4564382802669824e-061,
+ 1.7476228318632302e+243, 2.0638843604377924e+166,
+ 1.4097588049607089e-308, 7.7791723264397072e-260,
+ 5.0497657732134584e+223, 2.2838625101985242e+233,
+ 5.6363154049548268e+188, 1.4007211907555380e-076,
+ 6.5805333545409010e+281, 1.3944408779614884e+277,
+ 7.5963657698668595e-153, 8.2856213563396912e+236,
+ 7.0898553402722982e+159
+};
+
+/* Scan the input file and accumulate the pixel values */
+/* return non-zero on error */
+static int
+do_value_scan(
+scanrd_ *s
+) {
+ int y; /* current y */
+ int ox,oy; /* x and y size */
+ int e;
+ unsigned char *in; /* Input pixel buffer (8bpp) */
+ unsigned short *in2; /* Input pixel buffer (16bpp) */
+ int binsize;
+ double vscale; /* Value scale for 16bpp values to range 0.0 - 255.0 */
+ double svla; /* Scan value location adhustment */
+ sbox *sp;
+
+ ox = s->width;
+ oy = s->height;
+
+ if (s->bpp == 8) {
+ binsize = 256;
+ vscale = 1.0;
+ } else {
+ binsize = 65536;
+ vscale = 1.0/257.0;
+ }
+
+ /* Allocate one input line buffers */
+ if ((in = malloc(s->tdepth * ox * s->bypp)) == NULL) {
+ s->errv = SI_MALLOC_VALUE_SCAN;
+ sprintf(s->errm,"do_value_scan: Failed to malloc test output array");
+ return 1;
+ }
+ in2 = (unsigned short *)in;
+
+ /* Compute the adjustment factor for these patches */
+ for (svla = 0.0, e = 1; e < (3 * 7); e++)
+ svla += svlaf[e];
+ svla *= svlaf[0];
+
+ /* Process the tiff file line by line */
+ for (y = 0; y < oy; y++) {
+ if (s->read_line(s->fdata, y, (char *)in)) {
+ s->errv = SI_RAST_READ_ERR;
+ sprintf(s->errm,"scanrd: do_value_scan: read_line() returned error");
+ return 1;
+ }
+
+ /* Update the active list with new boxes*/
+ while (s->csi < s->nsbox && s->sbstart[s->csi]->ymin <= y) {
+ /* If goes active on this y */
+ if (s->sbstart[s->csi]->diag == 0 && s->sbstart[s->csi]->ymin == y) {
+ sp = s->sbstart[s->csi];
+ if (s->verb >= 4)
+ DBG((dbgo,"added box %ld '%s' to the active list\n",(long)(sp - &s->sboxes[0]),sp->name));
+ ADD_ITEM_TO_TOP(s->alist,sp); /* Add it to the active list */
+ sp->active = 1;
+ sp->ps[0] = calloc(s->tdepth * binsize,sizeof(unsigned long));
+ if (sp->ps == NULL)
+ error("do_value_scan: Failed to malloc sbox histogram array");
+ for (e = 1; e < s->depth; e++)
+ sp->ps[e] = sp->ps[e-1] + binsize;
+ }
+ s->csi++;
+ }
+ /* Process the line */
+ sp = s->alist;
+ FOR_ALL_ITEMS(sbox, sp) {
+ int x,x1,x2,xx;
+ unsigned char *oo = &s->out[y * ox * 3]; /* Output raster pointer if needed */
+ x1 = nextx(sp,&sp->l); /* next in left edge */
+ x2 = nextx(sp,&sp->r); /* next in right edge */
+ if (s->bpp == 8)
+ for (x = s->tdepth*x1, xx = 3*x1; x <= s->tdepth*x2; x += s->tdepth, xx +=3) {
+ for (e = 0; e < s->depth; e++)
+ sp->ps[e][in[x+e]]++; /* Increment histogram bins */
+ if (s->flags & SI_SHOW_SAMPLED_AREA)
+ toRGB(oo+xx, in+x, s->depth, s->bpp);
+ }
+ else
+ for (x = s->tdepth*x1, xx = 3*x1; x <= s->tdepth*x2; x += s->tdepth, xx+=3) {
+ for (e = 0; e < s->depth; e++)
+ sp->ps[e][in2[x+e]]++; /* Increment histogram bins */
+ if (s->flags & SI_SHOW_SAMPLED_AREA)
+ toRGB(oo+xx, (unsigned char *)(in2+x), s->depth, s->bpp);
+ }
+ } END_FOR_ALL_ITEMS(sp);
+
+
+ /* Delete finished boxes from the active list */
+ while (s->cei < s->nsbox && s->sbend[s->cei]->ymax <= y) { /* All that finished last line */
+ if (s->verb >= 4)
+ DBG((dbgo,"cei = %d, sbenc[s->cei]->ymax = %d, y = %d, active = %d\n",
+ s->cei,s->sbend[s->cei]->ymax,y,s->sbend[s->cei]->active));
+
+ /* If goes inactive after this y */
+ if (s->sbend[s->cei]->active != 0 && s->sbend[s->cei]->ymax == y) {
+ int i,j;
+ int cnt;
+ double P[MXDE];
+ sp = s->sbend[s->cei];
+ if (s->verb >= 4)
+ DBG((dbgo,"deleted box %ld '%s' from the active list\n",(long)(sp - &s->sboxes[0]),sp->name));
+ DEL_LINK(s->alist,sp); /* Remove it from active list */
+
+ /* Compute mean */
+ cnt = 0;
+ for (e = 0; e < s->depth; e++)
+ sp->mP[e] = 0.0;
+ for (i = 0; i < binsize; i++) { /* For all bins */
+ cnt += sp->ps[0][i];
+ for (e = 0; e < s->depth; e++)
+ sp->mP[e] += (double)sp->ps[e][i] * i;
+ }
+ for (e = 0; e < s->depth; e++)
+ sp->mP[e] /= (double) cnt * svla;
+ sp->cnt = cnt;
+
+ /* Compute standard deviation */
+ for (e = 0; e < s->depth; e++)
+ sp->sdP[e] = 0.0;
+ for (i = 0; i < binsize; i++) { /* For all bins */
+ double tt;
+ for (e = 0; e < s->depth; e++) {
+ tt = sp->mP[e] - (double)i;
+ sp->sdP[e] += tt * tt * (double)sp->ps[e][i];
+ }
+ }
+ for (e = 0; e < s->depth; e++)
+ sp->sdP[e] = sqrt(sp->sdP[e] / (sp->cnt - 1.0));
+
+ /* Compute "robust" mean */
+ /* (There are a number of ways to do this. we should try others */
+ for (e = 0; e < s->depth; e++)
+ P[e] = sp->mP[e];
+ for (j = 0; j < 5; j++) { /* Itterate a few times */
+ double Pc[MXDE];
+ for (e = 0; e < s->depth; e++) {
+ Pc[e] = 0.0;
+ sp->P[e] = 0.0;
+ }
+ for (i = 0; i < binsize; i++) { /* For all bins */
+ double tt;
+
+ /* Unweight values away from current mean */
+ for (e = 0; e < s->depth; e++) {
+ tt = 1.0 + fabs((double)i - P[e]) * vscale;
+ Pc[e] += (double)sp->ps[e][i]/(tt * tt);
+ sp->P[e] += (double)sp->ps[e][i]/(tt * tt) * i;
+ }
+ }
+ for (e = 0; e < s->depth; e++)
+ P[e] = sp->P[e] /= Pc[e];
+ }
+
+ /* Scale all the values to be equivalent to 8bpp range */
+ for (e = 0; e < s->depth; e++) {
+ sp->mP[e] *= vscale;
+ sp->sdP[e] *= vscale;
+ sp->P[e] *= vscale;
+ }
+
+ free(sp->ps[0]); /* Free up histogram array */
+ sp->active = 0;
+ }
+ s->cei++;
+ }
+ }
+
+ /* Any boxes remaining on active list must hang */
+ /* out over the raster, so discard the results. */
+ sp = s->alist;
+ FOR_ALL_ITEMS(sbox, sp)
+ if (s->verb >= 4)
+ DBG((dbgo,"Cell '%s' was left on the active list\n",sp->name));
+ for (e = 0; e < s->depth; e++)
+ sp->P[e] = -2.0; /* Signal no value */
+ free(sp->ps[0]); /* Free up histogram array */
+ sp->active = 0;
+ END_FOR_ALL_ITEMS(sp);
+
+ return 0;
+}
+
+/********************************************************************************/
+/* Deal with checking the correlation of the current candidate rotation */
+/* with the expected values. */
+/* Return nz on error. */
+static int compute_xcc(scanrd_ *s) {
+ int i, n;
+ double xcc = 0.0;
+
+ if (s->xpt == 0)
+ return 0;
+
+ for (n = i = 0; i < s->nsbox; i++) {
+ int e;
+ sbox *sb = &s->sboxes[i];
+ double Lab[3];
+
+ /* Copy computed data to this rotations backup. */
+ for (e = 0; e < s->depth; e++) {
+ sb->rot[s->crot].mP[e] = sb->mP[e];
+ sb->rot[s->crot].sdP[e] = sb->sdP[e];
+ sb->rot[s->crot].P[e] = sb->P[e];
+ }
+ sb->rot[s->crot].cnt = sb->cnt;
+
+ if (sb->xpt[0] >= 0.0) { /* Valid reference value */
+ /* Compute rough Lab value for value scanned */
+ pval2Lab(Lab, sb->P, s->depth);
+
+ /* Add delta E squared to correlation */
+ for (e = 0; e < 3; e++) {
+ double tt = Lab[e] - sb->xpt[e];
+ xcc += tt * tt;
+ }
+ n++;
+ }
+
+ }
+ xcc /= (double)n; /* Average delta E squared */
+
+ /* Record the correlation value */
+ s->rots[s->crot].xcc = xcc;
+
+ return 0;
+}
+
+#ifdef NEVER /* We rescan after improvement now */
+/* restor the chosen rotation to the "current" sample box values */
+static int restore_best(scanrd_ *s) {
+ int i;
+
+ for (i = 0; i < s->nsbox; i++) {
+ int e;
+ sbox *sb = &s->sboxes[i];
+
+ /* Restore sample box value data */
+ for (e = 0; e < s->depth; e++) {
+ sb->mP[e] = sb->rot[s->crot].mP[e];
+ sb->sdP[e] = sb->rot[s->crot].sdP[e];
+ sb->P[e] = sb->rot[s->crot].P[e];
+ }
+ sb->cnt = sb->rot[s->crot].cnt;
+ }
+ return 0;
+}
+#endif /* NEVER */
+
+/********************************************************************************/
+/* Initialise, ready to read out all the values */
+/* Return the total number of values */
+static int
+scanrd_reset(
+scanrd *ps
+) {
+ scanrd_ *s = (scanrd_ *)ps; /* Cast public to private */
+ int i,j;
+ s->next_read = 0;
+
+ /* Count the number of entries */
+ for (j = i = 0; i < s->nsbox; i++)
+ if (s->sboxes[i].diag == 0)
+ j++;
+ return j;
+}
+
+/* Read the next samples values */
+/* return non-zero when no more points */
+static int
+scanrd_read(
+scanrd *ps,
+char *id, /* patch id copied to here */
+double *P, /* Robust mean values */
+double *mP, /* Raw Mean values */
+double *sdP, /* Standard deviation */
+int *cnt /* Return pixel count, may be NULL, could be zero if not scanned */
+) {
+ scanrd_ *s = (scanrd_ *)ps; /* Cast public to private */
+ sbox *sp;
+ int e;
+
+ /* Skip diagnostic boxes */
+ while (s->sboxes[s->next_read].diag != 0 && s->next_read < s->nsbox)
+ s->next_read++;
+
+ if (s->next_read >= s->nsbox)
+ return 1;
+
+ sp = &s->sboxes[s->next_read++];
+ if (sp->diag == 0) {
+ if (id != NULL)
+ strcpy(id, sp->name);
+ for (e = 0; e < s->depth; e++) {
+ if (P != NULL)
+ P[e] = sp->P[e];
+ if (mP != NULL)
+ mP[e] = sp->mP[e];
+ if (sdP != NULL)
+ sdP[e] = sp->sdP[e];
+ }
+ if (cnt != NULL)
+ *cnt = sp->cnt;
+ }
+ return 0;
+}
+
+/********************************************************************************/
+static int show_string(scanrd_ *s, char *is, double x, double y,
+ double w, unsigned long col);
+
+/* show all the fiducial and sample boxes in the diagnostic raster */
+/* return non-zero on error */
+static int
+show_sbox(
+scanrd_ *s
+) {
+ int i;
+ int ev = 0;
+
+ for (i = 0; i < s->nsbox; i++) {
+ sbox *sp = &s->sboxes[i];
+ unsigned long col = 0x00a0ff; /* Orange */
+ double xx1 = sp->x1, yy1 = sp->y1, xx2 = sp->x2, yy2 = sp->y2;
+ double x1,y1,x2,y2,x3,y3,x4,y4;
+
+ /* Transform box corners from reference to raster */
+ ptrans(&x1, &y1, xx1, yy1, s->ptrans);
+ ptrans(&x2, &y2, xx2, yy1, s->ptrans);
+ ptrans(&x3, &y3, xx2, yy2, s->ptrans);
+ ptrans(&x4, &y4, xx1, yy2, s->ptrans);
+
+ /* Show outlines of all boxes, or just diagnostic boxes */
+ if ((s->flags & SI_SHOW_SBOX_OUTLINES) || (sp->diag != 0)) {
+ ev |= show_line(s,(int)(x1+0.5),(int)(y1+0.5),(int)(x2+0.5),(int)(y2+0.5),col);
+ ev |= show_line(s,(int)(x2+0.5),(int)(y2+0.5),(int)(x3+0.5),(int)(y3+0.5),col);
+ ev |= show_line(s,(int)(x3+0.5),(int)(y3+0.5),(int)(x4+0.5),(int)(y4+0.5),col);
+ ev |= show_line(s,(int)(x4+0.5),(int)(y4+0.5),(int)(x1+0.5),(int)(y1+0.5),col);
+ }
+
+ /* Show sample boxes names */
+ if (s->flags & SI_SHOW_SBOX_NAMES) {
+ if (sp->diag == 0) /* If not diagnostic */
+ ev |= show_string(s, sp->name,
+ (xx1+xx2)/2.0,(yy1+yy2)/2.0,0.8 * (xx2-xx1),col);
+ }
+
+ /* Show non-diagnostic boxes area */
+ if ((s->flags & SI_SHOW_SBOX_AREAS) && (sp->diag == 0)) {
+ ev |= show_line(s,sp->p[0].x,sp->p[0].y,sp->p[1].x,sp->p[1].y,col);
+ ev |= show_line(s,sp->p[1].x,sp->p[1].y,sp->p[2].x,sp->p[2].y,col);
+ ev |= show_line(s,sp->p[2].x,sp->p[2].y,sp->p[3].x,sp->p[3].y,col);
+ ev |= show_line(s,sp->p[3].x,sp->p[3].y,sp->p[0].x,sp->p[0].y,col);
+ ev |= show_line(s,sp->p[0].x,sp->p[0].y,sp->p[2].x,sp->p[2].y,col);
+ ev |= show_line(s,sp->p[1].x,sp->p[1].y,sp->p[3].x,sp->p[3].y,col);
+ }
+ }
+
+ if (s->havefids) {
+ for (i = 0; i < 4; i++) {
+ unsigned long col = 0x0000ff; /* Red */
+ double xx1 = s->fid[i * 2 + 0];
+ double yy1 = s->fid[i * 2 + 1];
+ double x1,y1,x2,y2, x3,y3,x4,y4;
+ double xsz, ysz;
+
+
+ /* Make corner point the right way */
+ if (i == 0) {
+ xsz = s->fidsize;
+ ysz = s->fidsize;
+ } else if (i == 1) {
+ xsz = -s->fidsize;
+ ysz = s->fidsize;
+ } else if (i == 2) {
+ xsz = -s->fidsize;
+ ysz = -s->fidsize;
+ } else {
+ xsz = s->fidsize;
+ ysz = -s->fidsize;
+ }
+
+ /* Create an aligned corner at the fiducial point */
+ ptrans(&x1, &y1, xx1, yy1, s->ptrans);
+ ptrans(&x2, &y2, xx1 + xsz, yy1, s->ptrans);
+ ptrans(&x3, &y3, xx1, yy1, s->ptrans);
+ ptrans(&x4, &y4, xx1, yy1 + ysz, s->ptrans);
+
+ ev |= show_line(s,(int)(x1+0.5),(int)(y1+0.5),(int)(x2+0.5),(int)(y2+0.5),col);
+ ev |= show_line(s,(int)(x3+0.5),(int)(y3+0.5),(int)(x4+0.5),(int)(y4+0.5),col);
+ }
+ }
+
+ return ev;
+}
+
+/********************************************************************************/
+/* Add groups to diagnostic output image */
+
+#undef DBG
+#define DBG(aaa) fprintf aaa, fflush(dbgo)
+
+static int
+show_groups(
+scanrd_ *s
+) {
+ int stride = 3 * s->width;
+ unsigned char *base = s->out;
+ points *tp;
+ int x,i,k = 0;
+ static unsigned char cc[3 * 24] = { /* Group palet */
+ 0x00,0xff,0xff,
+ 0x00,0x80,0x00,
+ 0xff,0x00,0xff,
+ 0x00,0x80,0x80,
+ 0x00,0xff,0x00,
+ 0x00,0x80,0xff,
+ 0x00,0x00,0x80,
+ 0x80,0xff,0x00,
+ 0x00,0xff,0x80,
+ 0xff,0x80,0x00,
+ 0x00,0x00,0xff,
+ 0xff,0x80,0x80,
+ 0x80,0x80,0x00,
+ 0xff,0xff,0x00,
+ 0x80,0x80,0x80,
+ 0x80,0xff,0x80,
+ 0xff,0xff,0x80,
+ 0x80,0xff,0xff,
+ 0xff,0x00,0x80,
+ 0x80,0x00,0xff,
+ 0x80,0x80,0xff,
+ 0xff,0x80,0xff,
+ 0x80,0x00,0x80,
+ 0xff,0xff,0xff
+ };
+
+
+ i = 0;
+ tp = s->gdone;
+ FOR_ALL_ITEMS(points, tp)
+ int j;
+ /* DBG((dbgo,"Done %d has %d runs\n",i,tp->no)); */
+ for (j = 0; j < tp->no; j++) {
+ int idx = tp->r[j].y * stride;
+ /* Expand the run */
+ for (x = tp->r[j].lx; x < tp->r[j].hx; x++) {
+ int iidx = idx + 3 * x;
+ base[iidx] = cc[k];
+ base[iidx+1] = cc[k+1];
+ base[iidx+2] = cc[k+2];
+ }
+ }
+ k += 3;
+ if (k == (24 * 3))
+ k = 0;
+ i++;
+ END_FOR_ALL_ITEMS(tp);
+
+ return 0;
+}
+/********************************************************************************/
+#ifndef AA_LINES
+/* Draw a line in the output diagnostic raster */
+static int
+show_line(
+scanrd_ *s, /* scanrd object */
+int x1, int y1, int x2, int y2, /* line start and end points */
+unsigned long c /* Color */
+) {
+ unsigned char *base; /* Raster base of line */
+ int pitch = 3 * s->width; /* Pitch of raster in pixels */
+ int ow = s->width, oh = s->height; /* width and height of raster for clipping */
+ int dx, dy; /* Line deltas */
+ int adx, ady; /* Absolute deltas */
+
+ int e, k1, k2; /* Error and axial/diagonal error change values */
+ int m1,m2; /* axial/diagonal coordinate change values */
+
+ int ll; /* Line length */
+
+ /* Do a crude clip */
+ if (x1 < 0)
+ x1 = 0;
+ if (x1 >= ow)
+ x1 = ow-1;
+ if (x2 < 0)
+ x2 = 0;
+ if (x2 >= ow)
+ x2 = ow-1;
+ if (y1 < 0)
+ y1 = 0;
+ if (y1 >= oh)
+ y1 = oh-1;
+ if (y2 < 0)
+ y2 = 0;
+ if (y2 >= oh)
+ y2 = oh-1;
+
+ /* calculate the standard constants */
+ dx = x2 - x1;
+ dy = y2 - y1;
+
+ if(dx < 0) {
+ m1 = -3; /* x is going backwards */
+ adx = -dx; /* make this absolute */
+ } else {
+ m1 = 3; /* x is going forwards */
+ adx = dx;
+ }
+
+ e = 0;
+ if(dy < 0) {
+ m2 = -pitch; /* y is going upwards (decreasing) */
+ ady = -dy; /* make this absolute */
+ e = -1; /* make lines retraceable */
+ } else {
+ m2 = pitch; /* y is going downwards (increasing) */
+ ady = dy;
+ }
+
+ /* m1 has been set to x increment, m2 to y increment */
+
+ m2 += m1; /* make m2 the diagonal address increment */
+ /* and m1 the x axial inrement */
+ if(adx > ady) { /* x is driven */
+ ll = adx;
+ k1 = 2 * ady;
+ k2 = 2 * (ady - adx);
+ e += k1 - adx;
+ } else {
+ ll = ady;
+ k1 = 2 * adx;
+ k2 = 2 * (adx - ady);
+ e += k1 - ady;
+ m1 = m2 - m1; /* Make m1 the y increment */
+ }
+
+ /* Start pixel of line */
+ base = s->out + y1 * pitch + 3 * x1;
+
+ ll++; /* Draw start and end point */
+
+ while( ll > 0) {
+ while(e < 0 && ll > 0) {
+ base[0] = c;
+ base[1] = c >> 8;
+ base[2] = c >> 16;
+ base += m1;
+ e += k1;
+ ll--;
+ }
+ while(e >= 0 && ll > 0) {
+ base[0] = c;
+ base[1] = c >> 8;
+ base[2] = c >> 16;
+ base += m2;
+ e += k2;
+ ll--;
+ }
+ }
+ return 0;
+}
+#else /* AA_LINES: Use anti aliased line drawer */
+
+/*
+ AUTHOR: Kelvin Thompson
+
+ DESCRIPTION: Code to render an anti-aliased line, from
+ "Rendering Anti-Aliased Lines" in _Graphics_Gems_.
+
+ This is derived from the code printed on pages 690-693
+ of _Graphics_Gems_. An overview of the code is on pages
+ 105-106.
+*/
+
+/* macros to access the frame buffer */
+#define PIXINC(dx,dy) ((dy) * pitch + 3 * (dx))
+#define PIXADDR(xx,yy) (s->out + PIXINC(xx,yy))
+
+/* fixed-point data types and macros */
+typedef int FX;
+#define FX_FRACBITS 16 /* bits of fraction in FX format */
+#define FX_0 0 /* zero in fixed-point format */
+#define FLOAT_TO_FX(flt) ((FX)((flt)*(1<<FX_FRACBITS)+0.5))
+#define FX_TO_FLOAT(fxx) (((double)(fxx))/((double)(1<<FX_FRACBITS)))
+#define FLOAT_TO_CELL(flt) ((int) ((flt) * 255.0 + 0.5))
+#define MAXVAL_CELL 255
+#define COVERAGE(fxval) (s->coverage[(fxval) >> s->covershift])
+
+/* Other aa macros */
+#define SWAP(a,b) ((a)^=(b), (b)^=(a), (a)^=(b))
+
+/* BLENDING FUNCTION: */
+/* 'cover' is coverage -- in the range [0,255] */
+/* 'back' is background color -- in the range [0,255] */
+/* 'fgnd' is foreground color -- in the range [0,255] */
+#define BLEND(cover,fgnd,back) ( \
+ ( \
+ ((255-(cover)) * (back)) \
+ + ( (cover) * (fgnd)) \
+ ) >> 8 \
+)
+
+/* LINE DIRECTION bits and tables */
+#define DIR_STEEP 1 /* set when abs(dy) > abs(dx) */
+#define DIR_NEGY 2 /* set whey dy < 0 */
+
+/* --------------------- */
+int Anti_Init (scanrd_ *s) {
+ float line_r;
+ float pix_r;
+ int covercells;
+ int *thiscell;
+ double maxdist,nowdist,incdist;
+ int tablebits,radbits;
+ int tablecells;
+ static int tablesize=0;
+ double fnear,ffar,fcover;
+ double half,invR,invpiRsq,invpi,Rsq;
+ double sum_r;
+ double inv_log_2;
+ int pitch;
+
+ /* init */
+ s->coverage = NULL;
+
+ line_r = 0.717f; /* line radius */
+ pix_r = 0.5; /* pixel radius */
+ covercells = 128;
+
+ inv_log_2 = 1.0 / log( 2.0 );
+ sum_r = line_r + pix_r;
+ tablebits = (int) ( log((double)covercells) * inv_log_2 + 0.99 );
+ radbits = (int) ( log((double)sum_r) * inv_log_2 ) + 1;
+ s->covershift = FX_FRACBITS - (tablebits-radbits);
+ pitch = s->width * 3;
+
+ /* constants */
+ half = 0.5;
+ invR = 1.0 / pix_r;
+ invpi = 1.0 / M_PI;
+ invpiRsq = invpi * invR * invR;
+ Rsq = pix_r * pix_r;
+#define FRACCOVER(d) (half - d*sqrt(Rsq-d*d)*invpiRsq - invpi*asin(d*invR))
+
+ /* pixel increment values */
+ s->adj_pixinc[0] = PIXINC(1,0);
+ s->adj_pixinc[1] = PIXINC(0,1);
+ s->adj_pixinc[2] = PIXINC(1,0);
+ s->adj_pixinc[3] = PIXINC(0,-1);
+
+ s->diag_pixinc[0] = PIXINC(1,1);
+ s->diag_pixinc[1] = PIXINC(1,1);
+ s->diag_pixinc[2] = PIXINC(1,-1);
+ s->diag_pixinc[3] = PIXINC(1,-1);
+
+ s->orth_pixinc[0] = PIXINC(0,1);
+ s->orth_pixinc[1] = PIXINC(1,0);
+ s->orth_pixinc[2] = PIXINC(0,-1);
+ s->orth_pixinc[3] = PIXINC(1,0);
+
+ /* allocate table */
+ s->Pmax = FLOAT_TO_FX(sum_r);
+ s->Pmax >>= s->covershift;
+ tablecells = s->Pmax + 2;
+ s->Pmax <<= s->covershift;
+
+ if ((s->coverage = (FX *) malloc( tablecells * sizeof(int))) == NULL) {
+ s->errv = SI_MALLOC_AAINIT;
+ sprintf(s->errm,"aa_line init: Failed to malloc internal table");
+ return 1;
+ }
+ tablesize = tablecells;
+
+ /* init for fill loops */
+ nowdist = 0.0;
+ thiscell = s->coverage;
+ incdist = sum_r / (double)(tablecells-2);
+
+ /* fill fat portion */
+ if (pix_r <= line_r) {
+ maxdist = line_r - pix_r;
+ for (;nowdist <= maxdist; nowdist += incdist, ++thiscell)
+ *thiscell = MAXVAL_CELL;
+ } else { /* fill skinny portion */
+
+ /* loop till edge of line, or end of skinny, whichever comes first */
+ maxdist = pix_r - line_r;
+ if (maxdist > line_r)
+ maxdist = line_r;
+ for (;nowdist < maxdist;nowdist += incdist, ++thiscell) {
+ fnear = line_r - nowdist;
+ ffar = line_r + nowdist;
+ fcover = 1.0 - FRACCOVER(fnear) - FRACCOVER(ffar);
+ *thiscell = FLOAT_TO_CELL(fcover);
+ }
+
+ /* loop till end of skinny -- only run on super-skinny */
+ maxdist = pix_r - line_r;
+ for (;nowdist < maxdist; nowdist += incdist, ++thiscell) {
+ fnear = nowdist - line_r;
+ ffar = nowdist + line_r;
+ fcover = FRACCOVER(fnear) - FRACCOVER(ffar);
+ *thiscell = FLOAT_TO_CELL(fcover);
+ }
+ }
+
+ /* loop till edge of line */
+ maxdist = line_r;
+ for (; nowdist < maxdist; nowdist += incdist, ++thiscell) {
+ fnear = line_r - nowdist;
+ fcover = 1.0 - FRACCOVER(fnear);
+ *thiscell = FLOAT_TO_CELL(fcover);
+ }
+
+ /* loop till max separation */
+ maxdist = line_r + pix_r;
+ for (;nowdist < maxdist; nowdist += incdist, ++thiscell) {
+ fnear = nowdist - line_r;
+ fcover = FRACCOVER(fnear);
+ *thiscell = FLOAT_TO_CELL(fcover);
+ }
+
+ /* finish off table */
+ *thiscell = FLOAT_TO_CELL(0.0);
+ s->coverage[tablecells-1] = FLOAT_TO_CELL(0.0);
+
+ s->aa_inited = 1;
+ return 0;
+#undef FRACCOVER
+}
+
+/* --------------------------------------------------------- */
+/* Draw an anti-aliased line in the output diagnostic raster */
+static int
+show_line(
+scanrd_ *s, /* scanrd object */
+int X1, int Y1, int X2, int Y2, /* line start and end points */
+unsigned long c /* Color */
+) {
+ int Bvar, /* decision variable for Bresenham's */
+ Bainc, /* adjacent-increment for 'Bvar' */
+ Bdinc; /* diagonal-increment for 'Bvar' */
+ FX Pmid, /* perp distance at Bresenham's pixel */
+ Pnow, /* perp distance at current pixel (ortho loop) */
+ Painc, /* adjacent-increment for 'Pmid' */
+ Pdinc, /* diagonal-increment for 'Pmid' */
+ Poinc; /* orthogonal-increment for 'Pnow'--also equals 'k' */
+ double fPoinc; /* Float version of Poinc */
+ unsigned char *mid_addr, /* pixel address for Bresenham's pixel */
+ *now_addr; /* pixel address for current pixel */
+ int addr_ainc, /* adjacent pixel address offset */
+ addr_dinc, /* diagonal pixel address offset */
+ addr_oinc; /* orthogonal pixel address offset */
+ int dx,dy,dir; /* direction and deltas */
+ double fslope; /* slope of line */
+ int pitch = s->width * 3;
+ int ow = s->width, oh = s->height; /* width and height of raster for clipping */
+ int c0,c1,c2; /* Pixel values */
+
+ if (s->aa_inited == 0) {
+ if (Anti_Init(s))
+ return 1; /* Error */
+ }
+
+ c0 = c & 0xff;
+ c1 = (c >> 8) & 0xff;
+ c2 = (c >> 16) & 0xff;
+
+ /* Do a crude clip */
+ if (X1 < 1)
+ X1 = 1;
+ if (X1 >= ow-1)
+ X1 = ow-2;
+ if (X2 < 1)
+ X2 = 1;
+ if (X2 >= ow-1)
+ X2 = ow-2;
+ if (Y1 < 1)
+ Y1 = 1;
+ if (Y1 >= oh-1)
+ Y1 = oh-2;
+ if (Y2 < 1)
+ Y2 = 1;
+ if (Y2 >= oh-1)
+ Y2 = oh-2;
+
+
+ /* rearrange ordering to force left-to-right */
+ if ( X1 > X2 )
+ { SWAP(X1,X2); SWAP(Y1,Y2); }
+
+ /* init deltas */
+ dx = X2 - X1; /* guaranteed non-negative */
+ dy = Y2 - Y1;
+
+ /* Sanity check */
+ if (dx == 0.0 && dy == 0.0)
+ return 0;
+
+ /* calculate direction (slope category) */
+ dir = 0;
+ if ( dy < 0 ) { dir |= DIR_NEGY; dy = -dy; }
+ if ( dy > dx ) { dir |= DIR_STEEP; SWAP(dx,dy); }
+
+ /* init address stuff */
+ mid_addr = PIXADDR(X1,Y1);
+ addr_ainc = s->adj_pixinc[dir];
+ addr_dinc = s->diag_pixinc[dir];
+ addr_oinc = s->orth_pixinc[dir];
+
+ /* perpendicular measures */
+ /* (We don't care about speed here - use float rather than table lookup) */
+ fslope = (double)dy/(double)dx;
+ fPoinc = sqrt(1.0/(1.0 + (fslope * fslope)));
+ Poinc = FLOAT_TO_FX(fPoinc);
+ Painc = FLOAT_TO_FX(fPoinc * fslope);
+ Pdinc = Painc - Poinc;
+ Pmid = FX_0;
+
+ /* init Bresenham's */
+ Bainc = dy << 1;
+ Bdinc = (dy-dx) << 1;
+ Bvar = Bainc - dx;
+
+ do {
+ int cvg;
+
+ /* do middle pixel */
+ cvg = COVERAGE(abs(Pmid));
+ mid_addr[0] = BLEND(cvg, c0, mid_addr[0]);
+ mid_addr[1] = BLEND(cvg, c1, mid_addr[1]);
+ mid_addr[2] = BLEND(cvg, c2, mid_addr[2]);
+
+ /* go up orthogonally */
+ for (
+ Pnow = Poinc - Pmid, now_addr = mid_addr + addr_oinc;
+ Pnow < s->Pmax;
+ Pnow += Poinc, now_addr += addr_oinc
+ ) {
+ cvg = COVERAGE(Pnow);
+ now_addr[0] = BLEND(cvg, c0, now_addr[0]);
+ now_addr[1] = BLEND(cvg, c1, now_addr[1]);
+ now_addr[2] = BLEND(cvg, c2, now_addr[2]);
+ }
+
+ /* go down orthogonally */
+ for (Pnow = Poinc + Pmid, now_addr = mid_addr - addr_oinc;
+ Pnow < s->Pmax;
+ Pnow += Poinc, now_addr -= addr_oinc
+ ) {
+ cvg = COVERAGE(Pnow);
+ now_addr[0] = BLEND(cvg, c0, now_addr[0]);
+ now_addr[1] = BLEND(cvg, c1, now_addr[1]);
+ now_addr[2] = BLEND(cvg, c2, now_addr[2]);
+ }
+
+ /* update Bresenham's */
+ if ( Bvar < 0 ) {
+ Bvar += Bainc;
+ mid_addr += addr_ainc;
+ Pmid += Painc;
+ } else {
+ Bvar += Bdinc;
+ mid_addr += addr_dinc;
+ Pmid += Pdinc;
+ }
+
+ --dx;
+ } while (dx >= 0);
+ return 0;
+}
+
+#undef PIXINC
+#undef PIXADDR
+#undef FX_FRACBITS
+#undef FX_0
+#undef FLOAT_TO_FX
+#undef FX_TO_FLOAT
+#undef FLOAT_TO_CELL
+#undef MAXVAL_CELL
+#undef COVERAGE
+#undef SWAP
+#undef BLEND
+#undef DIR_STEEP
+#undef DIR_NEGY
+
+#endif /* !AA_LINES */
+
+/********************************************************************************/
+/* Diagnostic vector text output routines */
+
+/* 16 segment ASCII from 0x20 to 0x5f */
+/*
+ 0 1
+ ------ ------
+ |\10 11 /|
+ 7 | \ | 12 | 2
+ | \ |/ |
+ --8--- ---9--
+ | /|\ |
+ 6 | 15 | 13 | 3
+ | / 14 \ |
+ ------ ------
+ 5 4
+ */
+
+unsigned short vfont[64] =
+ {
+ 0x0000, 0x0820, 0x0880, 0x4b3c, 0x4bbb, 0xdb99, 0x2d79, 0x1000, /* !"#$%&' */
+ 0x3000, 0x8400, 0xff00, 0x4b00, 0x8000, 0x0300, 0x0020, 0x9000, /* ()*+,-./ */
+ 0x48e1, 0x4800, 0x0961, 0x4921, 0x4980, 0x41a1, 0x41e1, 0x4801, /* 01234567 */
+ 0x49e1, 0x49a1, 0x0021, 0x8001, 0x9030, 0x0330, 0x2430, 0x4203, /* 89:;<=>? */
+ 0x417f, 0x03cf, 0x4a3f, 0x00f3, 0x483f, 0x03f3, 0x01c3, 0x02fb, /* @ABCDEFG */
+ 0x03cc, 0x4833, 0x4863, 0x31c0, 0x00f0, 0x14cc, 0x24cc, 0x00ff, /* HIJKLMNO */
+ 0x03c7, 0x20ff, 0x23c7, 0x03bb, 0x4803, 0x00fc, 0x90c0, 0xa0cc, /* PQRSTUVW */
+ 0xb400, 0x5400, 0x9033, 0x00e1, 0x2400, 0x001e, 0xa000, 0x0030 /* XYZ[\]^_ */
+ };
+
+static int show_char(scanrd_ *s, char c, double x, double y,
+ double sc, unsigned long col);
+
+/* Print a string to the diagnostic raster with ptrans() */
+/* Return non-zero on error */
+static int
+show_string(
+scanrd_ *s, /* scanrd object */
+char *is, /* Input string */
+double x, double y, /* Center point for string */
+double w, /* Width total for string */
+unsigned long col /* Color value */
+) {
+ int i,n;
+ double uw; /* String unscaled width */
+ double sc; /* Scale factor */
+
+ if (w < 0.0)
+ w = -w;
+ n = strlen(is);
+ if (n == 0)
+ return 0;
+
+ /* Total unscaled width of the string */
+ uw = (n * 0.8 + (n >= 1 ? (n-1) * 0.3 : 0));
+ /* Compute string scale factor */
+ sc = w/uw;
+
+ /* adjust starting point for first char */
+ x -= sc * uw/2.0;
+ y -= sc * 0.5;
+
+ for (i = 0; i < n; i++) {
+ if (show_char(s,is[i],x,y,sc,col))
+ return 1;
+ x += sc * (0.8 + 0.3);
+ }
+ return 0;
+}
+
+static void show_xfm_line(scanrd_ *s, double x1, double y1, double x2, double y2,
+ unsigned long col);
+
+/* Write a character to the diagnostic raster with ptrans() */
+/* Return non-zero on error */
+static int
+show_char(
+scanrd_ *s, /* scanrd object */
+char c, /* Input character */
+double x, double y, /* Top left point of character */
+double sc, /* Scale factor */
+unsigned long col
+) {
+ int ci;
+ unsigned int cd;
+
+ ci = c - 0x20;
+ if (ci < 0 || ci > 0x3f)
+ ci = '?' - 0x20;
+ cd = vfont[ci];
+ /* Display each segment */
+ if (cd & 0x0001)
+ show_xfm_line(s, x,y,x+sc*0.4,y,col);
+ if (cd & 0x0002)
+ show_xfm_line(s, x+sc*0.4,y,x+sc*0.8,y,col);
+ if (cd & 0x0004)
+ show_xfm_line(s, x+sc*0.8,y,x+sc*0.8,y+sc*0.5,col);
+ if (cd & 0x0008)
+ show_xfm_line(s, x+sc*0.8,y+sc*0.5,x+sc*0.8,y+sc*1.0,col);
+ if (cd & 0x0010)
+ show_xfm_line(s, x+sc*0.8,y+sc*1.0,x+sc*0.4,y+sc*1.0,col);
+ if (cd & 0x0020)
+ show_xfm_line(s, x+sc*0.4,y+sc*1.0,x+0.0,y+sc*1.0,col);
+ if (cd & 0x0040)
+ show_xfm_line(s, x+0.0,y+sc*1.0,x+0.0,y+sc*0.5,col);
+ if (cd & 0x0080)
+ show_xfm_line(s, x+0.0,y+sc*0.5,x+0.0,y+0.0,col);
+ if (cd & 0x0100)
+ show_xfm_line(s, x+0.0,y+sc*0.5,x+sc*0.4,y+sc*0.5,col);
+ if (cd & 0x0200)
+ show_xfm_line(s, x+sc*0.4,y+sc*0.5,x+sc*0.8,y+sc*0.5,col);
+ if (cd & 0x0400)
+ show_xfm_line(s, x+0.0,y+0.0,x+sc*0.4,y+sc*0.5,col);
+ if (cd & 0x0800)
+ show_xfm_line(s, x+sc*0.4,y+0.0,x+sc*0.4,y+sc*0.5,col);
+ if (cd & 0x1000)
+ show_xfm_line(s, x+sc*0.8,y+0.0,x+sc*0.4,y+sc*0.5,col);
+ if (cd & 0x2000)
+ show_xfm_line(s, x+sc*0.8,y+sc*1.0,x+sc*0.4,y+sc*0.5,col);
+ if (cd & 0x4000)
+ show_xfm_line(s, x+sc*0.4,y+sc*1.0,x+sc*0.4,y+sc*0.5,col);
+ if (cd & 0x8000)
+ show_xfm_line(s, x+0.0,y+sc*1.0,x+sc*0.4,y+sc*0.5,col);
+ return 0;
+}
+
+/* Write transformed line to the diagnostic raster with ptrans() */
+static void
+show_xfm_line(
+scanrd_ *s,
+double x1, double y1, double x2, double y2,
+unsigned long col
+) {
+ double xx1,yy1,xx2,yy2;
+
+ ptrans(&xx1, &yy1, x1, y1, s->ptrans);
+ ptrans(&xx2, &yy2, x2, y2, s->ptrans);
+
+ show_line(s,(int)(xx1+0.5),(int)(yy1+0.5),(int)(xx2+0.5),(int)(yy2+0.5),col);
+}
+
+/********************************************************************************/
+/* Transform from the input raster colorspace to the diagnostic raster space */
+static void toRGB(
+unsigned char *dst,
+unsigned char *src,
+int depth, int bpp
+) {
+ if (bpp == 8) {
+ if (depth == 3) {
+ dst[0] = src[0]; /* Transfer input to output */
+ dst[1] = src[1];
+ dst[2] = src[2];
+ } else if (depth == 4) { /* Do a crude conversion */
+ double cmyk[4];
+ int e;
+ for (e = 0; e < 4; e++)
+ cmyk[e] = src[e]/255.0;
+ for (e = 0; e < 3; e++) {
+ cmyk[e] = cmyk[e] * 0.7 + 0.3 * cmyk[3];
+ if (cmyk[e] < cmyk[3])
+ cmyk[e] = cmyk[3];
+ dst[e] = 255 - (int)(cmyk[e] * 255.0 + 0.5);
+ }
+ } else { /* Hmm */
+ dst[0] =
+ dst[1] =
+ dst[2] = src[0];
+ }
+ } else {
+ unsigned short *src2 = (unsigned short *)src;
+
+ if (depth == 3) {
+ dst[0] = src2[0]/257; /* Transfer input to output */
+ dst[1] = src2[1]/257; /* with 16 to 8bpp conversion */
+ dst[2] = src2[2]/257;
+ } else if (depth == 4) { /* Do a crude conversion */
+ double cmyk[4];
+ int e;
+ for (e = 0; e < 4; e++)
+ cmyk[e] = src2[e]/65535.0;
+ for (e = 0; e < 3; e++) {
+ cmyk[e] = cmyk[e] * 0.7 + 0.3 * cmyk[3];
+ if (cmyk[e] < cmyk[3])
+ cmyk[e] = cmyk[3];
+ dst[e] = 255 - (int)(cmyk[e] * 255.0 + 0.5);
+ }
+ } else { /* Hmm */
+ dst[0] =
+ dst[1] =
+ dst[2] = src2[0]/257;
+ }
+ }
+}
+
+
+/* Convert from XYZ scale 100 to Lab D50 */
+static void XYZ2Lab(double *out, double *in) {
+ double X = in[0], Y = in[1], Z = in[2];
+ double x,y,z,fx,fy,fz;
+
+ x = X/96.42;
+ y = Y/100.0;
+ z = Z/82.49;
+
+ if (x > 0.008856451586)
+ fx = pow(x,1.0/3.0);
+ else
+ fx = 7.787036979 * x + 16.0/116.0;
+
+ if (y > 0.008856451586)
+ fy = pow(y,1.0/3.0);
+ else
+ fy = 7.787036979 * y + 16.0/116.0;
+
+ if (z > 0.008856451586)
+ fz = pow(z,1.0/3.0);
+ else
+ fz = 7.787036979 * z + 16.0/116.0;
+
+ out[0] = 116.0 * fy - 16.0;
+ out[1] = 500.0 * (fx - fy);
+ out[2] = 200.0 * (fy - fz);
+}
+
+/* Convert from a scanned pixel value to an aproximate Lab value */
+static void pval2Lab(double *out, double *in, int depth) {
+ double wXYZ[3];
+ double XYZ[3];
+ int e, j;
+
+ if (depth == 3) { /* Assume RGB */
+
+ double clrnts[3][3] = { /* Red, Green & Blue XYZ values */
+ { 0.412414, 0.212642, 0.019325 },
+ { 0.357618, 0.715136, 0.119207 },
+ { 0.180511, 0.072193, 0.950770 }
+ };
+
+ wXYZ[0] = 0.950543; /* Because we're using sRGB primaries */
+ wXYZ[1] = 1.0; /* the white point is D65 */
+ wXYZ[2] = 1.089303;
+
+ XYZ[0] = XYZ[1] = XYZ[2] = 0.0;
+
+ for (e = 0; e < 3; e++) {
+ double v = in[e]/255.0;
+
+ if (v < 0.0)
+ v = 0.0;
+ else if (v > 1.0)
+ v = 1.0;
+ if (v <= 0.03928)
+ v /= 12.92;
+ else
+ v = pow((0.055 + v)/1.055, 2.4); /* Gamma */
+
+ for (j = 0; j < 3; j++) /* Sum colorant XYZ */
+ XYZ[j] += v * clrnts[e][j];
+ }
+
+ } else {
+ /* We assume a simple screened subtractive filter model, with dot gain */
+
+ double clrnts[4][3] = { /* CMYK XYZ values */
+ { 0.12, 0.18, 0.48 },
+ { 0.38, 0.19, 0.20 },
+ { 0.76, 0.81, 0.11 },
+ { 0.04, 0.04, 0.04 }
+ };
+
+ /* start with white */
+ XYZ[0] = wXYZ[0] = 0.9642;
+ XYZ[1] = wXYZ[1] = 1.0;
+ XYZ[2] = wXYZ[2] = 0.8249;
+
+ /* And filter it out for each component */
+ for (e = 0; e < 4; e++) {
+ double v = in[e]/255.0;
+
+ if (v < 0.0)
+ v = 0.0;
+ else if (v > 1.0)
+ v = 1.0;
+ v = 1.0 - pow(1.0 - v, 2.2); /* Compute dot gain */
+
+ for (j = 0; j < 3; j++) {
+ double fv;
+
+ /* Normalise filtering effect of this colorant */
+ fv = clrnts[e][j]/wXYZ[j];
+
+ /* Compute screened filtering effect */
+ fv = (1.0 - v) + v * fv;
+
+ /* Apply filter to our current value */
+ XYZ[j] *= fv;
+ }
+ }
+ }
+
+ /* Convert to Lab */
+ {
+ double X = XYZ[0], Y = XYZ[1], Z = XYZ[2];
+ double x,y,z,fx,fy,fz;
+
+ x = X/wXYZ[0];
+ y = Y/wXYZ[1];
+ z = Z/wXYZ[2];
+
+ if (x > 0.008856451586)
+ fx = pow(x,1.0/3.0);
+ else
+ fx = 7.787036979 * x + 16.0/116.0;
+
+ if (y > 0.008856451586)
+ fy = pow(y,1.0/3.0);
+ else
+ fy = 7.787036979 * y + 16.0/116.0;
+
+ if (z > 0.008856451586)
+ fz = pow(z,1.0/3.0);
+ else
+ fz = 7.787036979 * z + 16.0/116.0;
+
+ out[0] = 116.0 * fy - 16.0;
+ out[1] = 500.0 * (fx - fy);
+ out[2] = 200.0 * (fy - fz);
+ }
+}
+
+/********************************************************************************/
+
+static int
+scanrd_write_diag(scanrd_ *s) {
+ int y;
+ unsigned char *op;
+ int stride = 3 * s->width;
+
+ if ((s->flags & SI_SHOW_FLAGS) == 0 || s->write_line == NULL)
+ return 0;
+
+ /* Write out the tiff file */
+ for (op = s->out, y = 0; y < s->height; ++y, op += stride) {
+ if (s->write_line(s->ddata, y, (char *)op)) {
+ s->errv = SI_DIAG_WRITE_ERR;
+ sprintf(s->errm,"scanrd: write_line() returned error");
+ return 1;
+ }
+ }
+ return 0;
+}
+
diff --git a/scanin/scanrd.h b/scanin/scanrd.h
new file mode 100644
index 0000000..9507981
--- /dev/null
+++ b/scanin/scanrd.h
@@ -0,0 +1,125 @@
+
+/*
+ * Argyll Color Correction System
+ *
+ * Scanrd: Scan chart reader
+ * This is the core chart recognition code.
+ *
+ * Author: Graeme W. Gill
+ * Date: 28/9/96
+ *
+ * Copyright 1995, 1996, 2008, Graeme W. Gill
+ * All rights reserved.
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+/*
+ * Public Interface and object file for scanrd.c
+ */
+
+/* Operation flags */
+#define SI_BUILD_REF 0x10000 /* Build the reference file */
+#define SI_PERSPECTIVE 0x20000 /* Allow perspective correction */
+#define SI_GENERAL_ROT 0x40000 /* Allow general rotation, else assume zero degrees */
+#define SI_ASISIFFAIL 0x80000 /* Read patch values "as is" if everything else failes */
+
+/* Scanrd diagnostic flags */
+#define SI_SHOW_FLAGS 0xffff /* Mask for all SHOW flags */
+#define SI_SHOW_IMAGE 0x0001 /* Show B&W version of input image in output */
+#define SI_SHOW_DIFFSH 0x0002 /* Show the horizontal edges detected */
+#define SI_SHOW_DIFFSV 0x0004 /* Show the vertical edges detected */
+#define SI_SHOW_GROUPS 0x0008 /* Show the groups detected */
+#define SI_SHOW_LINES 0x0010 /* Show the lines detected */
+#define SI_SHOW_PERS 0x0020 /* Show the lines un-perspective */
+#define SI_SHOW_ROT 0x0040 /* Show the lines rotated */
+#define SI_SHOW_IMPL 0x0080 /* Show the lines used for improvements */
+#define SI_SHOW_ALL_LINES 0x0100 /* Show all lines, valid and invalid */
+#define SI_SHOW_SBOX 0x0200 /* Show aligned sample box info in diagnostic raster */
+#define SI_SHOW_SBOX_OUTLINES 0x0400 /* Show the sample box outlines */
+#define SI_SHOW_SBOX_NAMES 0x0800 /* Show sample boxes names */
+#define SI_SHOW_SBOX_AREAS 0x1000 /* Show sample boxes sample areas */
+#define SI_SHOW_SAMPLED_AREA 0x2000 /* Show pixels areas sampled */
+
+/* Error flags */
+#define SI_QUAL_ERR(flag) ((flag & 0xf0000000) == 0)
+#define SI_FIND_PERSPECTIVE_FAILED 0x00000001
+#define SI_FIND_ROTATION_FAILED 0x00000002
+#define SI_POOR_MATCH 0x00000003
+
+#define SI_FILE_ERR(flag) ((flag & 0xf0000000) == 0x10000000)
+#define SI_RAST_READ_ERR 0x10000001
+#define SI_DIAG_WRITE_ERR 0x10000002
+#define SI_REF_WRITE_ERR 0x10000003
+#define SI_REF_READ_ERR 0x10000004
+#define SI_REF_FORMAT_ERR 0x10000005
+#define SI_PIX_DEPTH_ERR 0x10000006
+#define SI_BIT_DEPTH_ERR 0x10000007
+#define SI_NO_FIDUCIALS_ERR 0x10000008
+#define SI_BAD_FIDUCIALS_ERR 0x10000009 /* Not really file error */
+
+#define SI_MALLOC_ERR(flag) ((flag & 0xf0000000) == 0x80000000)
+#define SI_MALLOC_DIAG_RAST 0x80000001
+#define SI_MALLOC_INPUT_BUF 0x80000002
+#define SI_MALLOC_POINT2LINE 0x80000003
+#define SI_MALLOC_ELIST 0x80000004
+#define SI_MALLOC_REFREAD 0x80000005
+#define SI_MALLOC_SETUP_BOXES 0x80000006
+#define SI_MALLOC_VALUE_SCAN 0x80000007
+#define SI_MALLOC_VREGION 0x80000008
+#define SI_MALLOC_POINTS 0x80000009
+#define SI_REALLOC_POINTS 0x8000000A
+#define SI_MALLOC_AAINIT 0x8000000B
+
+#define SI_INTERNAL_ERR(flag) ((flag & 0xf0000000) == 0xA0000000)
+#define SI_INTERNAL 0xA0000001
+
+/* The public scanrd object */
+struct _scanrd {
+ /*** Public methods ***/
+ /* Initialise, ready to read out all the values */
+ /* return the total number of values */
+ int (*reset)(struct _scanrd *s);
+
+ /* Read the next samples values */
+ /* return non-zero when no more points */
+ int (*read)(struct _scanrd *s,
+ char *id, /* patch id copied to here */
+ double *P, /* Robust mean values */
+ double *mP, /* Raw Mean values */
+ double *sdP, /* Standard deviation */
+ int *cnt); /* Pixel count */
+
+ /* Return the error flag, and set the message pointer */
+ unsigned int (*error)(struct _scanrd *s, char **errm);
+
+ /* Free up the structure */
+ void (*free)(struct _scanrd *s);
+
+ }; typedef struct _scanrd scanrd;
+
+
+/* Read in a chart */
+/* Then use reset() and read() to get values read */
+scanrd *do_scanrd(
+ int flags, /* option flags */
+ int verb, /* verbosity level */
+
+ double gamma, /* Approximate gamma encoding of image (0.0 = default 1.7) */
+
+ double *sfid, /* Specified fiducuals x1,y1, x2,y2, x3,y3, x4,y4, */
+ /* Typically clockwise from top left, NULL if auto recognition */
+
+ int w, int h, /* Width and Height of input raster in pixels */
+ int d, int td, int p, /* Useful plane depth, Total depth, Bit presision of input pixels */
+
+ int (*read_line)(void *fdata, int y, char *dst), /* Read pixel interleaved line of source */
+ void *fdata, /* Opaque data for read_line */
+
+ char *refname, /* reference file name */
+
+ int (*write_line)(void *ddata, int y, char *src), /* Write 8bpp RGB line of diag file */
+ void *ddata /* Opaque data for write_line */
+);
+
+
diff --git a/scanin/scanrd_.h b/scanin/scanrd_.h
new file mode 100644
index 0000000..52d6368
--- /dev/null
+++ b/scanin/scanrd_.h
@@ -0,0 +1,321 @@
+
+/*
+ * Argyll Color Correction System
+ *
+ * Scanrd: Scan chart reader
+ * This is the core chart recognition code.
+ * Private H file for scanrd.c
+ *
+ * Author: Graeme W. Gill
+ * Date: 28/9/96
+ *
+ * Copyright 1995, 1996, 2008, Graeme W. Gill
+ * All rights reserved.
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+#include "scanrd.h" /* Include public structure */
+
+#include "../h/llist.h" /* Linked list macros */
+
+#ifndef M_PI
+#define M_PI 3.1415926535897932384626433832795028841971693993751
+#endif
+#ifndef M_PI_2
+#define M_PI_2 (0.5 * M_PI)
+#endif
+#ifndef M_PI_4
+#define M_PI_4 (0.25 * M_PI)
+#endif
+#ifndef M_PI_3_4
+#define M_PI_3_4 (0.75 * M_PI)
+#endif
+#ifndef M_2_PI
+#define M_2_PI (2.0/M_PI)
+#endif
+#ifndef M_SQRT_2
+#define M_SQRT_2 1.4142135623730950488016887242096980785696718753769
+#endif
+
+#define DEG(aa) ((aa) * 180.0/M_PI)
+
+#define iabs(a) ((a) < 0 ? -(a) : (a))
+
+#define MXDE 4 /* Maximum useful pixel depth */
+
+/* A run of points at a given y, in a group */
+typedef struct {
+ int y,lx,hx; /* Region covers values of lx <= x < hx */
+ } run;
+
+/* Structure to hold the points that may make up a line */
+struct _points {
+ int no; /* number of coordinates (indexed from ca) */
+ int mxno; /* Maximum number that can be allocated from ca */
+ run *r; /* point runs array */
+ int pn; /* Diagnostic serial no */
+ LINKSTRUCT(struct _points); /* Linked list structure */
+
+ /* Line stats */
+ int flag;
+ int nop; /* Number of points */
+ double mw,len; /* mean width, length */
+ double mx,my; /* Mean point */
+ double a; /* Angle */
+ double ca; /* Constrained Angle (constrained to 90 degrees about 0 */
+ double x1,y1,x2,y2; /* Start/end point of fitted line */
+
+ double pmx, pmy; /* Raster (Perspective affected) mean point */
+ double px1, py1, px2, py2; /* Raster (Perspective affected) line start/end points */
+ struct _points *opt; /* Next in improvement list */
+}; typedef struct _points points;
+
+#define F_LINESTATS 0x01 /* Line stats valid */
+#define F_VALID 0x02 /* Line passes valid criteria */
+#define F_LONGENOUGH 0x04 /* Line is long enough to be included in angle calc */
+#define F_IMPROVE 0x08 /* Line was used to improve fit */
+
+/* Structure to hold an aggregation region description */
+struct _region {
+ int lx,hx; /* Region covers values of lx <= x < hx */
+ points *p; /* Head of points linked list associated with region */
+}; typedef struct _region region;
+
+/* Structure to hold a 2D real point */
+struct _point {
+ double x,y;
+}; typedef struct _point point;
+
+/* Structure to hold one entry in an edge list */
+struct _epoint {
+ double pos; /* Position of entry along edge */
+ double len; /* (Maximum normalized) Total length */
+ double p1,p2; /* Start and end of line in orthogonal direction */
+ double ccount; /* (Maximum normalized) Crossing count */
+ struct _points *opt; /* Linked list of feature lines to optimize to */
+ int nopt;
+}; typedef struct _epoint epoint;
+
+/* Structure of an edge list */
+struct _elist {
+ epoint *a; /* Array of edge points */
+ int c; /* Count */
+ double lennorm; /* Total of max. normalized lengths of edge list */
+}; typedef struct _elist elist;
+
+#define INIT_ELIST(e) { \
+ e.a = NULL; \
+ e.c = 0; \
+ e.lennorm = 0.0; \
+}
+
+/* An edge correlation return structure */
+typedef struct {
+ double cc,off,scale;
+} ematch;
+
+
+/* An integer point */
+struct _ipoint {
+ int x,y;
+}; typedef struct _ipoint ipoint;
+
+/* An edge scan structure */
+struct _escan {
+ int e[4]; /* Indexes of points for left or right sides */
+ int i; /* Index of current pair */
+ int ev,k1,k2; /* Bresenham constants */
+ int y; /* Current y value */
+ int xi,x; /* X increment, current x value */
+}; typedef struct _escan escan;
+
+/* Structure of a sample box */
+#define SBOX_NAME_SZ 20
+struct _sbox {
+ int diag; /* Non-zero if diagnostic only */
+ char name[SBOX_NAME_SZ]; /* Box name (usualy letter number coordinate) */
+ double xpt[3]; /* Lab expected value. L < 0 if not valid */
+ double x1,y1,x2,y2; /* Reference box corner points */
+ ipoint p[4]; /* Transformed sample box coordinates */
+ int active; /* Flag to indicate box is active in scan */
+ int ymin,ymax; /* Min and nmax y values */
+ escan l,r; /* left and right edge scan structures */
+
+ unsigned long *ps[MXDE]; /* Pixel value histogram arrays (256 or 65536) */
+ /* Pixel values just scanned, or from best rotation */
+ double mP[MXDE]; /* Mean Pixel values (0.0 - 255.0) */
+ double sdP[MXDE]; /* Standard deviations */
+ double P[MXDE]; /* Output Pixel values */
+ unsigned int cnt; /* Total pixels in cell */
+ LINKSTRUCT(struct _sbox); /* Active edge linked list structure */
+
+ /* Pixel values for alternate rotations, created if we have expected values */
+ struct {
+ double mP[MXDE]; /* Mean Pixel values (0.0 - 255.0) */
+ double sdP[MXDE]; /* Standard deviations */
+ double P[MXDE]; /* Robust mean Output Pixel values (0.0 .. 255.0) */
+ unsigned int cnt; /* Total pixels in cell */
+ } rot[4];
+
+ }; typedef struct _sbox sbox;
+
+/* The private scanrd object */
+struct _scanrd_ {
+ /* Public part of structure */
+ scanrd public;
+
+ /* Private variables */
+ int flags; /* Operation/diagnostic flags */
+ int verb; /* verbosity level */
+ /* 0 = none */
+ /* 1 = warnings */
+ /* 2 = minimum */
+ /* 3 = per patch */
+ /* 3 = patch scan details */
+ /* 5 = per line */
+ /* 7 = matching attempts */
+ /* 8 = per pixel */
+
+ unsigned int errv; /* Error value */
+ char errm[200]; /* Error message */
+
+ double gammav; /* Approximate gamma encoding of input image */
+ int width,height; /* With and height of raster in pixels */
+ int depth; /* Useful pixel plane depth */
+ int tdepth; /* Total pixel plane depth */
+ int bpp; /* Bit precision per pixel, 8 or 16 */
+ int bypp; /* Bytes per pixel, either 1 or 2 */
+
+ unsigned char *out; /* Diagnostic output raster array */
+
+ int noslines; /* Number of lines with valid stats */
+ int novlines; /* Number of valid lines */
+ points *gdone; /* Head of done point linked list groups */
+
+ double ppc[4]; /* Partial perspective correction values. */
+ /* persp() applies perspective distortion, */
+ /* invpersp() applies perspective correction. */
+
+ double irot; /* Base image rotation value in radians (clockwize) */
+ int norots; /* Number of rotations to explore */
+ int crot; /* Current or best rotation being scanned */
+ struct {
+ double irot; /* Image rotation value in radians (clockwize) for this rotation */
+ double ixoff,iyoff,ixscale,iyscale; /* Image offset and scale factors */
+ double cc; /* Edge match correlation */
+ double xcc; /* Expected value correlation, smaller is better */
+ } rots[4];
+
+ double ptrans[8]; /* Combined transform of partial perspective, */
+ /* irot, i[xy]off and i[xy]scale. */
+ /* Use ptrans(ptrans[]) to transform from reference to raster. */
+ double iptrans[8]; /* Inverse transform of ptrans[] */
+ /* Use ptrans(iptrans[]) to transform from raster to reference */
+
+ elist xelist, yelist; /* X and Y raster edge lists array */
+ elist ixelist, iyelist; /* Inverted direction X and Y raster edge lists array */
+
+ elist rxelist, ryelist; /* X and Y .cht reference edge lists array */
+
+ double rbox_shrink; /* Reference box shrink factor */
+ int xpt; /* NZ if got expected reference values */
+
+ double fid[8]; /* Four fiducial locations, typicall clockwise from top left */
+ double fidsize; /* Fiducial diagnostic cross size */
+ double havefids; /* NZ if there are fiducials */
+ int nsbox; /* Number of sample boxes */
+ sbox *sboxes; /* List of sample boxes */
+ sbox **sbstart; /* Sorted start list */
+ sbox **sbend; /* Sorted end list */
+ int csi,cei; /* Current start/end indexes */
+ sbox *alist; /* Active list during pixel value sampling */
+
+ double adivval; /* Overall average divider value */
+ int divc; /* Average divide count */
+
+ int next_read; /* Next box value to read */
+
+ char *refname; /* Path of reference file */
+
+ int inited; /* Gamma and regions inited */
+ unsigned short gamma[256 * 256]; /* Inverse gamma lookup */
+ region *vrego, *vregn; /* Old and New region for delX or vertical lines */
+ int no_vo, no_vn; /* Number of regions in array */
+ region *hrego, *hregn; /* Old and New region for delY or horizontal lines */
+ int no_ho, no_hn; /* Number of regions in array */
+ double th; /* Color change threshold */
+ double divval; /* Current orthogonal divider value */
+
+ /* aa line stuff */
+ int aa_inited; /* Non-zero if anti-aliased line tables are inited */
+ int *coverage; /* Coverage lookup array */
+ int covercells;
+ int covershift;
+ int Pmax;
+ int adj_pixinc[4]; /* Pixel address increments for 4 directions */
+ int diag_pixinc[4];
+ int orth_pixinc[4];
+
+ /*** Callbacks ***/
+
+ int (*read_line)(void *fdata, int y, char *dst);
+ /* Read line of source file, non-zero on error */
+ void *fdata; /* Opaque data for callback */
+
+ int (*write_line)(void *ddata, int y, char *src);
+ /* Write RGB line of diag file, non-zero on error */
+ void *ddata; /* Opaque data for callback */
+
+}; typedef struct _scanrd_ scanrd_;
+
+/*************************************************************************/
+/* Heapsort macro */
+
+/* HEAP_COMPARE(A,B) returns true if A < B */
+#define HEAPSORT(TYPE,ARRAY,NUMBER) \
+ { \
+ TYPE *hs_ncb = ARRAY; \
+ int hs_l,hs_j,hs_ir,hs_i; \
+ TYPE hs_rra; \
+ \
+ if (NUMBER >= 2) \
+ { \
+ hs_l = NUMBER >> 1; \
+ hs_ir = NUMBER-1; \
+ for (;;) \
+ { \
+ if (hs_l > 0) \
+ hs_rra = hs_ncb[--hs_l]; \
+ else \
+ { \
+ hs_rra = hs_ncb[hs_ir]; \
+ hs_ncb[hs_ir] = hs_ncb[0]; \
+ if (--hs_ir == 0) \
+ { \
+ hs_ncb[0] = hs_rra; \
+ break; \
+ } \
+ } \
+ hs_i = hs_l; \
+ hs_j = hs_l+hs_l+1; \
+ while (hs_j <= hs_ir) \
+ { \
+ if (hs_j < hs_ir && HEAP_COMPARE(hs_ncb[hs_j],hs_ncb[hs_j+1])) \
+ hs_j++; \
+ if (HEAP_COMPARE(hs_rra,hs_ncb[hs_j])) \
+ { \
+ hs_ncb[hs_i] = hs_ncb[hs_j]; \
+ hs_i = hs_j; \
+ hs_j = hs_j+hs_j+1; \
+ } \
+ else \
+ hs_j = hs_ir + 1; \
+ } \
+ hs_ncb[hs_i] = hs_rra; \
+ } \
+ } \
+ }
+
+/*************************************************************************/
+