swh:1:snp:9dcf3ab72851691ef27e40da9b2a50243c1bdd22
Tip revision: 5f23fb0b6e1d50d996ac54daaa7e637e5d8decaf authored by Software Heritage on 05 May 2020, 00:00:00 UTC
ipol: Deposit 1363 in collection ipol
ipol: Deposit 1363 in collection ipol
Tip revision: 5f23fb0
ccproc.c
/*
* ccproc.c
*
* Copyright (C) 2019, Enric Meinhardt-Llopis, CMLA, ÉNS Paris-Saclay.
* Adapted by Tristan Dagobert to work with
* the cloud detector.
*
* 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 <https://www.gnu.org/licenses/>.
*/
// This file contains the implemantation of the "ccproc" function
// It is a general tool to deal with connected components in images.
// Also, it contains the implementation of the "bfollow" function, to track
// the boundaries of these connected components in anticlockwise order.
#include <math.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <float.h>
#include <assert.h>
#include "abstract_dsf.h"
// equivalence relations of floats
//
// A function of this signature is the most important imput parameter. It is
// used to decide whether neighboring pixels belong to the same connected
// component or not.
typedef int (*float_equivalence_relation_t)(float,float);
// three example equivalence relations are provided:
// wheter the two floats are equal
int float_equality(float a, float b)
{
return a == b;
}
// whether two floats are equal, or are both null
int floatnan_equality(float a, float b)
{
if (isnan(a) && isnan(b)) return true;
return a == b;
}
// whether they are both nan or both non-nan
int float_eq_isnan(float a, float b)
{
return isnan(a) == isnan(b);
}
// function to sort int vectors with increasing first component
static int compare_pair_increasing(const void *aa, const void *bb)
{
const int *a = (const int *)aa;
const int *b = (const int *)bb;
return (*a > *b) - (*a < *b);
}
// function to sort int vectors with decreasing first component
static int compare_pair_decreasing(const void *aa, const void *bb)
{
return -compare_pair_increasing(aa, bb);
}
// given an image of indexes, whether a given pixel is at a boundary
// of a region of pixels of the same index
static bool idx_boundaryingP(int *idxs, int w, int h, int idx)
{
int i = idx % w;
int j = idx / w;
assert(j*w+i == idx);
int my_idx = idxs[idx];
if (i-1 >= 0 && my_idx != idxs[(j+0)*w + i-1]) return true;
if (i+1 < w && my_idx != idxs[(j+0)*w + i+1]) return true;
if (j-1 >= 0 && my_idx != idxs[(j-1)*w + i+0]) return true;
if (j+1 < h && my_idx != idxs[(j+1)*w + i+0]) return true;
return false;
}
static void swapi(int *t, int i, int j)
{
int tmp = t[i];
t[i] = t[j];
t[j] = tmp;
}
// whether a point is inside the image domain
static bool insideP(int w, int h, int i, int j)
{
return i >= 0 && j >= 0 && i < w && j < h;
}
// directions
// 0: ( 1, 0)
// 1: ( 0,-1)
// 2: (-1, 0)
// 3: ( 0, 1)
// get the neighbor of pixel (i,j) in the direction dir
static void get_neighbour(int *ni, int *nj, int i, int j, int dir)
{
*ni = i;
*nj = j;
switch (dir)
{
case 0:
*ni += 1;
break;
case 1:
*nj -= 1;
break;
case 2:
*ni -= 1;
break;
case 3:
*nj += 1;
break;
}
}
// whether an edge (i,j,dir) is at the boundary of an eq-region
static bool pix_boundaryingP(float *x, int w, int h,
float_equivalence_relation_t eq, int i, int j, int dir)
{
assert(insideP(w, h, i, j));
int ni, nj;
get_neighbour(&ni, &nj, i, j, dir);
if (!insideP(w, h, ni, nj))
return true;
float vij = x[ j*w+ i];
float vnn = x[nj*w+ni];
return !eq(vij, vnn);
}
// follow the closed boundary of a connected component containing point (i,j)
// note: the output array must be pre-allocated
// out_bd is a list of triplets (i,j,dir) conforming the boundary
int bfollow(int *out_bd, float *x, int w, int h,
float_equivalence_relation_t eq,
int i, int j)
{
assert(i >= 0 && j >= 0 && i < w && j < h);
// find a pixel that is outside, it will be used to begin the loop
int ifirst = i;
int jfirst = j;
int dfirst = 1;
while (!pix_boundaryingP(x, w, h, eq, ifirst, jfirst, dfirst))
ifirst -= 1;
// loop until the first boundary element is found again
int out_n = 0;
int dir = dfirst;
i = ifirst;
j = jfirst;
do
{
assert(pix_boundaryingP(x, w, h, eq, i, j, dir));
out_bd[3*out_n + 0] = i;
out_bd[3*out_n + 1] = j;
out_bd[3*out_n + 2] = dir;
out_n += 1;
switch(dir) // counterclockwise track (could be a table also)
{
case 0:
if (pix_boundaryingP(x, w, h, eq, i, j, 1))
dir = 1;
else if (pix_boundaryingP(x, w, h, eq, i, j-1, 0))
j -= 1;
else if (pix_boundaryingP(x, w, h, eq, i+1, j-1, 3))
{
i += 1;
j -= 1;
dir = 3;
}
else exit(EXIT_FAILURE);
break;
case 1:
if (pix_boundaryingP(x, w, h, eq, i, j, 2))
dir = 2;
else if (pix_boundaryingP(x, w, h, eq, i-1, j, 1))
i -= 1;
else if (pix_boundaryingP(x, w, h, eq, i-1, j-1, 0))
{
i -= 1;
j -= 1;
dir = 0;
}
else exit(EXIT_FAILURE);
break;
case 2:
if (pix_boundaryingP(x, w, h, eq, i, j, 3))
dir = 3;
else if (pix_boundaryingP(x, w, h, eq, i, j+1, 2))
j += 1;
else if (pix_boundaryingP(x, w, h, eq, i-1, j+1, 1))
{
i -= 1;
j += 1;
dir = 1;
}
else exit(EXIT_FAILURE);
break;
case 3:
if (pix_boundaryingP(x, w, h, eq, i, j, 0))
dir = 0;
else if (pix_boundaryingP(x, w, h, eq, i+1, j, 3))
i += 1;
else if (pix_boundaryingP(x, w, h, eq, i+1, j+1, 2))
{
i += 1;
j += 1;
dir = 2;
}
else exit(EXIT_FAILURE);
break;
}
}
while (i != ifirst || j != jfirst || dir != dfirst);
return out_n;
}
static int compute_representatives(int *rep, float *x, int w, int h,
float_equivalence_relation_t eq)
{
// rep[i] = i
adsf_begin(rep, w*h);
// join equivalent neighbors (neighbors == 4-neighbors ALWAYS)
for (int j = 0; j < h; j++)
for (int i = 0; i < w; i++)
{
int p0 = j*w + i;
int p1 = j*w + i+1;
int p2 = (j+1)*w + i;
if (i+1 < w && eq(x[p0], x[p1])) adsf_union(rep, w*h, p0, p1);
if (j+1 < h && eq(x[p0], x[p2])) adsf_union(rep, w*h, p0, p2);
}
// canonicalize dsf (after this, the DSF is not changed anymore)
for (int i = 0; i < w*h; i++)
rep[i] = adsf_find(rep, w*h, i);
// count connected components
int r = 0;
for (int i = 0; i < w*h; i++)
if (rep[i] == i)
r += 1;
return r;
}
static
void compute_pos_and_idx(int *pos, int *idx, int *rep, int w, int h, int r)
{
for (int i = 0; i < r; i++)
pos[2*i + 0] = 0; // initialize areas of each region
int rcx = 0;
for (int i = 0; i < w*h; i++)
if (rep[i] == i)
{
idx[i] = rcx;
pos[2*rcx + 1] = i; // representative
rcx += 1;
}
assert(rcx == r);
for (int i = 0; i < w*h; i++)
{
int j = rep[i];
int t = idx[j];
pos[2*t + 0] += 1;
}
qsort(pos, r, 2*sizeof*pos, compare_pair_decreasing);
for (int i = 0; i < r; i++)
{
int ir = pos[2*i + 1];
idx[ir] = i;
assert(ir == rep[ir]);
}
}
static void reverse_indexes(int *out_all, int *idx, int *rep, int n)
{
int *ipair = malloc(2 * n * sizeof*ipair);
// reverse indices
for (int i = 0; i < n; i++)
{
ipair[2*i + 0] = idx[rep[i]];
ipair[2*i + 1] = i;
}
qsort(ipair, n, 2*sizeof*ipair, compare_pair_increasing);
// fill-in table of all indices
for (int i = 0; i < n; i++)
out_all[i] = ipair[2*i+1];
free(ipair);
}
static void reorder_boundaries(int *bdsize,
int *idx, int *all, int *first, int *size, int w, int h, int r)
{
for (int i = 0; i < r; i++)
{
int *t = all + first[i];
int a = 0;
int b = size[i];
// vector: t[0] t[1] ... t[b-1]
while (a < b)
if (idx_boundaryingP(idx, w, h, t[a]))
a = a + 1;
else
swapi(t, a, --b);
bdsize[i] = a;
}
}
static
void assert_consistency(int *idx, int *all, int *size, int *first, int r)
{
for (int i = 0; i < r; i++)
for (int j = 0; j < size[i]; j++)
assert(idx[all[first[i] + j]] == i);
}
// core function
int ccproc(
int *out_size, // total size of each CC
int *out_bdsize, // boundary size of each CC
int *out_all, // array of all the indexes, connected
int *out_first, // array of first indexes of each CC
int *out_idx, // image with the indices of each region
float *x, int w, int h, // input image
float_equivalence_relation_t eq
)
{
// pre-setup
if (!eq)
eq = floatnan_equality;
// create table of representatives
int *rep = malloc(w * h * sizeof*rep);
int r = compute_representatives(rep, x, w, h, eq);
// set the index/position correspondence
int *pair = malloc(2 * r * sizeof*pair);
int *tmpi = malloc(w*h*sizeof*tmpi);
compute_pos_and_idx(pair, tmpi, rep, w, h, r);
// reverse indexes and fill table with all
reverse_indexes(out_all, tmpi, rep, w*h);
// fill-in table of sizes
for (int i = 0; i < r; i++)
out_size[i] = pair[2*i + 0];
// fill-in image of representatives
for (int i = 0; i < w*h; i++)
out_idx[i] = tmpi[rep[i]];
// fill-in table of first indexes
out_first[0] = 0;
int cx = 1;
for (int i = 1; i < w*h; i++)
if (rep[out_all[i-1]] != rep[out_all[i]])
out_first[cx++] = i;
assert(cx == r);
// verify consistency
assert_consistency(out_idx, out_all, out_size, out_first, r);
// reorder out_all so that boundarying points come first
reorder_boundaries(out_bdsize, out_idx, out_all, out_first, out_size,
w, h, r);
// cleanup and exit
free(pair);
free(rep);
free(tmpi);
return r;
}