432 lines
11 KiB
C

/*
* binmorph.c - functions for morphological operations on binary image
*
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
/*
TEST (10000 dilations / erosions / substitutions) for i5-3210M CPU @ 2.50GHz x2cores x2hyper
1. no openmp:
* 28.8 / 29.2 / 14.8
2. 2 threads:
* 17.0 / 17.9 / 8.8
3. 4 threads:
* 17.0 / 17.5 / 8.8
4. 8 threads:
* 17.0 / 17.7 / 8.9
*
option -O3 gives for nthreads = 4:
* 6.9 / 6.9 / 2.9
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <err.h>
#include <sys/time.h>
#include "binmorph.h"
//#include "fifo.h"
#ifdef OMP
#ifndef OMP_NUM_THREADS
#define OMP_NUM_THREADS 4
#endif
#define Stringify(x) #x
#define OMP_FOR(x) _Pragma(Stringify(omp parallel for x))
#else
#define OMP_FOR(x)
#endif
// global arrays for erosion/dilation masks
unsigned char *ER = NULL, *DIL = NULL;
bool __Init_done = false; // == true if arrays are inited
/*
* =================== AUXILIARY FUNCTIONS ===================>
*/
/**
* function for different purposes that need to know time intervals
* @return double value: time in seconds
*/
double dtime(){
double t;
struct timeval tv;
gettimeofday(&tv, NULL);
t = tv.tv_sec + ((double)tv.tv_usec)/1e6;
return t;
}
/**
* Memory allocation with control
* @param N - number of elements
* @param S - size of one element
* @return allocated memory or die
*/
void *my_alloc(size_t N, size_t S){
void *p = calloc(N, S);
if(!p) err(1, "malloc");
return p;
}
/**
* This function inits masks arrays for erosion and dilation
* You may call it yourself or it will be called when one of
* `erosion` or `dilation` functions will be ran first time
*/
void morph_init(){
if(__Init_done) return;
int i;//,j;
//bool a[8], b[8], c[9];
ER = Malloc(256, 1);
DIL = Malloc(256, 1);
for(i = 0; i < 256; i++){
/*ER[i] = DIL[i] = 0;
for(j = 0; j < 8; j++){
a[j] = (i >> j) & 1;
b[j] = a[j];
c[j] = a[j];
}
for(j = 1; j < 7; j++)
if(!a[j])
b[j+1] = b[j-1]= false;
else
c[j] = c[j-1] = c[j+1] = true;
b[1] &= a[0]; b[6] &= a[7];
c[1] |= a[0]; c[6] |= a[7];
for(j = 0; j < 8; j++){
ER[i] |= b[j] << j;
DIL[i] |= c[j] << j;
}
*/
ER[i] = i & ((i << 1) | 1) & ((i >> 1) | (0x80)); // don't forget that << and >> set borders to zero
DIL[i] = i | (i << 1) | (i >> 1);
}
__Init_done = true;
}
/**
* Print out small "packed" images as matrix of "0" and "1"
* @param i (i) - image
* @param W, H - size of image
*/
void printC(unsigned char *i, int W, int H){
int x,y,b;
for(y = 0; y < H; y++){
for(x = 0; x < W; x++, i++){
unsigned char c = *i;
for(b = 0; b < 8; b++)
printf("%c", c << b & 0x80 ? '1' : '0');
}
printf("\n");
}
}
/**
* Print out small "unpacked" images as almost equiaxed matrix of ".." and "##"
* @param img (i) - image
* @param W, H - size of image
*/
void printB(bool *img, int W, int H){
int i, j;
for(j = 0; j < W; j++)
if(j % 10 == 1){ printf("%02d", j-1); printf(" ");}
printf("\n");
for(i = 0; i < H; i++){
printf("%2d ",i);
for(j = 0; j < W; j++)
printf("%s", (*img++) ? "##" : "..");
printf("\n");
}
}
/*
* <=================== AUXILIARY FUNCTIONS ===================
*/
/*
* =================== CONVERT IMAGE TYPES ===================>
*/
/**
* Convert boolean image into pseudo-packed (1 char == 8 pixels)
* @param im (i) - image to convert
* @param W, H - size of image im (must be larger than 1)
* @param W_0 (o) - (stride) new width of image
* @return allocated memory area with "packed" image
*/
unsigned char *bool2char(bool *im, int W, int H, int *W_0){
if(W < 2 || H < 2) errx(1, "bool2char: image size too small");
int y, W0 = (W + 7) / 8;
unsigned char *ret = Malloc(W0 * H, 1);
OMP_FOR()
for(y = 0; y < H; y++){
int x, i, X;
bool *ptr = &im[y*W];
unsigned char *rptr = &ret[y*W0];
for(x = 0, X = 0; x < W0; x++, rptr++){
for(i = 7; i > -1 && X < W; i--, X++, ptr++){
*rptr |= *ptr << i;
}
}
}
if(W_0) *W_0 = W0;
return ret;
}
/**
* Convert "packed" image into boolean
* @param image (i) - input image
* @param W, H, W_0 - size of image and width of "packed" image
* @return allocated memory area with "unpacked" image
*/
bool *char2bool(unsigned char *image, int W, int H, int W_0){
int y;
bool *ret = Malloc(W * H, sizeof(bool));
OMP_FOR()
for(y = 0; y < H; y++){
int x, X, i;
bool *optr = &ret[y*W];
unsigned char *iptr = &image[y*W_0];
for(x = 0, X = 0; x < W_0; x++, iptr++)
for(i = 7; i > -1 && X < W; i--, X++, optr++){
*optr = (*iptr >> i) & 1;
}
}
return ret;
}
/**
* Convert "packed" image into size_t array for conncomp procedure
* @param image (i) - input image
* @param W, H, W_0 - size of image and width of "packed" image
* @return allocated memory area with copy of an image
*/
size_t *char2st(unsigned char *image, int W, int H, int W_0){
int y;
size_t *ret = Malloc(W * H, sizeof(size_t));
OMP_FOR()
for(y = 0; y < H; y++){
int x, X, i;
size_t *optr = &ret[y*W];
unsigned char *iptr = &image[y*W_0];
for(x = 0, X = 0; x < W_0; x++, iptr++)
for(i = 7; i > -1 && X < W; i--, X++, optr++){
*optr = (*iptr >> i) & 1;
}
}
return ret;
}
/*
* <=================== CONVERT IMAGE TYPES ===================
*/
/*
* =================== MORPHOLOGICAL OPERATIONS ===================>
*/
/**
* Remove all non-4-connected pixels
* @param image (i) - input image
* @param W, H - size of image
* @return allocated memory area with converted input image
*/
unsigned char *FC_filter(unsigned char *image, int W, int H){
if(W < 1 || H < 2) errx(1, "4-connect: image size too small");
unsigned char *ret = Malloc(W*H, 1);
int y = 0, w = W-1, h = H-1;
// top of image, y = 0
#define IM_UP
#include "fc_filter.h"
#undef IM_UP
// mid of image, y = 1..h-1
#include "fc_filter.h"
// image bottom, y = h
y = h;
#define IM_DOWN
#include "fc_filter.h"
#undef IM_DOWN
return ret;
}
/**
* Make morphological operation of dilation
* @param image (i) - input image
* @param W, H - size of image
* @return allocated memory area with dilation of input image
*/
unsigned char *dilation(unsigned char *image, int W, int H){
if(W < 2 || H < 2) errx(1, "Dilation: image size too small");
if(!__Init_done) morph_init();
unsigned char *ret = Malloc(W*H, 1);
int y = 0, w = W-1, h = H-1;
// top of image, y = 0
#define IM_UP
#include "dilation.h"
#undef IM_UP
// mid of image, y = 1..h-1
#include "dilation.h"
// image bottom, y = h
y = h;
#define IM_DOWN
#include "dilation.h"
#undef IM_DOWN
return ret;
}
/**
* Make morphological operation of erosion
* @param image (i) - input image
* @param W, H - size of image
* @return allocated memory area with erosion of input image
*/
unsigned char *erosion(unsigned char *image, int W, int H){
if(W < 2 || H < 2) errx(1, "Erosion: image size too small");
if(!__Init_done) morph_init();
unsigned char *ret = Malloc(W*H, 1);
int y, w = W-1, h = H-1;
OMP_FOR()
for(y = 1; y < h; y++){ // reset first & last rows of image
unsigned char *iptr = &image[W*y];
unsigned char *optr = &ret[W*y];
unsigned char p = ER[*iptr] & 0x7f & iptr[-W] & iptr[W];
int x;
if(!(iptr[1] & 0x80)) p &= 0xfe;
*optr++ = p;
iptr++;
for(x = 1; x < w; x++, iptr++, optr++){
p = ER[*iptr] & iptr[-W] & iptr[W];
if(!(iptr[-1] & 1)) p &= 0x7f;
if(!(iptr[1] & 0x80)) p &= 0xfe;
*optr = p;
}
p = ER[*iptr] & 0xfe & iptr[-W] & iptr[W];
if(!(iptr[-1] & 1)) p &= 0x7f;
*optr++ = p;
iptr++;
}
return ret;
}
/*
* <=================== MORPHOLOGICAL OPERATIONS ===================
*/
/*
* =================== LOGICAL OPERATIONS ===================>
*/
/**
* Logical AND of two images
* @param im1, im2 (i) - two images
* @param W, H - their size (of course, equal for both images)
* @return allocated memory area with image = (im1 AND im2)
*/
unsigned char *imand(unsigned char *im1, unsigned char *im2, int W, int H){
unsigned char *ret = Malloc(W*H, 1);
int y;
OMP_FOR()
for(y = 0; y < H; y++){
int x, S = y*W;
unsigned char *rptr = &ret[S], *p1 = &im1[S], *p2 = &im2[S];
for(x = 0; x < W; x++)
*rptr++ = *p1++ & *p2++;
}
return ret;
}
/**
* Substitute image 2 from image 1: reset to zero all bits of image 1 which set to 1 on image 2
* @param im1, im2 (i) - two images
* @param W, H - their size (of course, equal for both images)
* @return allocated memory area with image = (im1 AND (!im2))
*/
unsigned char *substim(unsigned char *im1, unsigned char *im2, int W, int H){
unsigned char *ret = Malloc(W*H, 1);
int y;
OMP_FOR()
for(y = 0; y < H; y++){
int x, S = y*W;
unsigned char *rptr = &ret[S], *p1 = &im1[S], *p2 = &im2[S];
for(x = 0; x < W; x++)
*rptr++ = *p1++ & (~*p2++);
}
return ret;
}
/*
* <=================== LOGICAL OPERATIONS ===================
*/
/*
* =================== CONNECTED COMPONENTS LABELING ===================>
*/
/**
* label 4-connected components on image
* (slow algorythm, but easy to parallel)
*
* @param I (i) - image ("packed")
* @param W,H,W_0 - size of the image (W - width in pixels, W_0 - width in octets)
* @param Nobj (o) - number of objects found
* @return an array of labeled components
*/
CCbox *cclabel4(unsigned char *Img, int W, int H, int W_0, size_t *Nobj){
unsigned char *I = FC_filter(Img, W_0, H);
#include "cclabling.h"
FREE(I);
return ret;
}
CCbox *cclabel4_1(unsigned char *Img, int W, int H, int W_0, size_t *Nobj){
unsigned char *I = FC_filter(Img, W_0, H);
#include "cclabling_1.h"
FREE(I);
return ret;
}
// label 8-connected components, look cclabel4
CCbox *cclabel8(unsigned char *I, int W, int H, int W_0, size_t *Nobj){
//unsigned char *I = EC_filter(Img, W_0, H);
#define LABEL_8
#include "cclabling.h"
#undef LABEL_8
// FREE(I);
return ret;
}
CCbox *cclabel8_1(unsigned char *I, int W, int H, int W_0, size_t *Nobj){
#define LABEL_8
#include "cclabling_1.h"
#undef LABEL_8
return ret;
}
/*
* <=================== CONNECTED COMPONENTS LABELING ===================
*/
/*
* <=================== template ===================>
*/