1
- import { Image , Point , ThresholdAlgorithm } from '../..' ;
1
+ import {
2
+ Image ,
3
+ Point ,
4
+ ThresholdAlgorithm ,
5
+ overlapImages ,
6
+ writeSync ,
7
+ } from '../..' ;
2
8
import {
3
9
LevelingAlgorithm ,
4
10
getAlignMask ,
5
11
prepareForAlign ,
6
12
} from '../../align/align' ;
13
+ import { max } from '../../operations/greyAlgorithms' ;
7
14
8
15
import {
9
- ComputeNbOperationsOptions ,
16
+ ComputeXYMarginsOptions ,
10
17
computeNbOperations ,
11
18
computeXYMargins ,
12
19
} from './utils/computeNbOperations' ;
13
20
import { findOverlap } from './utils/findOverlap' ;
14
21
import { getMinDiffTranslation } from './utils/getMinDiffTranslation' ;
15
22
16
- export interface AlignDifferentSizeOptions extends ComputeNbOperationsOptions {
23
+ export interface AlignDifferentSizeOptions extends ComputeXYMarginsOptions {
17
24
/**
18
25
* Maximal number of operations that the algorithm can perform.
19
26
* This number is used to rescale the images if they are too big so that
20
27
* the algorithm takes roughly always the same time to compute.
28
+ * You can use scalingFactor instead to specify the scaling factor to apply,
29
+ * in that case, maxNbOperations should be undefined.
21
30
* @default 1e8
22
31
*/
23
32
maxNbOperations ?: number ;
33
+ /**
34
+ * Scaling factor to apply to the images before the rough alignment phase.
35
+ * Can only be used if maxnbOperations is undefined.
36
+ * @default undefined
37
+ */
38
+ scalingFactor ?: number ;
24
39
/**
25
40
* Threshold algorithm to use for the alignment masks.
26
41
* @default 'otsu'
@@ -47,10 +62,20 @@ export interface AlignDifferentSizeOptions extends ComputeNbOperationsOptions {
47
62
* @default 0.1
48
63
*/
49
64
minFractionPixels ?: number ;
65
+ /**
66
+ * Minimal number of overlapping pixels to apply the algorithm.
67
+ * @default undefined
68
+ */
69
+ minNbPixels ?: number ;
70
+ /**
71
+ * Display debug information?
72
+ * @default false
73
+ */
74
+ debug ?: boolean ;
50
75
}
51
76
52
77
/**
53
- * Align two different size images by finding the position wchich minimises the difference.
78
+ * Align two different size images by finding the position which minimises the difference.
54
79
* @param source - Source image.
55
80
* @param destination - Destination image.
56
81
* @param options - Align different size options.
@@ -64,34 +89,82 @@ export function alignDifferentSize(
64
89
const {
65
90
xFactor = 0.5 ,
66
91
yFactor = 0.5 ,
67
- maxNbOperations = 1e8 ,
68
92
precisionFactor = 1.5 ,
69
93
thresholdAlgoritm = 'otsu' ,
70
94
level = 'minMax' ,
71
95
blurKernelSize,
72
96
minFractionPixels,
97
+ debug = false ,
73
98
} = options ;
74
99
75
- const margins = computeXYMargins ( source , destination , { xFactor, yFactor } ) ;
100
+ let maxNbOperations ;
101
+ let scalingFactor ;
102
+ if (
103
+ options . maxNbOperations === undefined &&
104
+ options . scalingFactor === undefined
105
+ ) {
106
+ maxNbOperations = 1e8 ;
107
+ } else if (
108
+ options . maxNbOperations !== undefined &&
109
+ options . scalingFactor === undefined
110
+ ) {
111
+ maxNbOperations = options . maxNbOperations ;
112
+ } else if (
113
+ options . maxNbOperations === undefined &&
114
+ options . scalingFactor !== undefined
115
+ ) {
116
+ scalingFactor = options . scalingFactor ;
117
+ } else {
118
+ throw new Error ( 'You cannot define both maxNbOperations and scalingFactor' ) ;
119
+ }
76
120
77
- const nbOperations = computeNbOperations ( source , destination , margins ) ;
121
+ const margins = computeXYMargins ( source , destination , { xFactor , yFactor } ) ;
78
122
79
- let scalingFactor = 1 ;
80
- if ( nbOperations > maxNbOperations ) {
81
- scalingFactor = Math . sqrt ( nbOperations / maxNbOperations ) ;
123
+ if ( maxNbOperations !== undefined ) {
124
+ const initialSrcMask = getAlignMask ( source , thresholdAlgoritm ) ;
125
+ const initialDstMask = getAlignMask ( destination , thresholdAlgoritm ) ;
126
+ const nbOperations = computeNbOperations ( source , destination , {
127
+ sourceMask : initialSrcMask ,
128
+ destinationMask : initialDstMask ,
129
+ margins,
130
+ } ) ;
131
+ if ( debug ) {
132
+ console . log ( { nbOperations } ) ;
133
+ }
134
+ scalingFactor = 1 ;
135
+ if ( nbOperations > maxNbOperations ) {
136
+ scalingFactor = Math . sqrt ( nbOperations / maxNbOperations ) ;
137
+ }
138
+ }
139
+ if ( debug ) {
140
+ console . log ( { scalingFactor } ) ;
82
141
}
142
+
83
143
// Rough alignment
84
144
const smallSource = prepareForAlign ( source , {
85
145
scalingFactor,
86
146
level,
87
147
blurKernelSize,
88
148
} ) ;
89
- const smallMask = getAlignMask ( smallSource , thresholdAlgoritm ) ;
149
+ const smallSrcMask = getAlignMask ( smallSource , thresholdAlgoritm ) ;
90
150
const smallDestination = prepareForAlign ( destination , {
91
151
scalingFactor,
92
152
level,
93
153
blurKernelSize,
94
154
} ) ;
155
+ const smallDstMask = getAlignMask ( smallDestination , thresholdAlgoritm ) ;
156
+
157
+ const smallNbPixels =
158
+ ( smallSrcMask . getNbNonZeroPixels ( ) + smallDstMask . getNbNonZeroPixels ( ) ) / 2 ;
159
+
160
+ if ( debug ) {
161
+ console . log ( { smallNbPixels } ) ;
162
+ writeSync ( `${ __dirname } /smallSource.png` , smallSource ) ;
163
+ writeSync ( `${ __dirname } /smallDestination.png` , smallDestination ) ;
164
+ writeSync ( `${ __dirname } /smallSrcMask.png` , smallSrcMask ) ;
165
+ writeSync ( `${ __dirname } /smallDstMask.png` , smallDstMask ) ;
166
+ }
167
+
95
168
const smallMargins = computeXYMargins ( smallSource , smallDestination , {
96
169
xFactor,
97
170
yFactor,
@@ -101,14 +174,26 @@ export function alignDifferentSize(
101
174
smallSource ,
102
175
smallDestination ,
103
176
{
104
- sourceMask : smallMask ,
177
+ sourceMask : smallSrcMask ,
178
+ destinationMask : smallDstMask ,
105
179
leftRightMargin : smallMargins . xMargin ,
106
180
topBottomMargin : smallMargins . yMargin ,
107
- minFractionPixels ,
181
+ minNbPixels : smallNbPixels ,
108
182
} ,
109
183
) ;
110
184
185
+ if ( debug ) {
186
+ console . log ( { roughTranslation } ) ;
187
+ const overlap = overlapImages ( smallSource , smallDestination , {
188
+ origin : roughTranslation ,
189
+ } ) ;
190
+ writeSync ( `${ __dirname } /roughOverlap.png` , overlap ) ;
191
+ }
192
+
111
193
// Find overlapping surface and source and destination origins
194
+ if ( debug ) {
195
+ console . log ( 'Find overlap' ) ;
196
+ }
112
197
const scaledTranslation = {
113
198
column : Math . round ( roughTranslation . column * scalingFactor ) ,
114
199
row : Math . round ( roughTranslation . row * scalingFactor ) ,
@@ -142,23 +227,38 @@ export function alignDifferentSize(
142
227
blurKernelSize,
143
228
scalingFactor : 1 ,
144
229
} ) ;
145
- const mask = getAlignMask ( preciseSource , thresholdAlgoritm ) ;
230
+ const srcMask = getAlignMask ( preciseSource , thresholdAlgoritm ) ;
146
231
const preciseDestination = prepareForAlign ( destinationCrop , {
147
232
level,
148
233
blurKernelSize,
149
234
scalingFactor : 1 ,
150
235
} ) ;
236
+ const dstMask = getAlignMask ( preciseDestination , thresholdAlgoritm ) ;
237
+ if ( debug ) {
238
+ writeSync ( `${ __dirname } /preciseSource.png` , preciseSource ) ;
239
+ writeSync ( `${ __dirname } /preciseDestination.png` , preciseDestination ) ;
240
+ writeSync ( `${ __dirname } /srcMask.png` , srcMask ) ;
241
+ writeSync ( `${ __dirname } /dstMask.png` , dstMask ) ;
242
+ }
243
+
244
+ const nbPixels =
245
+ ( srcMask . getNbNonZeroPixels ( ) + dstMask . getNbNonZeroPixels ( ) ) / 2 ;
151
246
152
247
const preciseMargins = Math . round ( precisionFactor * scalingFactor ) ;
153
248
249
+ if ( debug ) {
250
+ console . log ( 'Compute precise translation' ) ;
251
+ }
154
252
const preciseTranslation = getMinDiffTranslation (
155
253
preciseSource ,
156
254
preciseDestination ,
157
255
{
158
256
leftRightMargin : preciseMargins ,
159
257
topBottomMargin : preciseMargins ,
160
258
minFractionPixels,
161
- sourceMask : mask ,
259
+ sourceMask : srcMask ,
260
+ destinationMask : dstMask ,
261
+ minNbPixels : nbPixels ,
162
262
} ,
163
263
) ;
164
264
0 commit comments