8 /* if defined, residual is calculated and loop is aborted, if no improvement
9 * is reached. this does not seem to work, so it should be undefined */
12 /* just process, but do not generate neighbor list and do not solve multigrid */
13 //#define TEST_NO_RENDER
15 /* paste all color+luminance pyramids (from large to small) into result */
16 //#define TEST_PYRAMID
18 /* test flow planes by drawing vectors into image
19 * note: this only works in combination with TEST_PYRAMID
23 /* test makred planes as pyramid on the result
24 * note: this only works in combination with TEST_PYRAMID
28 /* test shrinking of X and Y only */
29 //#define TEST_SHRINK_XY
31 /* test shrinking of X, Y and Z */
32 //#define TEST_SHRINK_Z
34 /* test expanding of X and Y only */
35 //#define TEST_EXPAND_XY
37 /* test expanding of X, Y and Z */
38 //#define TEST_EXPAND_Z
40 void ASSERT_FLOW(int w, int h, int k, const darray_t *flow, const darray_t *flow_i)
43 ASSERT(darrayGetNumberOfDimensions(flow) == 4, "flow array must have 4 dimensions");
44 ASSERT(w == darrayGetDimensions(flow)[0], "x-sizes of flow vectors are different");
45 ASSERT(h == darrayGetDimensions(flow)[1], "y-sizes of flow vectors are different");
46 ASSERT(k-1 == darrayGetDimensions(flow)[2], "z-sizes of flow vectors are not k-1");
47 ASSERT(2 == darrayGetDimensions(flow)[3], "number of flow cubes are not 2");
50 ASSERT(darrayGetNumberOfDimensions(flow_i) == 4, "flow_i array must have 4 dimensions");
51 ASSERT(w == darrayGetDimensions(flow_i)[0], "x-sizes of flow_i vectors are different");
52 ASSERT(h == darrayGetDimensions(flow_i)[1], "y-sizes of flow_i vectors are different");
53 ASSERT(k-1 == darrayGetDimensions(flow_i)[2], "z-sizes of flow_i vectors are not k-1");
54 ASSERT(2 == darrayGetDimensions(flow_i)[3], "number of flow_i cubes are not 2");
58 #define NEIGHBORS_2D 9
59 #define NEIGHBORS_3D 27
62 int num; /* # of neighbors */
63 int index[NEIGHBORS_2D]; /* index of neighbors in an array */
64 double weight[NEIGHBORS_2D]; /* calculated weight of neighbors */
67 int num; /* # of neighbors */
68 int index[NEIGHBORS_3D]; /* index of neighbors in an array */
69 double weight[NEIGHBORS_3D]; /* calculated weight of neighbors */
72 static struct neigh_off {
74 } neigh_off[NEIGHBORS_3D] = {
75 { 0, 0, 0 }, /* entry [0] always points to the same point */
84 { 0, 0, 1 }, /* here begins the 3D part */
104 /* Shrink matrix by reducing z planes
106 * Every second (odd) plane is removed in the followin way:
107 * - The even plane is taken and the following odd plane is summed, using
108 * forward motion flow vectors. (If the motion vectors are not ouside the
110 * - The result is averaged.
112 * if the number of planes are odd, the last (even) plane is just copied.
114 * src plane 0 ---->_(+) dst plane 0
117 * / forward flow plane 0
122 * src plane 2 ---->_(+) dst plane 1
125 * / forward flow plane 2
130 * src plane 4 -------> dst plane 2
132 int shrink_z(darray_t *src, darray_t *dst, const darray_t *flow)
134 int x, y, z, xx, yy, w, h, ks, kd;
136 double *src_ptr, *dst_ptr, *sum_ptr, *flow_x_ptr, *flow_y_ptr;
137 darray_t *sum = NULL;
140 ASSERT(darrayGetNumberOfDimensions(src) == 3, "src array must have 3 dimensions");
141 w = darrayGetDimensions(src)[0];
142 h = darrayGetDimensions(src)[1];
143 ks = darrayGetDimensions(src)[2];
146 /* create dst, if not given */
148 int dimensions[3] = {w, h, kd};
150 dst = darrayCreate(3, dimensions);
156 ASSERT(darrayGetNumberOfDimensions(dst) == 3, "dst array must have 3 dimensions");
157 ASSERT(w == darrayGetDimensions(dst)[0], "x-size of source and destination must be equal");
158 ASSERT(h == darrayGetDimensions(dst)[1], "y-size of source and destination must be equal");
159 ASSERT(kd == ((ks+1)>>1), "z-size of destination must be half of the source (+an extra plane if source z-size is odd)");
160 ASSERT_FLOW(w, h, ks, flow, NULL);
162 sum = darrayCreate(3, darrayGetDimensions(dst));
166 /* clear dst array with zeros, fill sum array with ones */
168 dst_ptr = darrayGetPr(dst);
169 for (i = 0; i < pixles; i++) {
170 darrayCheckPr(dst, dst_ptr, 1);
173 sum_ptr = darrayGetPr(sum);
174 for (i = 0; i < pixles; i++) {
175 darrayCheckPr(sum, sum_ptr, 1);
179 /* first copy all even planes */
180 for (z = 0; z < ks; z++) {
183 darrayCheckPr(src, darrayGetPr(src) + z*w*h, w*h);
184 darrayCheckPr(dst, darrayGetPr(dst) + (z/2)*w*h, w*h);
185 memcpy(darrayGetPr(dst) + (z/2)*w*h, darrayGetPr(src) + z*w*h, w*h*sizeof(double));
188 /* project all odd planes */
189 for (z = 0; z < ks; z++) {
192 /* project up via forward flow */
193 src_ptr = darrayGetPr(src) + z*w*h;
194 dst_ptr = darrayGetPr(dst) + (z/2)*w*h;
195 sum_ptr = darrayGetPr(sum) + (z/2)*w*h;
196 flow_x_ptr = darrayGetPr(flow) + (z-1)*w*h;
197 flow_y_ptr = flow_x_ptr + (ks-1)*w*h;
198 for (y = 0; y < h; y++) {
199 for (x = 0; x < w; x++) {
200 darrayCheckPr(flow, flow_x_ptr, 1);
201 xx = x + *flow_x_ptr++;
202 darrayCheckPr(flow, flow_y_ptr, 1);
203 yy = y + *flow_y_ptr++;
204 if (xx>=0 && yy>=0 && xx<w && yy<h) {
205 darrayCheckPr(dst, dst_ptr, 1);
206 darrayCheckPr(src, src_ptr + xx + yy*w, 1);
207 *dst_ptr += src_ptr[xx + yy*w];
208 darrayCheckPr(sum, sum_ptr, 1);
217 /* average projected pixles */
219 dst_ptr = darrayGetPr(dst);
220 sum_ptr = darrayGetPr(sum);
221 for (i = 0; i < pixles; i++) {
222 darrayCheckPr(dst, dst_ptr, 1);
223 darrayCheckPr(sum, sum_ptr, 1);
224 *dst_ptr++ /= *sum_ptr++;
229 darrayMove(dst, src);
242 /* Expand matrix by expanding z planes
244 * The array is expanded by inserting new planes in the following way:
245 * - Forward flow vectors are used to project previous src plane to inserted
246 * odd destination plane. The weight is A.
247 * - Also forward flow vectors are used to project next src plane to inserted
248 * odd destination plane. The weight is B.
249 * - The result is averaged.
250 * - First border plane is just copied. (weighted 1.0)
251 * - Last border plane is projected using forward flow vector. (weighted 1.0)
252 * - Forward flow vectors are used to project previous source plane to even
253 * destination plane. The weight is B.
254 * - the source plane is copied to even destination plane. The weight is A.
255 * - All pixles pixles on the new plane result from two planes. Due
256 * to optical flow, none or many pixles are summed. All pixles on the
257 * new plane are summed and then averaged. At locations where no pixle
258 * was projected, the result is 0.
260 * If the number of planes in the result array is odd, it is just copied.
262 * The array (src) is scaled to array (dst) as shown:
265 * dst[1] = src[0] * A + src[1] * B
266 * dst[2] = src[1] * A + src[0] * B
267 * dst[3] = src[1] * A + src[2] * B
268 * dst[4] = src[2] * A + src[1] * B
271 * The scaling shown in a different way:
273 * src plane 0 -------> dst plane 0
277 * \ dst forward flow plane 0
281 * / dst forward flow plane 1
287 * \ dst forward flow plane 0
288 * \ dst forward flow plane 1
290 * src plane 1 -*A-->(+) dst plane 2
294 * \ dst forward flow plane 2
298 * / dst forward flow plane 3
304 * \ dst forward flow plane 2
305 * \ dst forward flow plane 3
307 * src plane 2 -*A-->(+) dst plane 4
311 * \ dst forward flow plane 4
316 * if odd number of dst planes:
317 * src plane 3 -------> dst plane 6
322 static int expand_z(darray_t *src, const darray_t *dst, const darray_t *flow)
324 int x, y, z, xx, yy, xxx, yyy, w, h, ks, kd, k_;
327 double *src1_ptr, *src2_ptr, *dst1_ptr, *dst2_ptr, *sum1_ptr, *sum2_ptr, *flow_x1_ptr, *flow_y1_ptr, *flow_x2_ptr, *flow_y2_ptr;
328 darray_t *sum = NULL;
329 double A = sqrt(2) / 2;
332 ASSERT(darrayGetNumberOfDimensions(src) == 3, "src array must have 3 dimensions");
333 ASSERT(darrayGetNumberOfDimensions(dst) == 3, "dst array must have 3 dimensions");
334 w = darrayGetDimensions(src)[0];
335 h = darrayGetDimensions(src)[1];
336 ks = darrayGetDimensions(src)[2];
337 kd = darrayGetDimensions(dst)[2];
338 ASSERT(w == darrayGetDimensions(dst)[0], "x-size of source and destination must be equal");
339 ASSERT(h == darrayGetDimensions(dst)[1], "y-size of source and destination must be equal");
340 ASSERT(ks == ((kd+1)>>1), "z-size of source must be half of the destination (+an extra plane if destination z-size is odd)");
341 ASSERT_FLOW(w, h, kd, flow, NULL);
343 sum = darrayCreate(3, darrayGetDimensions(dst));
347 /* clear dst array with zeroes, keep sum with zeroes */
349 dst1_ptr = darrayGetPr(dst);
350 for (i = 0; i < pixles; i++)
354 k_ = kd&(~1); /* even number of planes */
356 /* copy first plane with weight 1.0 */
357 darrayCheckPr(dst, darrayGetPr(dst) + z*w*h, w*h);
358 darrayCheckPr(src, darrayGetPr(src) + (z/2)*w*h, w*h);
359 memcpy(darrayGetPr(dst) + z*w*h, darrayGetPr(src) + (z/2)*w*h, w*h*sizeof(double));
360 /* fill sum array with ones */
362 sum1_ptr = darrayGetPr(sum) + z*w*h;
363 for (i = 0; i < pixles; i++)
367 /* project planes in between */
368 for (z = 0; z < k_-2; z+=2) {
369 dst1_ptr = darrayGetPr(dst) + z*w*h;
370 dst2_ptr = dst1_ptr + w*h;
371 sum1_ptr = darrayGetPr(sum) + z*w*h;
372 sum2_ptr = sum1_ptr + w*h;
373 src1_ptr = darrayGetPr(src) + (z/2)*w*h;
374 src2_ptr = src1_ptr + w*h;
375 flow_x1_ptr = darrayGetPr(flow) + z*w*h;
376 flow_y1_ptr = flow_x1_ptr + (kd-1)*w*h;
377 flow_x2_ptr = flow_x1_ptr + w*h;
378 flow_y2_ptr = flow_y1_ptr + w*h;
379 for (y = 0; y < h; y++) {
380 for (x = 0; x < w; x++) {
381 /* even plane (weight A) */
382 darrayCheckPr(dst, dst2_ptr + x + y*w, 1);
383 darrayCheckPr(src, src2_ptr + x + y*w, 1);
384 dst2_ptr[x + y*w] += src2_ptr[x + y*w] * A;
385 darrayCheckPr(sum, sum2_ptr + x + y*w, 1);
386 sum2_ptr[x + y*w] += A;
387 /* get position via firt plane */
388 darrayCheckPr(flow, flow_x1_ptr, 1);
390 darrayCheckPr(flow, flow_y1_ptr, 1);
394 if (xx<0 || yy<0 || xx>=w || yy>=h)
396 /* odd plane (weight A)*/
397 darrayCheckPr(dst, dst1_ptr + xx + yy*w, 1);
398 darrayCheckPr(src, src1_ptr + x + y*w, 1);
399 dst1_ptr[xx + yy*w] += src1_ptr[x + y*w] * A;
400 darrayCheckPr(sum, sum1_ptr + xx + yy*w, 1);
401 sum1_ptr[xx + yy*w] += A;
402 /* follow position via second plane */
403 darrayCheckPr(flow, flow_x2_ptr + xx + yy*w, 1);
404 _x += flow_x2_ptr[xx + yy*w];
405 darrayCheckPr(flow, flow_y2_ptr + xx + yy*w, 1);
406 _y += flow_y2_ptr[xx + yy*w];
409 if (xxx<0 || yyy<0 || xxx>=w || yyy>=h)
411 /* even plane (weight B) */
412 darrayCheckPr(dst, dst2_ptr + xxx + yyy*w, 1);
413 darrayCheckPr(src, src1_ptr + x + y*w, 1);
414 dst2_ptr[xxx + yyy*w] += src1_ptr[x + y*w] * B;
415 darrayCheckPr(sum, sum2_ptr + xxx + yyy*w, 1);
416 sum2_ptr[xxx + yyy*w] += B;
417 /* odd plane (weigth B) */
418 darrayCheckPr(dst, dst1_ptr + xx + yy*w, 1);
419 darrayCheckPr(src, src2_ptr + xxx + yyy*w, 1);
420 dst1_ptr[xx + yy*w] += src2_ptr[xxx + yyy*w] * B;
421 darrayCheckPr(sum, sum1_ptr + xx + yy*w, 1);
422 sum1_ptr[xx + yy*w] += B;
427 /* project last plane with weight 1.0 */
428 dst1_ptr = darrayGetPr(dst) + z*w*h;
429 sum1_ptr = darrayGetPr(sum) + z*w*h;
430 src1_ptr = darrayGetPr(src) + (z/2)*w*h;
431 flow_x1_ptr = darrayGetPr(flow) + z*w*h;
432 flow_y1_ptr = flow_x1_ptr + (kd-1)*w*h;
433 for (y = 0; y < h; y++) {
434 for (x = 0; x < w; x++) {
435 /* get position via firt plane */
436 darrayCheckPr(flow, flow_x1_ptr, 1);
438 darrayCheckPr(flow, flow_y1_ptr, 1);
442 if (xx<0 || yy<0 || xx>=w || yy>=h)
444 /* border (odd) plane (weight 1.0)*/
445 darrayCheckPr(dst, dst1_ptr + xx + yy*w, 1);
446 darrayCheckPr(src, src1_ptr + x + y*w, 1);
447 dst1_ptr[xx + yy*w] += src1_ptr[x + y*w];
448 darrayCheckPr(sum, sum1_ptr + xx + yy*w, 1);
449 sum1_ptr[xx + yy*w] += 1.0;
455 /* copy extra plane, if destiantion size is odd */
457 darrayCheckPr(dst, darrayGetPr(dst) + z*w*h, w*h);
458 darrayCheckPr(src, darrayGetPr(src) + (z/2)*w*h, w*h);
459 memcpy(darrayGetPr(dst) + z*w*h, darrayGetPr(src) + (z/2)*w*h, w*h*sizeof(double));
460 /* fill sum array with ones */
462 sum1_ptr = darrayGetPr(sum) + z*w*h;
463 for (i = 0; i < pixles; i++)
467 /* average projected pixles */
469 dst1_ptr = darrayGetPr(dst);
470 sum1_ptr = darrayGetPr(sum);
471 for (i = 0; i < pixles; i++) {
472 /* only if there is a sum */
473 darrayCheckPr(sum, sum1_ptr, 1);
474 darrayCheckPr(dst, dst1_ptr, 1);
476 *dst1_ptr++ /= *sum1_ptr;
493 /* Shrink flow planes
495 * Each pair of flow planes is shrinked by combining (tracing) two planes
496 * in forward direction.
498 * If the number of flow planes is odd, the extra plane is removed.
500 int shrink_flow_z(darray_t *src, darray_t *dst)
502 int w, h, ks, kd, x, y, z, xx, yy;
504 double *src_x1_ptr, *src_x2_ptr, *src_y1_ptr, *src_y2_ptr, *dst_x_ptr, *dst_y_ptr;
507 ASSERT(darrayGetNumberOfDimensions(src) == 4, "src array must have 4 dimensions");
508 w = darrayGetDimensions(src)[0];
509 h = darrayGetDimensions(src)[1];
510 ks = darrayGetDimensions(src)[2];
511 ASSERT(2 == darrayGetDimensions(src)[3], "number of cubes must be 2");
515 int dimensions[4] = {w, h, kd, 2};
517 dst = darrayCreate(4, dimensions);
523 ASSERT(darrayGetNumberOfDimensions(dst) == 4, "dst array must have 4 dimensions");
524 ASSERT(w == darrayGetDimensions(dst)[0], "x-size must be equal");
525 ASSERT(h == darrayGetDimensions(dst)[1], "y-size must be equal");
526 ASSERT(kd == darrayGetDimensions(dst)[2], "z-size of dest must be half of the planes + optional odd plane");
527 ASSERT(2 == darrayGetDimensions(dst)[3], "number of cubes must be 2");
529 for (z = 0; z < kd; z++) {
530 src_x1_ptr = darrayGetPr(src) + (z*2)*w*h;
531 src_y1_ptr = src_x1_ptr + w*h*ks;
532 src_x2_ptr = src_x1_ptr + w*h;
533 src_y2_ptr = src_y1_ptr + w*h;
534 dst_x_ptr = darrayGetPr(dst) + z*w*h;
535 dst_y_ptr = dst_x_ptr + w*h*kd;
536 for (y = 0; y < h; y++) {
537 for (x = 0; x < w; x++) {
538 /* get position via first plane */
539 darrayCheckPr(src, src_x1_ptr, 1);
541 darrayCheckPr(src, src_y1_ptr, 1);
545 if (xx<0 || yy<0 || xx>=w || yy>=h) {
546 /* use an outside value when outside in the first plane */
547 darrayCheckPr(dst, dst_x_ptr, 1);
549 darrayCheckPr(dst, dst_y_ptr, 1);
553 /* follow position via second plane */
554 darrayCheckPr(src, src_x2_ptr + xx + yy*w, 1);
555 _x += src_x2_ptr[xx + yy*w];
556 darrayCheckPr(src, src_y2_ptr + xx + yy*w, 1);
557 _y += src_y2_ptr[xx + yy*w];
558 /* store displacement result */
559 darrayCheckPr(dst, dst_x_ptr, 1);
561 darrayCheckPr(dst, dst_y_ptr, 1);
568 darrayMove(dst, src);
579 /* Shrink source matrix to half width and height and store into destinaction
582 * The destination matrix must be half in width and heit of the source matrix.
583 * The z-depth is not changed, so the destination matrix has same z-dimension.
585 * If a dimension of a source matrix is odd, the end of the dimension is just
586 * copied and not scaled.
588 * The points in a plane are shrinked from 2x2 to 1x1 by calculating the mean
589 * of all eight values.
591 * If the array has more than two dimensions, all planes in all dimensions
594 int shrink_xy(darray_t *src, darray_t *dst)
596 int x, xx, y, z, ws, wd, w_, hs, hd, h_, k, dims, i;
600 dims = darrayGetNumberOfDimensions(src);
601 ASSERT(dims >= 2, "Source array must have at least 2 dimensions");
602 ws = darrayGetDimensions(src)[0];
604 hs = darrayGetDimensions(src)[1];
607 for (i = 2; i < dims; i++)
608 k *= darrayGetDimensions(src)[i];
610 /* create dst, if not given */
612 int dimensions[dims];
614 dimensions[0] = (ws+1)>>1;
615 dimensions[1] = (hs+1)>>1;
616 for (i = 2; i < dims; i++)
617 dimensions[i] = darrayGetDimensions(src)[i];
619 dst = darrayCreate(dims, dimensions);
625 ASSERT(darrayGetNumberOfDimensions(dst) == dims, "Destination array must have equal dimensions than source array");
626 wd = darrayGetDimensions(dst)[0];
627 hd = darrayGetDimensions(dst)[1];
628 ASSERT((ws&1) || ws == wd*2, "x-size of source must be twice the size of destination (this is even source size)");
629 ASSERT(!(ws&1) || ws-1 == (wd-1)*2, "x-size of source-1 must be twice the size of destination-1 (this odd source size)");
630 ASSERT((hs&1) || hs == hd*2, "y-size of source must be twice the size of destination (this is even source size)");
631 ASSERT(!(hs&1) || hs-1 == (hd-1)*2, "y-size of source-1 must be twice the size of destination-1 (this odd source size)");
632 for (i = 2; i < dims; i++)
633 ASSERT(darrayGetDimensions(src)[i] == darrayGetDimensions(dst)[i], "dimension 3 and greater must be equal");
635 for (z = 0; z < k; z++) {
636 for (y = 0; y < h_; y++) {
638 s1 = darrayGetPr(src) + y*2*ws + z*ws*hs;
640 d = darrayGetPr(dst) + y*wd + z*wd*hd;
641 for (x = 0, xx = 0; x < w_; x++, xx+=2) {
642 darrayCheckPr(dst, d + x, 1);
643 darrayCheckPr(src, s1 + xx, 2);
644 darrayCheckPr(src, s2 + xx, 2);
645 d[x] = (s1[xx] + s1[xx+1] + s2[xx] + s2[xx+1]) * 0.25;
649 darrayCheckPr(dst, d + x, 1);
650 darrayCheckPr(src, s1 + xx, 1);
651 darrayCheckPr(src, s2 + xx, 1);
652 d[x] = (s1[xx] + s2[xx]) * 0.5;
657 s1 = darrayGetPr(src) + y*2*ws + z*ws*hs;
658 d = darrayGetPr(dst) + y*wd + z*wd*hd;
659 for (x = 0, xx = 0; x < w_; x++, xx+=2) {
660 darrayCheckPr(dst, d + x, 1);
661 darrayCheckPr(src, s1 + xx, 2);
662 d[x] = (s1[xx] + s1[xx+1]) * 0.5;
666 darrayCheckPr(dst, d + x, 1);
667 darrayCheckPr(src, s1 + xx, 1);
674 darrayMove(dst, src);
685 /* Expand X and Y using a simple linear filter
687 * The array (src) is scaled to array (dst) as shown:
690 * dst[1] = src[0] * A + src[1] * B
691 * dst[2] = src[1] * A + src[0] * B
692 * dst[3] = src[1] * A + src[2] * B
693 * dst[4] = src[2] * A + src[1] * B
694 * dst[5] = src[2] * A + src[3] * B
695 * dst[6] = src[3] * A + src[2] * B
698 * Optional if the source grid has odd dimension:
702 * First the x-dimension is scaled, then the y-dimension is scaled
708 static void expand_xy(darray_t *src, const darray_t *dst)
710 int x, y, z, ws, wd, w_, hs, hd, h_, k;
711 double *s, *s1, *s2, *d, *d1, *d2;
712 double A = sqrt(2) / 2;
715 ASSERT(darrayGetNumberOfDimensions(dst) == 3, "nvalue array must have 3 dimensions");
716 ASSERT(darrayGetNumberOfDimensions(src) == 3, "value array must have 3 dimensions");
717 ws = darrayGetDimensions(src)[0];
718 wd = darrayGetDimensions(dst)[0];
720 hs = darrayGetDimensions(src)[1];
721 hd = darrayGetDimensions(dst)[1];
723 k = darrayGetDimensions(src)[2];
724 ASSERT((wd&1) || wd == ws*2, "x-size of destination must be twice the size of source (this is even destination size)");
725 ASSERT(!(wd&1) || wd-1 == (ws-1)*2, "x-size of destination-1 must be twice the size of source-1 (this odd destination size)");
726 ASSERT((hd&1) || hd == hs*2, "y-size of destination must be twice the size of source (this is even destination size)");
727 ASSERT(!(hd&1) || hd-1 == (hs-1)*2, "y-size of destination-1 must be twice the size of source-1 (this odd destination size)");
728 ASSERT(k == darrayGetDimensions(dst)[2], "z-size of source and destination must be equal");
730 /* Expand x-dimension
731 * src is scaled in x-direction and stored in dst. In y-
732 * direction it is stored in the upper half */
733 for (z = 0; z < k; z++) {
734 for (y = 0; y < hs; y++) {
735 s = darrayGetPr(src) + y*ws + z*ws*hs;
736 d = darrayGetPr(dst) + (y+h_)*wd + z*wd*hd;
737 *d++ = *s; /* x=0 plane */
738 for (x = 0; x < w_-1; x++) {
739 darrayCheckPr(dst, d, 1);
740 darrayCheckPr(src, s, 2);
741 *d++ = A*s[0] + B*s[1];
742 *d++ = A*s[1] + B*s[0];
745 darrayCheckPr(dst, d, 1);
746 darrayCheckPr(src, s, 1);
747 *d++ = *s++; /* x=w-1 plane */
748 /* extra plane (odd fine grid) */
750 darrayCheckPr(dst, d, 1);
751 darrayCheckPr(src, s, 1);
757 /* Expand y-dimension
758 * upper half of dst is scaled in y-direction. */
759 for (z = 0; z < k; z++) {
761 s = darrayGetPr(dst) + h_*wd + z*wd*hd;
762 d = darrayGetPr(dst) + z*wd*hd;
763 for (x = 0; x < wd; x++) {
764 darrayCheckPr(dst, d, 1);
765 darrayCheckPr(dst, s, 1);
766 *d++ = *s++; /* y=0 plane */
768 for (y = 0; y < h_-1; y++) {
769 s1 = darrayGetPr(dst) + (y+h_)*wd + z*wd*hd;
771 d1 = darrayGetPr(dst) + (y*2+1)*wd + z*wd*hd;
773 for (x = 0; x < wd; x++) {
774 darrayCheckPr(dst, d1, 1);
775 darrayCheckPr(dst, d2, 1);
776 darrayCheckPr(dst, s1, 1);
777 darrayCheckPr(dst, s2, 1);
778 *d1++ = A * *s1 + B * *s2;
779 *d2++ = A * *s2 + B * *s1;
784 /* y=h-1 plane and extra plane (already there) */
788 /* Quantize the binary information (for marked arrays)
790 * If an element is greater theshold, the element is set to gt, otherwise le.
792 void quantize(const darray_t *array, double threshold, double gt, double le)
797 dims = darrayGetNumberOfDimensions(array);
798 for (i = 0; i < dims; i++)
799 n *= darrayGetDimensions(array)[i];
800 ptr = darrayGetPr(array);
802 for (i = 0; i < n; i++, ptr++) {
803 darrayCheckPr(array, ptr, 1);
804 if (*ptr > threshold)
811 /* multiply the array content with a value
813 void multiply(const darray_t *array, double factor)
818 dims = darrayGetNumberOfDimensions(array);
819 for (i = 0; i < dims; i++)
820 n *= darrayGetDimensions(array)[i];
821 ptr = darrayGetPr(array);
823 for (i = 0; i < n; i++, ptr++) {
824 darrayCheckPr(array, ptr, 1);
829 /* create an array of neighbors and use flow vectors
830 * flow_x and flow_x are the vectors that point to neighbors of the next z plane
831 * flow_x and flow_x are the vectors that point to neighbors of the previous z plane
833 unsigned char *gen_neighbor(int w, int h, int k, const darray_t *flow, const darray_t *flow_i)
835 int x, y, z, xx, yy, zz, i, j, n;
838 double *flow_x_ptr, *flow_y_ptr, *flow_ix_ptr, *flow_iy_ptr;
839 unsigned char *neighbors, *ret_neighbors;
840 unsigned long nb_size;
844 ASSERT_FLOW(w, h, k, flow, flow_i);
845 flow_x_ptr = darrayGetPr(flow);
846 flow_y_ptr = flow_x_ptr + w*h*(k-1);
847 flow_ix_ptr = darrayGetPr(flow_i);
848 flow_iy_ptr = flow_ix_ptr + w*h*(k-1);
850 nb_num = (k==1) ? NEIGHBORS_2D : NEIGHBORS_3D;
851 nb_size = (k==1) ? sizeof(struct neighbors_2d) : sizeof(struct neighbors_3d);
852 ret_neighbors = neighbors = calloc(w*h*k, nb_size);
853 // printf("alloc neighbor grid=%lu Mbytes\n", nb_size*w*h*k/1024/1024);
854 if (!ret_neighbors) {
855 printf("failed to allocate neighbor grid (%lu megabytes)\n", nb_size * w*h*k / 1024 / 1024 );
859 for (z = 0; z < k; z++) {
860 for (y = 0; y < h; y++) {
861 for (x = 0; x < w; x++) {
863 i_index = ((struct neighbors_2d *)neighbors)->index;
865 i_index = ((struct neighbors_3d *)neighbors)->index;
866 for (i = 0, n = 0; i < nb_num; i++) {
867 /* xx, yy, zz is the neighbor of color source
868 * The first neighbor is always the point itself!
870 xx = x+neigh_off[i].x;
871 yy = y+neigh_off[i].y;
872 zz = z+neigh_off[i].z;
873 if (xx < 0 || yy < 0 || zz < 0)
875 if (xx >= w || yy >= h || zz >= k)
878 darrayCheckPr(flow, flow_x_ptr + xx + yy*w + (zz-1)*w*h, 1);
879 _x = flow_x_ptr[xx + yy*w + (zz-1)*w*h];
880 darrayCheckPr(flow, flow_y_ptr + xx + yy*w + (zz-1)*w*h, 1);
881 _y = flow_y_ptr[xx + yy*w + (zz-1)*w*h];
885 darrayCheckPr(flow_i, flow_ix_ptr + xx + yy*w + zz*w*h, 1);
886 _x = flow_ix_ptr[xx + yy*w + zz*w*h];
887 darrayCheckPr(flow_i, flow_iy_ptr + xx + yy*w + zz*w*h, 1);
888 _y = flow_iy_ptr[xx + yy*w + zz*w*h];
892 if (xx < 0 || yy < 0)
894 if (xx >= w || yy >= h)
896 index = xx + yy*w + zz*w*h;
897 /* eliminate double entries */
898 for (j = 0; j < n; j++) {
899 if (i_index[j] == index)
904 i_index[n++] = index;
907 ((struct neighbors_2d *)neighbors)->num = n;
909 ((struct neighbors_3d *)neighbors)->num = n;
910 neighbors += nb_size;
915 return ret_neighbors;
918 /* weighten neighbors */
919 void weighten(const darray_t *luminance, unsigned char *neighbors)
921 int x, y, z, i, w, h, k, num;
922 double val, nval, mean, variance, sigma, sum, weight, min;
923 double sqdiff[NEIGHBORS_3D];
924 double log0_01 = log(0.01);
926 unsigned long nb_size;
927 int *i_index; double *w_index;
929 ASSERT(darrayGetNumberOfDimensions(luminance) == 3, "luminance array must have 3 dimensions");
930 w = darrayGetDimensions(luminance)[0];
931 h = darrayGetDimensions(luminance)[1];
932 k = darrayGetDimensions(luminance)[2];
933 lum_ptr = darrayGetPr(luminance);
935 nb_size = (k==1) ? sizeof(struct neighbors_2d) : sizeof(struct neighbors_3d);
937 for (z = 0; z < k; z++) {
938 for (y = 0; y < h; y++) {
939 for (x = 0; x < w; x++, neighbors += nb_size) {
941 num = ((struct neighbors_2d *)neighbors)->num;
942 i_index = ((struct neighbors_2d *)neighbors)->index;
943 w_index = ((struct neighbors_2d *)neighbors)->weight;
945 num = ((struct neighbors_3d *)neighbors)->num;
946 i_index = ((struct neighbors_3d *)neighbors)->index;
947 w_index = ((struct neighbors_3d *)neighbors)->weight;
949 /* Be sure that we have at least 3 neighbors.
950 * This applies for every corner in a plane.
951 * The first point in the neighbor list iss the point itself.
953 ASSERT(num > 3, "Every point must have at least 3 neighbors");
954 /* value of current pixle */
955 val = lum_ptr[i_index[0]];
958 for (i = 0; i < num; i++) {
959 /* value of neighbor pixles, including current pixle */
960 darrayCheckPr(luminance, lum_ptr + i_index[i], 1);
961 nval = lum_ptr[i_index[i]];
962 /* squared diffs between current pixle and each neighbor */
963 sqdiff[i] = (nval-val) * (nval-val);
965 variance += nval * nval;
973 variance -= mean * mean;
975 sigma = variance * 0.6;
977 for (i = 2; i < num; i++) {
981 if (sigma < -min / log0_01)
982 sigma = -min / log0_01;
983 if (sigma < 0.000002)
987 for (i = 0; i < num; i++) {
988 sum += exp(-sqdiff[i]/sigma);
990 /* each weight (divided by sum) */
991 for (i = 0; i < num; i++) {
992 weight = exp(-sqdiff[i]/sigma) / sum;
993 /* substract 1, if point is the point, not neighbor */
1003 /* Smooth the value array
1005 * If a pixle is marked (0 in mark array), the value at level 0 is taken from
1006 * the init array (color of marked pixle). At other level the color is not
1009 * If a pixle is not marked, the value is calculated as below.
1011 static void smooth(int level, darray_t *value, const darray_t *mark, const darray_t *init, unsigned char *neighbors)
1013 int x, y, z, w, h, k, i, num;
1014 double *value_ptr, *value_line_ptr, *mark_line_ptr, *init_line_ptr;
1016 unsigned long nb_size;
1017 int *i_index; double *w_index;
1019 ASSERT(darrayGetNumberOfDimensions(value) == 3, "value array must have 3 dimensions");
1020 ASSERT(darrayGetNumberOfDimensions(mark) == 3, "mark array must have 3 dimensions");
1021 ASSERT(darrayGetNumberOfDimensions(init) == 3, "init array must have 3 dimensions");
1022 w = darrayGetDimensions(value)[0];
1023 h = darrayGetDimensions(value)[1];
1024 k = darrayGetDimensions(value)[2];
1025 ASSERT(w == darrayGetDimensions(mark)[0], "x-sizes of mark array is different");
1026 ASSERT(h == darrayGetDimensions(mark)[1], "y-sizes of mark array is different");
1027 ASSERT(k == darrayGetDimensions(mark)[2], "z-sizes of mark array is different");
1028 value_ptr = darrayGetPr(value);
1030 nb_size = (k==1) ? sizeof(struct neighbors_2d) : sizeof(struct neighbors_3d);
1032 for (z = 0; z < k; z++) {
1033 for (y = 0; y < h; y++) {
1034 value_line_ptr = darrayGetPr(value) + y*w + z*w*h;
1035 mark_line_ptr = darrayGetPr(mark) + y*w + z*w*h;
1036 init_line_ptr = darrayGetPr(init) + y*w + z*w*h;
1037 for (x = 0; x < w; x++, neighbors += nb_size) {
1039 num = ((struct neighbors_2d *)neighbors)->num;
1040 i_index = ((struct neighbors_2d *)neighbors)->index;
1041 w_index = ((struct neighbors_2d *)neighbors)->weight;
1043 num = ((struct neighbors_3d *)neighbors)->num;
1044 i_index = ((struct neighbors_3d *)neighbors)->index;
1045 w_index = ((struct neighbors_3d *)neighbors)->weight;
1047 /* if pixle is marked */
1048 darrayCheckPr(mark, mark_line_ptr + x, 1);
1049 if (mark_line_ptr[x] < 0.5) {
1050 /* always use initial color on marked plane, otherwise do not smooth */
1052 darrayCheckPr(value, value_line_ptr + x, 1);
1053 darrayCheckPr(init, init_line_ptr + x, 1);
1054 value_line_ptr[x] = init_line_ptr[x];
1058 /* Calculate value */
1060 for (i = 1; i < num; i++) { /* sum value without point itself */
1061 darrayCheckPr(value, value_ptr + i_index[i], 1);
1062 val += w_index[i] * value_ptr[i_index[i]];
1065 darrayCheckPr(value, value_line_ptr + x, 1);
1066 darrayCheckPr(init, init_line_ptr + x, 1);
1067 value_line_ptr[x] = (init_line_ptr[x] - val) / w_index[0];
1069 darrayCheckPr(value, value_line_ptr + x, 1);
1070 value_line_ptr[x] = (-val) / w_index[0];
1078 /* Calculate residual
1080 static double residual(int level, darray_t *value, const darray_t *mark, const darray_t *init, unsigned char *neighbors)
1082 int x, y, z, w, h, k, i, num;
1083 double *value_ptr, *mark_line_ptr, *init_line_ptr;
1084 double val, residual = 0;
1085 unsigned long nb_size;
1086 int *i_index; double *w_index;
1088 ASSERT(darrayGetNumberOfDimensions(value) == 3, "value array must have 3 dimensions");
1089 ASSERT(darrayGetNumberOfDimensions(mark) == 3, "mark array must have 3 dimensions");
1090 ASSERT(darrayGetNumberOfDimensions(init) == 3, "init array must have 3 dimensions");
1091 w = darrayGetDimensions(value)[0];
1092 h = darrayGetDimensions(value)[1];
1093 k = darrayGetDimensions(value)[2];
1094 ASSERT(w == darrayGetDimensions(mark)[0], "x-sizes of mark array is different");
1095 ASSERT(h == darrayGetDimensions(mark)[1], "y-sizes of mark array is different");
1096 ASSERT(k == darrayGetDimensions(mark)[2], "z-sizes of mark array is different");
1097 value_ptr = darrayGetPr(value);
1099 nb_size = (k==1) ? sizeof(struct neighbors_2d) : sizeof(struct neighbors_3d);
1101 for (z = 0; z < k; z++) {
1102 for (y = 0; y < h; y++) {
1103 init_line_ptr = darrayGetPr(init) + y*w + z*w*h;
1104 mark_line_ptr = darrayGetPr(mark) + y*w + z*w*h;
1105 for (x = 0; x < w; x++, neighbors += nb_size) {
1107 num = ((struct neighbors_2d *)neighbors)->num;
1108 i_index = ((struct neighbors_2d *)neighbors)->index;
1109 w_index = ((struct neighbors_2d *)neighbors)->weight;
1111 num = ((struct neighbors_3d *)neighbors)->num;
1112 i_index = ((struct neighbors_3d *)neighbors)->index;
1113 w_index = ((struct neighbors_3d *)neighbors)->weight;
1115 /* if pixle is marked, no residual */
1116 darrayCheckPr(mark, mark_line_ptr + x, 1);
1117 if (mark_line_ptr[x] < 0.5)
1119 /* Calculate value */
1121 for (i = 0; i < num; i++) { /* sum value including point itself */
1122 darrayCheckPr(value, value_ptr + i_index[i], 1);
1123 val += w_index[i] * value_ptr[i_index[i]];
1126 darrayCheckPr(init, init_line_ptr + x, 1);
1127 val = init_line_ptr[x] - val;
1130 residual += val * val;
1141 * Given are arrays for value and value of next level.
1143 * If value and next value have not only different width/height, but also
1144 * different depth (z dimension), a temporary array (temp) is used to
1145 * perform the intermediate step of scaling x,y,z.
1147 * Note that the flow vectors must be scaled xy already (if x and y is shrinked
1150 static int restrict(const darray_t *value, darray_t *nvalue, const darray_t *flow)
1153 darray_t *temp = NULL;
1156 ASSERT(darrayGetNumberOfDimensions(value) == 3, "Source array must have 3 dimensions");
1157 ASSERT(darrayGetNumberOfDimensions(nvalue) == 3, "Destination array must have 3 dimensions");
1158 wd = darrayGetDimensions(nvalue)[0];
1159 hd = darrayGetDimensions(nvalue)[1];
1160 kd = darrayGetDimensions(nvalue)[2];
1161 ks = darrayGetDimensions(value)[2];
1163 ASSERT(flow, "when scaling z, flow vectors must be given");
1166 int dimensions[3] = {wd, hd, ks};
1168 /* Cube that is only shrinked in x and y */
1169 temp = darrayCreate(3, dimensions);
1173 rc = shrink_xy((darray_t *)value, temp);
1176 rc = shrink_z(temp, nvalue, flow);
1180 rc = shrink_xy((darray_t *)value, nvalue);
1188 darrayDestroy(temp);
1195 * Given are arrays for value and value of next level.
1197 * If value and next value have not only different width/height, but also
1198 * different depth (z dimension), a temporary array (temp3) is used to
1199 * perform the intermediate step of scaling x,y,z.
1201 * Note that the flow vectors must be scaled xy already (if x and y is shrinked
1204 * First the value is shrinked to the size of the value of the next level.
1205 * The error is calculated by substracting the values of the next level
1206 * from the value. The error is then expanded. The expanded error is
1207 * substracted from the value. So the changes in the next level's value are
1208 * prolonged to the value.
1210 static int prolong(darray_t *value, const darray_t *nvalue, const darray_t *flow)
1212 int ws, hs, ks, wd, hd, kd;
1214 darray_t *temp1 = NULL, *temp2 = NULL, *temp3 = NULL;
1215 double *value_ptr, *nvalue_ptr, *temp1_ptr, *temp2_ptr;
1218 ASSERT(darrayGetNumberOfDimensions(value) == 3, "nvalue array must have 3 dimensions");
1219 ASSERT(darrayGetNumberOfDimensions(nvalue) == 3, "value array must have 3 dimensions");
1220 ws = darrayGetDimensions(nvalue)[0];
1221 hs = darrayGetDimensions(nvalue)[1];
1222 ks = darrayGetDimensions(nvalue)[2];
1223 wd = darrayGetDimensions(value)[0];
1224 hd = darrayGetDimensions(value)[1];
1225 kd = darrayGetDimensions(value)[2];
1227 ASSERT(flow, "when scaling z, flow vectors must be given");
1229 /* generate temporary arrays */
1230 temp1 = darrayCreate(3, darrayGetDimensions(value));
1233 temp2 = darrayCreate(3, darrayGetDimensions(nvalue));
1237 int dimensions[3] = {ws, hs, kd};
1239 /* Cube that is only shrinked in x and y */
1240 temp3 = darrayCreate(3, dimensions);
1245 /* temp2 = shrink(value) */
1247 rc = shrink_xy(value, temp3);
1250 rc = shrink_z(temp3, temp2, flow);
1254 rc = shrink_xy(value, temp2);
1259 /* temp2 -= nvalue */
1260 temp2_ptr = darrayGetPr(temp2);
1261 nvalue_ptr = darrayGetPr(nvalue);
1263 for (i = 0; i < pixles; i++) {
1264 darrayCheckPr(temp2, temp2_ptr, 1);
1265 darrayCheckPr(nvalue, nvalue_ptr, 1);
1266 *temp2_ptr++ -= *nvalue_ptr++;
1269 /* Expand (temp2 -> temp1) */
1271 rc = expand_z(temp2, temp3, flow);
1274 expand_xy(temp3, temp1);
1276 expand_xy(temp2, temp1);
1278 /* value -= temp1 */
1279 temp1_ptr = darrayGetPr(temp1);
1280 value_ptr = darrayGetPr(value);
1282 for (i = 0; i < pixles; i++) {
1283 darrayCheckPr(temp1, temp1_ptr, 1);
1284 darrayCheckPr(value, value_ptr, 1);
1285 *value_ptr++ -= *temp1_ptr++;
1291 /* destroy arrays */
1292 darrayDestroy(temp1);
1293 darrayDestroy(temp2);
1294 darrayDestroy(temp3);
1299 /* Paste one array into anothter */
1300 void paste(const darray_t *src, darray_t *dst, int xoff, int yoff, int zoff)
1302 int ws, hs, ks, wd, hd, /*kd,*/ x, y, z;
1303 double *src_ptr, *dst_ptr;
1305 ws = darrayGetDimensions(src)[0];
1306 hs = darrayGetDimensions(src)[1];
1307 ks = darrayGetDimensions(src)[2];
1308 wd = darrayGetDimensions(dst)[0];
1309 hd = darrayGetDimensions(dst)[1];
1310 // kd = darrayGetDimensions(dst)[2];
1311 src_ptr = darrayGetPr(src);
1312 dst_ptr = darrayGetPr(dst);
1314 for (z = 0; z < ks; z++) {
1315 for (y = 0; y < hs; y++) {
1316 for (x = 0; x < ws; x++) {
1317 darrayCheckPr(dst, dst_ptr + (x+xoff) + (y+yoff)*wd + (z+zoff)*wd*hd, 1);
1318 darrayCheckPr(src, src_ptr + x + y*ws + z*ws*hs, 1);
1319 dst_ptr[(x+xoff) + (y+yoff)*wd + (z+zoff)*wd*hd] = src_ptr[x + y*ws + z*ws*hs];
1325 inline void draw_line(double *ptr, int w, int h, int x0, int y0, int x1, int y1, double color)
1327 int dx, dy, sx, sy, err, e2;
1341 if (x0 > 0 && x0 < w && y0 > 0 && y0 < h)
1342 ptr[x0 + y0*w] = color;
1343 if (x0 == x1 && y0 == y1)
1357 /* Draw motion flow vectors into dst
1359 * The forward vectors are drawn white, the inverse vectors black.
1361 * The flow vectors are drawn to the upper left corner of the dst, which
1362 * may be larger in size than the flow arrays.
1364 void draw_flow(darray_t *dst, const darray_t *flow, const darray_t *flow_i)
1366 int wd, hd, ws, hs, ks, x, y, z;
1367 double *flow_x_ptr, *flow_y_ptr, *flow_ix_ptr, *flow_iy_ptr;
1370 wd = darrayGetDimensions(dst)[0];
1371 hd = darrayGetDimensions(dst)[1];
1372 ws = darrayGetDimensions(flow)[0];
1373 hs = darrayGetDimensions(flow)[1];
1374 ks = darrayGetDimensions(flow)[2];
1375 flow_x_ptr = darrayGetPr(flow);
1376 flow_y_ptr = flow_x_ptr + ws*hs*ks;
1377 flow_ix_ptr = darrayGetPr(flow_i);
1378 flow_iy_ptr = flow_ix_ptr + ws*hs*ks;
1380 for (z = 0; z <= ks; z++) {
1381 for (y = step/2; y < hs-step/2+1; y+=step) {
1382 for (x = step/2; x < ws-step/2+1; x+=step) {
1385 draw_line(darrayGetPr(dst) + z*wd*hd, wd, hd, x, y, x+round(flow_x_ptr[x + y*ws + z*ws*hs]), y+(int)(flow_y_ptr[x + y*ws + z*ws*hs]+0.5), 1.0);
1388 draw_line(darrayGetPr(dst) + z*wd*hd, wd, hd, x, y, x+round(flow_ix_ptr[x + y*ws + (z-1)*ws*hs]), y+(int)(flow_iy_ptr[x + y*ws + (z-1)*ws*hs]+0.5), 0.0);
1396 * Note that the flow vectors must be scaled xy already (if x and y is shrinked
1400 int solve_mg(int nlevel, int iters, int ncycle, darray_t **values, darray_t **marks, const darray_t *init, unsigned char **nb_list, darray_t **flows, int scalexyz)
1403 double curr_residual, prev_residual = 10000000000000.0;
1405 int cycle, level, i;
1408 for (cycle = 0; cycle < ncycle; cycle++) {
1413 for (level = 0; level < nlevel-1; level++) {
1414 for (i = 0; i < iters; i++)
1415 smooth(level, values[level], marks[level], init, nb_list[level]);
1417 curr_residual += residual(level, values[level], marks[level], init, nb_list[level]);
1419 if (level < scalexyz)
1420 rc = restrict(values[level], values[level+1], flows[level]);
1422 rc = restrict(values[level], values[level+1], NULL);
1427 /* end of going donw */
1428 for (i = 0; i < iters*5; i++)
1429 smooth(nlevel-1, values[nlevel-1], marks[nlevel-1], init, nb_list[nlevel-1]);
1432 for (level = nlevel-2; level >= 0; level--) {
1433 if (level < scalexyz)
1434 rc = prolong(values[level], values[level+1], flows[level]);
1436 rc = prolong(values[level], values[level+1], NULL);
1439 for (i = 0; i < iters; i++)
1440 smooth(level, values[level], marks[level], init, nb_list[level]);
1443 /* end if residual raises again */
1445 if (prev_residual < curr_residual)
1447 curr_residual = prev_residual;