begin process at 2012 02 08 23:21:08
  Trouver un code source :
 
dans
 
Accueil > 

Code

 > 

Graphique

 > TRAITEMENT DE L'IMAGE: FILTRE MÉDIAN EN TEMPS CONSTANT

TRAITEMENT DE L'IMAGE: FILTRE MÉDIAN EN TEMPS CONSTANT




 Description

Cliquez pour voir la capture en taille normale
Le filtre médian est un filtre, spatial et non linéaire, qui calcule en chaque pixel la médiane des niveaux de gris des pixels de sa fenêtre, ce qui donnera le niveau de gris du pixel dans l'image filtrée.
Imaginons une fenêtre de taille r, pour trouver la médiane, il faut trier les pixels et retenir le pixel médian. Si on programme cette fonction d'une manière naïve, on devra pour chaque pixel trier les r pixels voisins.  La complexité de cet algorithme explose lorsque r augmente trop.


Cet algorithme propose une méthode pour calculer le filtre médian avec un temps constant pour tout r ! Quelque soit la taille de la fenêtre, la complexité reste la même!

Cet algorithme n'est pas de moi mais il est tellement performant qu'il devait être sur Codes Sources. Voici le lien du site internet des créateurs:
http://nomis80.org/ctmf.html
Vous trouverez aussi sur ce site internet, le très intéressant document technique de cet algorithme.


Si vous n'êtes pas convaincu, j'ai créé une petite interface graphique qui permet d'appliquer le filtre médian sur des images. Vous pouvez ainsi modifier r (presque) en temps réel.
Le traitement des images couleurs est simple mais pourrait être amélioré: je lance 3 fois l'algorithme suivant les composantes R,G et B. On peut donc choisir de filtrer en couleur ou en niveau de gris. On peut aussi voir le temps de l'exécution de la fonction en bas à droite de la fenêtre.
Pour sélectionner une autre image, il suffit de faire un clic droit sur l'image.


J'insiste, allez visiter leur site internet.

Source

  • /*
  • * ctmf.c - Constant-time median filtering
  • * Copyright (C) 2006 Simon Perreault
  • *
  • * Reference: S. Perreault and P. Hébert, "Median Filtering in Constant Time",
  • * IEEE Transactions on Image Processing, September 2007.
  • *
  • * This program has been obtained from http://nomis80.org/ctmf.html. No patent
  • * covers this program, although it is subject to the following license:
  • *
  • * 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 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 General Public License for more details.
  • *
  • * You should have received a copy of the GNU General Public License
  • * along with this program. If not, see <http://www.gnu.org/licenses/>.
  • *
  • * Contact:
  • * Laboratoire de vision et systèmes numériques
  • * Pavillon Adrien-Pouliot
  • * Université Laval
  • * Sainte-Foy, Québec, Canada
  • * G1K 7P4
  • *
  • * perreaul@gel.ulaval.ca
  • */
  • /* Standard C includes */
  • #include <assert.h>
  • #include <math.h>
  • #include <stdlib.h>
  • #include <string.h>
  • /* Type declarations */
  • #ifdef _MSC_VER
  • #include <basetsd.h>
  • typedef UINT8 uint8_t;
  • typedef UINT16 uint16_t;
  • typedef UINT32 uint32_t;
  • #pragma warning( disable: 4799 )
  • #else
  • #include <stdint.h>
  • #endif
  • /* Intrinsic declarations */
  • #if defined(__SSE2__) || defined(__MMX__)
  • #if defined(__SSE2__)
  • #include <emmintrin.h>
  • #elif defined(__MMX__)
  • #include <mmintrin.h>
  • #endif
  • #if defined(__GNUC__)
  • #include <mm_malloc.h>
  • #elif defined(_MSC_VER)
  • #include <malloc.h>
  • #endif
  • #elif defined(__ALTIVEC__)
  • #include <altivec.h>
  • #endif
  • /* Compiler peculiarities */
  • #if defined(__GNUC__)
  • #include <stdint.h>
  • #define inline __inline__
  • #define align(x) __attribute__ ((aligned (x)))
  • #elif defined(_MSC_VER)
  • #define inline __inline
  • #define align(x) __declspec(align(x))
  • #else
  • #define inline
  • #define align(x)
  • #endif
  • #ifndef MIN
  • #define MIN(a,b) ((a) > (b) ? (b) : (a))
  • #endif
  • #ifndef MAX
  • #define MAX(a,b) ((a) < (b) ? (b) : (a))
  • #endif
  • /**
  • * This structure represents a two-tier histogram. The first tier (known as the
  • * "coarse" level) is 4 bit wide and the second tier (known as the "fine" level)
  • * is 8 bit wide. Pixels inserted in the fine level also get inserted into the
  • * coarse bucket designated by the 4 MSBs of the fine bucket value.
  • *
  • * The structure is aligned on 16 bytes, which is a prerequisite for SIMD
  • * instructions. Each bucket is 16 bit wide, which means that extra care must be
  • * taken to prevent overflow.
  • */
  • typedef struct align(16)
  • {
  • uint16_t coarse[16];
  • uint16_t fine[16][16];
  • } Histogram;
  • /**
  • * HOP is short for Histogram OPeration. This macro makes an operation \a op on
  • * histogram \a h for pixel value \a x. It takes care of handling both levels.
  • */
  • #define HOP(h,x,op) \
  • h.coarse[x>>4] op; \
  • *((uint16_t*) h.fine + x) op;
  • #define COP(c,j,x,op) \
  • h_coarse[ 16*(n*c+j) + (x>>4) ] op; \
  • h_fine[ 16 * (n*(16*c+(x>>4)) + j) + (x & 0xF) ] op;
  • /**
  • * Adds histograms \a x and \a y and stores the result in \a y. Makes use of
  • * SSE2, MMX or Altivec, if available.
  • */
  • #if defined(__SSE2__)
  • static inline void histogram_add( const uint16_t x[16], uint16_t y[16] )
  • {
  • *(__m128i*) &y[0] = _mm_add_epi16( *(__m128i*) &y[0], *(__m128i*) &x[0] );
  • *(__m128i*) &y[8] = _mm_add_epi16( *(__m128i*) &y[8], *(__m128i*) &x[8] );
  • }
  • #elif defined(__MMX__)
  • static inline void histogram_add( const uint16_t x[16], uint16_t y[16] )
  • {
  • *(__m64*) &y[0] = _mm_add_pi16( *(__m64*) &y[0], *(__m64*) &x[0] );
  • *(__m64*) &y[4] = _mm_add_pi16( *(__m64*) &y[4], *(__m64*) &x[4] );
  • *(__m64*) &y[8] = _mm_add_pi16( *(__m64*) &y[8], *(__m64*) &x[8] );
  • *(__m64*) &y[12] = _mm_add_pi16( *(__m64*) &y[12], *(__m64*) &x[12] );
  • }
  • #elif defined(__ALTIVEC__)
  • static inline void histogram_add( const uint16_t x[16], uint16_t y[16] )
  • {
  • *(vector unsigned short*) &y[0] = vec_add( *(vector unsigned short*) &y[0], *(vector unsigned short*) &x[0] );
  • *(vector unsigned short*) &y[8] = vec_add( *(vector unsigned short*) &y[8], *(vector unsigned short*) &x[8] );
  • }
  • #else
  • static inline void histogram_add( const uint16_t x[16], uint16_t y[16] )
  • {
  • int i;
  • for ( i = 0; i < 16; ++i ) {
  • y[i] += x[i];
  • }
  • }
  • #endif
  • /**
  • * Subtracts histogram \a x from \a y and stores the result in \a y. Makes use
  • * of SSE2, MMX or Altivec, if available.
  • */
  • #if defined(__SSE2__)
  • static inline void histogram_sub( const uint16_t x[16], uint16_t y[16] )
  • {
  • *(__m128i*) &y[0] = _mm_sub_epi16( *(__m128i*) &y[0], *(__m128i*) &x[0] );
  • *(__m128i*) &y[8] = _mm_sub_epi16( *(__m128i*) &y[8], *(__m128i*) &x[8] );
  • }
  • #elif defined(__MMX__)
  • static inline void histogram_sub( const uint16_t x[16], uint16_t y[16] )
  • {
  • *(__m64*) &y[0] = _mm_sub_pi16( *(__m64*) &y[0], *(__m64*) &x[0] );
  • *(__m64*) &y[4] = _mm_sub_pi16( *(__m64*) &y[4], *(__m64*) &x[4] );
  • *(__m64*) &y[8] = _mm_sub_pi16( *(__m64*) &y[8], *(__m64*) &x[8] );
  • *(__m64*) &y[12] = _mm_sub_pi16( *(__m64*) &y[12], *(__m64*) &x[12] );
  • }
  • #elif defined(__ALTIVEC__)
  • static inline void histogram_sub( const uint16_t x[16], uint16_t y[16] )
  • {
  • *(vector unsigned short*) &y[0] = vec_sub( *(vector unsigned short*) &y[0], *(vector unsigned short*) &x[0] );
  • *(vector unsigned short*) &y[8] = vec_sub( *(vector unsigned short*) &y[8], *(vector unsigned short*) &x[8] );
  • }
  • #else
  • static inline void histogram_sub( const uint16_t x[16], uint16_t y[16] )
  • {
  • int i;
  • for ( i = 0; i < 16; ++i ) {
  • y[i] -= x[i];
  • }
  • }
  • #endif
  • static inline void histogram_muladd( const uint16_t a, const uint16_t x[16],
  • uint16_t y[16] )
  • {
  • int i;
  • for ( i = 0; i < 16; ++i ) {
  • y[i] += a * x[i];
  • }
  • }
  • static void ctmf_helper(
  • const unsigned char* const src, unsigned char* const dst,
  • const int width, const int height,
  • const int src_step, const int dst_step,
  • const int r, const int cn,
  • const int pad_left, const int pad_right
  • )
  • {
  • const int m = height, n = width;
  • int i, j, k, c;
  • const unsigned char *p, *q;
  • Histogram H[4];
  • uint16_t *h_coarse, *h_fine, luc[4][16];
  • assert( src );
  • assert( dst );
  • assert( r >= 0 );
  • assert( width >= 2*r+1 );
  • assert( height >= 2*r+1 );
  • assert( src_step != 0 );
  • assert( dst_step != 0 );
  • /* SSE2 and MMX need aligned memory, provided by _mm_malloc(). */
  • #if defined(__SSE2__) || defined(__MMX__)
  • h_coarse = (uint16_t*) _mm_malloc( 1 * 16 * n * cn * sizeof(uint16_t), 16 );
  • h_fine = (uint16_t*) _mm_malloc( 16 * 16 * n * cn * sizeof(uint16_t), 16 );
  • memset( h_coarse, 0, 1 * 16 * n * cn * sizeof(uint16_t) );
  • memset( h_fine, 0, 16 * 16 * n * cn * sizeof(uint16_t) );
  • #else
  • h_coarse = (uint16_t*) calloc( 1 * 16 * n * cn, sizeof(uint16_t) );
  • h_fine = (uint16_t*) calloc( 16 * 16 * n * cn, sizeof(uint16_t) );
  • #endif
  • /* First row initialization */
  • for ( j = 0; j < n; ++j ) {
  • for ( c = 0; c < cn; ++c ) {
  • COP( c, j, src[cn*j+c], += r+1 );
  • }
  • }
  • for ( i = 0; i < r; ++i ) {
  • for ( j = 0; j < n; ++j ) {
  • for ( c = 0; c < cn; ++c ) {
  • COP( c, j, src[src_step*i+cn*j+c], ++ );
  • }
  • }
  • }
  • for ( i = 0; i < m; ++i ) {
  • /* Update column histograms for entire row. */
  • p = src + src_step * MAX( 0, i-r-1 );
  • q = p + cn * n;
  • for ( j = 0; p != q; ++j ) {
  • for ( c = 0; c < cn; ++c, ++p ) {
  • COP( c, j, *p, -- );
  • }
  • }
  • p = src + src_step * MIN( m-1, i+r );
  • q = p + cn * n;
  • for ( j = 0; p != q; ++j ) {
  • for ( c = 0; c < cn; ++c, ++p ) {
  • COP( c, j, *p, ++ );
  • }
  • }
  • /* First column initialization */
  • memset( H, 0, cn*sizeof(H[0]) );
  • memset( luc, 0, cn*sizeof(luc[0]) );
  • if ( pad_left ) {
  • for ( c = 0; c < cn; ++c ) {
  • histogram_muladd( r, &h_coarse[16*n*c], H[c].coarse );
  • }
  • }
  • for ( j = 0; j < (pad_left ? r : 2*r); ++j ) {
  • for ( c = 0; c < cn; ++c ) {
  • histogram_add( &h_coarse[16*(n*c+j)], H[c].coarse );
  • }
  • }
  • for ( c = 0; c < cn; ++c ) {
  • for ( k = 0; k < 16; ++k ) {
  • histogram_muladd( 2*r+1, &h_fine[16*n*(16*c+k)], &H[c].fine[k][0] );
  • }
  • }
  • for ( j = pad_left ? 0 : r; j < (pad_right ? n : n-r); ++j ) {
  • for ( c = 0; c < cn; ++c ) {
  • const uint16_t t = 2*r*r + 2*r;
  • uint16_t sum = 0, *segment;
  • int b;
  • histogram_add( &h_coarse[16*(n*c + MIN(j+r,n-1))], H[c].coarse );
  • /* Find median at coarse level */
  • for ( k = 0; k < 16 ; ++k ) {
  • sum += H[c].coarse[k];
  • if ( sum > t ) {
  • sum -= H[c].coarse[k];
  • break;
  • }
  • }
  • assert( k < 16 );
  • /* Update corresponding histogram segment */
  • if ( luc[c][k] <= j-r ) {
  • memset( &H[c].fine[k], 0, 16 * sizeof(uint16_t) );
  • for ( luc[c][k] = j-r; luc[c][k] < MIN(j+r+1,n); ++luc[c][k] ) {
  • histogram_add( &h_fine[16*(n*(16*c+k)+luc[c][k])], H[c].fine[k] );
  • }
  • if ( luc[c][k] < j+r+1 ) {
  • histogram_muladd( j+r+1 - n, &h_fine[16*(n*(16*c+k)+(n-1))], &H[c].fine[k][0] );
  • luc[c][k] = j+r+1;
  • }
  • }
  • else {
  • for ( ; luc[c][k] < j+r+1; ++luc[c][k] ) {
  • histogram_sub( &h_fine[16*(n*(16*c+k)+MAX(luc[c][k]-2*r-1,0))], H[c].fine[k] );
  • histogram_add( &h_fine[16*(n*(16*c+k)+MIN(luc[c][k],n-1))], H[c].fine[k] );
  • }
  • }
  • histogram_sub( &h_coarse[16*(n*c+MAX(j-r,0))], H[c].coarse );
  • /* Find median in segment */
  • segment = H[c].fine[k];
  • for ( b = 0; b < 16 ; ++b ) {
  • sum += segment[b];
  • if ( sum > t ) {
  • dst[dst_step*i+cn*j+c] = 16*k + b;
  • break;
  • }
  • }
  • assert( b < 16 );
  • }
  • }
  • }
  • #if defined(__SSE2__) || defined(__MMX__)
  • _mm_empty();
  • _mm_free(h_coarse);
  • _mm_free(h_fine);
  • #else
  • free(h_coarse);
  • free(h_fine);
  • #endif
  • }
  • /**
  • * \brief Constant-time median filtering
  • *
  • * This function does a median filtering of an 8-bit image. The source image is
  • * processed as if it was padded with zeros. The median kernel is square with
  • * odd dimensions. Images of arbitrary size may be processed.
  • *
  • * To process multi-channel images, you must call this function multiple times,
  • * changing the source and destination adresses and steps such that each channel
  • * is processed as an independent single-channel image.
  • *
  • * Processing images of arbitrary bit depth is not supported.
  • *
  • * The computing time is O(1) per pixel, independent of the radius of the
  • * filter. The algorithm's initialization is O(r*width), but it is negligible.
  • * Memory usage is simple: it will be as big as the cache size, or smaller if
  • * the image is small. For efficiency, the histograms' bins are 16-bit wide.
  • * This may become too small and lead to overflow as \a r increases.
  • *
  • * \param src Source image data.
  • * \param dst Destination image data. Must be preallocated.
  • * \param width Image width, in pixels.
  • * \param height Image height, in pixels.
  • * \param src_step Distance between adjacent pixels on the same column in
  • * the source image, in bytes.
  • * \param dst_step Distance between adjacent pixels on the same column in
  • * the destination image, in bytes.
  • * \param r Median filter radius. The kernel will be a 2*r+1 by
  • * 2*r+1 square.
  • * \param cn Number of channels. For example, a grayscale image would
  • * have cn=1 while an RGB image would have cn=3.
  • * \param memsize Maximum amount of memory to use, in bytes. Set this to
  • * the size of the L2 cache, then vary it slightly and
  • * measure the processing time to find the optimal value.
  • * For example, a 512 kB L2 cache would have
  • * memsize=512*1024 initially.
  • */
  • void ctmf(
  • const unsigned char* const src, unsigned char* const dst,
  • const int width, const int height,
  • const int src_step, const int dst_step,
  • const int r, const int cn, const long unsigned int memsize
  • )
  • {
  • /*
  • * Processing the image in vertical stripes is an optimization made
  • * necessary by the limited size of the CPU cache. Each histogram is 544
  • * bytes big and therefore I can fit a limited number of them in the cache.
  • * That number may sometimes be smaller than the image width, which would be
  • * the number of histograms I would need without stripes.
  • *
  • * I need to keep histograms in the cache so that they are available
  • * quickly when processing a new row. Each row needs access to the previous
  • * row's histograms. If there are too many histograms to fit in the cache,
  • * thrashing to RAM happens.
  • *
  • * To solve this problem, I figure out the maximum number of histograms
  • * that can fit in cache. From this is determined the number of stripes in
  • * an image. The formulas below make the stripes all the same size and use
  • * as few stripes as possible.
  • *
  • * Note that each stripe causes an overlap on the neighboring stripes, as
  • * when mowing the lawn. That overlap is proportional to r. When the overlap
  • * is a significant size in comparison with the stripe size, then we are not
  • * O(1) anymore, but O(r). In fact, we have been O(r) all along, but the
  • * initialization term was neglected, as it has been (and rightly so) in B.
  • * Weiss, "Fast Median and Bilateral Filtering", SIGGRAPH, 2006. Processing
  • * by stripes only makes that initialization term bigger.
  • *
  • * Also, note that the leftmost and rightmost stripes don't need overlap.
  • * A flag is passed to ctmf_helper() so that it treats these cases as if the
  • * image was zero-padded.
  • */
  • int stripes = (int) ceil( (double) (width - 2*r) / (memsize / sizeof(Histogram) - 2*r) );
  • int stripe_size = (int) ceil( (double) ( width + stripes*2*r - 2*r ) / stripes );
  • int i;
  • for ( i = 0; i < width; i += stripe_size - 2*r ) {
  • int stripe = stripe_size;
  • /* Make sure that the filter kernel fits into one stripe. */
  • if ( i + stripe_size - 2*r >= width || width - (i + stripe_size - 2*r) < 2*r+1 ) {
  • stripe = width - i;
  • }
  • ctmf_helper( src + cn*i, dst + cn*i, stripe, height, src_step, dst_step, r, cn,
  • i == 0, stripe == width - i );
  • if ( stripe == width - i ) {
  • break;
  • }
  • }
  • }
/*
 * ctmf.c - Constant-time median filtering
 * Copyright (C) 2006  Simon Perreault
 *
 * Reference: S. Perreault and P. Hébert, "Median Filtering in Constant Time",
 * IEEE Transactions on Image Processing, September 2007.
 *
 * This program has been obtained from http://nomis80.org/ctmf.html. No patent
 * covers this program, although it is subject to the following license:
 *
 *  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 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Contact:
 *  Laboratoire de vision et systèmes numériques
 *  Pavillon Adrien-Pouliot
 *  Université Laval
 *  Sainte-Foy, Québec, Canada
 *  G1K 7P4
 *
 *  perreaul@gel.ulaval.ca
 */

/* Standard C includes */
#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>

/* Type declarations */
#ifdef _MSC_VER
#include <basetsd.h>
typedef UINT8 uint8_t;
typedef UINT16 uint16_t;
typedef UINT32 uint32_t;
#pragma warning( disable: 4799 )
#else
#include <stdint.h>
#endif

/* Intrinsic declarations */
#if defined(__SSE2__) || defined(__MMX__)
#if defined(__SSE2__)
#include <emmintrin.h>
#elif defined(__MMX__)
#include <mmintrin.h>
#endif
#if defined(__GNUC__)
#include <mm_malloc.h>
#elif defined(_MSC_VER)
#include <malloc.h>
#endif
#elif defined(__ALTIVEC__)
#include <altivec.h>
#endif

/* Compiler peculiarities */
#if defined(__GNUC__)
#include <stdint.h>
#define inline __inline__
#define align(x) __attribute__ ((aligned (x)))
#elif defined(_MSC_VER)
#define inline __inline
#define align(x) __declspec(align(x))
#else
#define inline
#define align(x)
#endif

#ifndef MIN
#define MIN(a,b) ((a) > (b) ? (b) : (a))
#endif

#ifndef MAX
#define MAX(a,b) ((a) < (b) ? (b) : (a))
#endif

/**
 * This structure represents a two-tier histogram. The first tier (known as the
 * "coarse" level) is 4 bit wide and the second tier (known as the "fine" level)
 * is 8 bit wide. Pixels inserted in the fine level also get inserted into the
 * coarse bucket designated by the 4 MSBs of the fine bucket value.
 *
 * The structure is aligned on 16 bytes, which is a prerequisite for SIMD
 * instructions. Each bucket is 16 bit wide, which means that extra care must be
 * taken to prevent overflow.
 */
typedef struct align(16)
{
    uint16_t coarse[16];
    uint16_t fine[16][16];
} Histogram;

/**
 * HOP is short for Histogram OPeration. This macro makes an operation \a op on
 * histogram \a h for pixel value \a x. It takes care of handling both levels.
 */
#define HOP(h,x,op) \
    h.coarse[x>>4] op; \
    *((uint16_t*) h.fine + x) op;

#define COP(c,j,x,op) \
    h_coarse[ 16*(n*c+j) + (x>>4) ] op; \
    h_fine[ 16 * (n*(16*c+(x>>4)) + j) + (x & 0xF) ] op;

/**
 * Adds histograms \a x and \a y and stores the result in \a y. Makes use of
 * SSE2, MMX or Altivec, if available.
 */
#if defined(__SSE2__)
static inline void histogram_add( const uint16_t x[16], uint16_t y[16] )
{
    *(__m128i*) &y[0] = _mm_add_epi16( *(__m128i*) &y[0], *(__m128i*) &x[0] );
    *(__m128i*) &y[8] = _mm_add_epi16( *(__m128i*) &y[8], *(__m128i*) &x[8] );
}
#elif defined(__MMX__)
static inline void histogram_add( const uint16_t x[16], uint16_t y[16] )
{
    *(__m64*) &y[0]  = _mm_add_pi16( *(__m64*) &y[0],  *(__m64*) &x[0]  );
    *(__m64*) &y[4]  = _mm_add_pi16( *(__m64*) &y[4],  *(__m64*) &x[4]  );
    *(__m64*) &y[8]  = _mm_add_pi16( *(__m64*) &y[8],  *(__m64*) &x[8]  );
    *(__m64*) &y[12] = _mm_add_pi16( *(__m64*) &y[12], *(__m64*) &x[12] );
}
#elif defined(__ALTIVEC__)
static inline void histogram_add( const uint16_t x[16], uint16_t y[16] )
{
    *(vector unsigned short*) &y[0] = vec_add( *(vector unsigned short*) &y[0], *(vector unsigned short*) &x[0] );
    *(vector unsigned short*) &y[8] = vec_add( *(vector unsigned short*) &y[8], *(vector unsigned short*) &x[8] );
}
#else
static inline void histogram_add( const uint16_t x[16], uint16_t y[16] )
{
    int i;
    for ( i = 0; i < 16; ++i ) {
        y[i] += x[i];
    }
}
#endif

/**
 * Subtracts histogram \a x from \a y and stores the result in \a y. Makes use
 * of SSE2, MMX or Altivec, if available.
 */
#if defined(__SSE2__)
static inline void histogram_sub( const uint16_t x[16], uint16_t y[16] )
{
    *(__m128i*) &y[0] = _mm_sub_epi16( *(__m128i*) &y[0], *(__m128i*) &x[0] );
    *(__m128i*) &y[8] = _mm_sub_epi16( *(__m128i*) &y[8], *(__m128i*) &x[8] );
}
#elif defined(__MMX__)
static inline void histogram_sub( const uint16_t x[16], uint16_t y[16] )
{
    *(__m64*) &y[0]  = _mm_sub_pi16( *(__m64*) &y[0],  *(__m64*) &x[0]  );
    *(__m64*) &y[4]  = _mm_sub_pi16( *(__m64*) &y[4],  *(__m64*) &x[4]  );
    *(__m64*) &y[8]  = _mm_sub_pi16( *(__m64*) &y[8],  *(__m64*) &x[8]  );
    *(__m64*) &y[12] = _mm_sub_pi16( *(__m64*) &y[12], *(__m64*) &x[12] );
}
#elif defined(__ALTIVEC__)
static inline void histogram_sub( const uint16_t x[16], uint16_t y[16] )
{
    *(vector unsigned short*) &y[0] = vec_sub( *(vector unsigned short*) &y[0], *(vector unsigned short*) &x[0] );
    *(vector unsigned short*) &y[8] = vec_sub( *(vector unsigned short*) &y[8], *(vector unsigned short*) &x[8] );
}
#else
static inline void histogram_sub( const uint16_t x[16], uint16_t y[16] )
{
    int i;
    for ( i = 0; i < 16; ++i ) {
        y[i] -= x[i];
    }
}
#endif

static inline void histogram_muladd( const uint16_t a, const uint16_t x[16],
        uint16_t y[16] )
{
    int i;
    for ( i = 0; i < 16; ++i ) {
        y[i] += a * x[i];
    }
}

static void ctmf_helper(
        const unsigned char* const src, unsigned char* const dst,
        const int width, const int height,
        const int src_step, const int dst_step,
        const int r, const int cn,
        const int pad_left, const int pad_right
        )
{
    const int m = height, n = width;
    int i, j, k, c;
    const unsigned char *p, *q;

    Histogram H[4];
    uint16_t *h_coarse, *h_fine, luc[4][16];

    assert( src );
    assert( dst );
    assert( r >= 0 );
    assert( width >= 2*r+1 );
    assert( height >= 2*r+1 );
    assert( src_step != 0 );
    assert( dst_step != 0 );

    /* SSE2 and MMX need aligned memory, provided by _mm_malloc(). */
#if defined(__SSE2__) || defined(__MMX__)
    h_coarse = (uint16_t*) _mm_malloc(  1 * 16 * n * cn * sizeof(uint16_t), 16 );
    h_fine   = (uint16_t*) _mm_malloc( 16 * 16 * n * cn * sizeof(uint16_t), 16 );
    memset( h_coarse, 0,  1 * 16 * n * cn * sizeof(uint16_t) );
    memset( h_fine,   0, 16 * 16 * n * cn * sizeof(uint16_t) );
#else
    h_coarse = (uint16_t*) calloc(  1 * 16 * n * cn, sizeof(uint16_t) );
    h_fine   = (uint16_t*) calloc( 16 * 16 * n * cn, sizeof(uint16_t) );
#endif

    /* First row initialization */
    for ( j = 0; j < n; ++j ) {
        for ( c = 0; c < cn; ++c ) {
            COP( c, j, src[cn*j+c], += r+1 );
        }
    }
    for ( i = 0; i < r; ++i ) {
        for ( j = 0; j < n; ++j ) {
            for ( c = 0; c < cn; ++c ) {
                COP( c, j, src[src_step*i+cn*j+c], ++ );
            }
        }
    }

    for ( i = 0; i < m; ++i ) {

        /* Update column histograms for entire row. */
        p = src + src_step * MAX( 0, i-r-1 );
        q = p + cn * n;
        for ( j = 0; p != q; ++j ) {
            for ( c = 0; c < cn; ++c, ++p ) {
                COP( c, j, *p, -- );
            }
        }

        p = src + src_step * MIN( m-1, i+r );
        q = p + cn * n;
        for ( j = 0; p != q; ++j ) {
            for ( c = 0; c < cn; ++c, ++p ) {
                COP( c, j, *p, ++ );
            }
        }

        /* First column initialization */
        memset( H, 0, cn*sizeof(H[0]) );
        memset( luc, 0, cn*sizeof(luc[0]) );
        if ( pad_left ) {
            for ( c = 0; c < cn; ++c ) {
                histogram_muladd( r, &h_coarse[16*n*c], H[c].coarse );
            }
        }
        for ( j = 0; j < (pad_left ? r : 2*r); ++j ) {
            for ( c = 0; c < cn; ++c ) {
                histogram_add( &h_coarse[16*(n*c+j)], H[c].coarse );
            }
        }
        for ( c = 0; c < cn; ++c ) {
            for ( k = 0; k < 16; ++k ) {
                histogram_muladd( 2*r+1, &h_fine[16*n*(16*c+k)], &H[c].fine[k][0] );
            }
        }

        for ( j = pad_left ? 0 : r; j < (pad_right ? n : n-r); ++j ) {
            for ( c = 0; c < cn; ++c ) {
                const uint16_t t = 2*r*r + 2*r;
                uint16_t sum = 0, *segment;
                int b;

                histogram_add( &h_coarse[16*(n*c + MIN(j+r,n-1))], H[c].coarse );

                /* Find median at coarse level */
                for ( k = 0; k < 16 ; ++k ) {
                    sum += H[c].coarse[k];
                    if ( sum > t ) {
                        sum -= H[c].coarse[k];
                        break;
                    }
                }
                assert( k < 16 );

                /* Update corresponding histogram segment */
                if ( luc[c][k] <= j-r ) {
                    memset( &H[c].fine[k], 0, 16 * sizeof(uint16_t) );
                    for ( luc[c][k] = j-r; luc[c][k] < MIN(j+r+1,n); ++luc[c][k] ) {
                        histogram_add( &h_fine[16*(n*(16*c+k)+luc[c][k])], H[c].fine[k] );
                    }
                    if ( luc[c][k] < j+r+1 ) {
                        histogram_muladd( j+r+1 - n, &h_fine[16*(n*(16*c+k)+(n-1))], &H[c].fine[k][0] );
                        luc[c][k] = j+r+1;
                    }
                }
                else {
                    for ( ; luc[c][k] < j+r+1; ++luc[c][k] ) {
                        histogram_sub( &h_fine[16*(n*(16*c+k)+MAX(luc[c][k]-2*r-1,0))], H[c].fine[k] );
                        histogram_add( &h_fine[16*(n*(16*c+k)+MIN(luc[c][k],n-1))], H[c].fine[k] );
                    }
                }

                histogram_sub( &h_coarse[16*(n*c+MAX(j-r,0))], H[c].coarse );

                /* Find median in segment */
                segment = H[c].fine[k];
                for ( b = 0; b < 16 ; ++b ) {
                    sum += segment[b];
                    if ( sum > t ) {
                        dst[dst_step*i+cn*j+c] = 16*k + b;
                        break;
                    }
                }
                assert( b < 16 );
            }
        }
    }

#if defined(__SSE2__) || defined(__MMX__)
    _mm_empty();
    _mm_free(h_coarse);
    _mm_free(h_fine);
#else
    free(h_coarse);
    free(h_fine);
#endif
}

/**
 * \brief Constant-time median filtering
 *
 * This function does a median filtering of an 8-bit image. The source image is
 * processed as if it was padded with zeros. The median kernel is square with
 * odd dimensions. Images of arbitrary size may be processed.
 *
 * To process multi-channel images, you must call this function multiple times,
 * changing the source and destination adresses and steps such that each channel
 * is processed as an independent single-channel image.
 *
 * Processing images of arbitrary bit depth is not supported.
 *
 * The computing time is O(1) per pixel, independent of the radius of the
 * filter. The algorithm's initialization is O(r*width), but it is negligible.
 * Memory usage is simple: it will be as big as the cache size, or smaller if
 * the image is small. For efficiency, the histograms' bins are 16-bit wide.
 * This may become too small and lead to overflow as \a r increases.
 *
 * \param src           Source image data.
 * \param dst           Destination image data. Must be preallocated.
 * \param width         Image width, in pixels.
 * \param height        Image height, in pixels.
 * \param src_step      Distance between adjacent pixels on the same column in
 *                      the source image, in bytes.
 * \param dst_step      Distance between adjacent pixels on the same column in
 *                      the destination image, in bytes.
 * \param r             Median filter radius. The kernel will be a 2*r+1 by
 *                      2*r+1 square.
 * \param cn            Number of channels. For example, a grayscale image would
 *                      have cn=1 while an RGB image would have cn=3.
 * \param memsize       Maximum amount of memory to use, in bytes. Set this to
 *                      the size of the L2 cache, then vary it slightly and
 *                      measure the processing time to find the optimal value.
 *                      For example, a 512 kB L2 cache would have
 *                      memsize=512*1024 initially.
 */
void ctmf(
        const unsigned char* const src, unsigned char* const dst,
        const int width, const int height,
        const int src_step, const int dst_step,
        const int r, const int cn, const long unsigned int memsize
        )
{
    /*
     * Processing the image in vertical stripes is an optimization made
     * necessary by the limited size of the CPU cache. Each histogram is 544
     * bytes big and therefore I can fit a limited number of them in the cache.
     * That number may sometimes be smaller than the image width, which would be
     * the number of histograms I would need without stripes.
     *
     * I need to keep histograms in the cache so that they are available
     * quickly when processing a new row. Each row needs access to the previous
     * row's histograms. If there are too many histograms to fit in the cache,
     * thrashing to RAM happens.
     *
     * To solve this problem, I figure out the maximum number of histograms
     * that can fit in cache. From this is determined the number of stripes in
     * an image. The formulas below make the stripes all the same size and use
     * as few stripes as possible.
     *
     * Note that each stripe causes an overlap on the neighboring stripes, as
     * when mowing the lawn. That overlap is proportional to r. When the overlap
     * is a significant size in comparison with the stripe size, then we are not
     * O(1) anymore, but O(r). In fact, we have been O(r) all along, but the
     * initialization term was neglected, as it has been (and rightly so) in B.
     * Weiss, "Fast Median and Bilateral Filtering", SIGGRAPH, 2006. Processing
     * by stripes only makes that initialization term bigger.
     *
     * Also, note that the leftmost and rightmost stripes don't need overlap.
     * A flag is passed to ctmf_helper() so that it treats these cases as if the
     * image was zero-padded.
     */
    int stripes = (int) ceil( (double) (width - 2*r) / (memsize / sizeof(Histogram) - 2*r) );
    int stripe_size = (int) ceil( (double) ( width + stripes*2*r - 2*r ) / stripes );

    int i;

    for ( i = 0; i < width; i += stripe_size - 2*r ) {
        int stripe = stripe_size;
        /* Make sure that the filter kernel fits into one stripe. */
        if ( i + stripe_size - 2*r >= width || width - (i + stripe_size - 2*r) < 2*r+1 ) {
            stripe = width - i;
        }

        ctmf_helper( src + cn*i, dst + cn*i, stripe, height, src_step, dst_step, r, cn,
                i == 0, stripe == width - i );

        if ( stripe == width - i ) {
            break;
        }
    }
}

 Conclusion

Sources :  http://nomis80.org/ctmf.html

 Fichier Zip

Les Membres Club peuvent télécharger directement un fichier contenu dans le zip sans télécharger le zip en entier !

Télécharger le zip


 Sources du même auteur

Source avec Zip Source avec une capture VISUALISATION DES IMAGES EN 3D SANS OPENGL
Source avec Zip Source avec une capture ANALYSE DE LA TEXTURE D'UNE IMAGE : FILTRE DE GABOR
Source avec Zip Source avec une capture VIEWER COMPLET POUR LE TRAITEMENT DE L'IMAGE : IMANALYSE
Source avec Zip Source avec une capture ALGORITHMES D'OPTIMISATION NON LINÉAIRE: DESCENTE DE GRADIEN...
Source avec Zip Source avec une capture CLASSE GRAPH: GESTION DES GRAPHIQUES DANS LES APPLICATIONS W...

 Sources de la même categorie

Source avec Zip APPLICATION DE DESSIN DE QUELQUES FIGURES par laguchori
Source avec Zip Source avec une capture HDR EXPOSURE FUSION par mecrosoft
Source avec Zip Source avec une capture IRC CLIENT MULTISERVEUR EN MFC (TXIRC) par TeniX
Source avec Zip ENTETE DU FICHIER BMP (BIPMAP) par k.Lutchi
Source avec Zip Source avec une capture XCOUPE : COUPE 2D par pop70

 Sources en rapport avec celle ci

Source avec Zip Source avec une capture FFT2D, IMAGE, SPECTRE, FILTRE PASSE-BAS PASSE-HAUT par JCDjcd
Source avec Zip Source avec une capture RÉHAUSSEUR DE CONTRASTES par pygargue
Source avec Zip Source avec une capture TRAITEMENT DE L'IMAGE : APPLICATION DE FILTRES (CMUGRAPHIC... par Pistol_Pete
Source avec Zip Source avec une capture EDITEUR D'IMAGES - C++ DEVCPP - FLOU, INVERSION, ROTATION, E... par pyronet
Source avec Zip Source avec une capture TRAITEMENT D'IMAGES BMP [C++\QT3] par rouliow

Commentaires et avis

Commentaire de Forman le 14/03/2009 11:56:56

Salut,

j'ai lu rapidement le code et si j'ai bien compris, le fait que la complexité ne dépende pas de la taille du filtre est dû au fait que les données sont de cardinal fini (ie, dans l'intervalle {0,...,255}) donc qu'on peut utiliser un histogramme pour extraire la valeur médiane, tandis que les données du pixel suivant (à droite) sont obtenues en modifiant légèrement l'histogramme du haut et celui de gauche, c'est bien ça?

Commentaire de Pistol_Pete le 17/03/2009 08:59:52

Salut Forman

Oui c'est ça: En théorie et sans utiliser d'optimisation voici une petite explication de cet algorithme et pourquoi la complexité ne dépend pas du rayon:

On va maintenir à jour autant d'histogramme qu'il y a de colonne. Aussi, chaque colonne aura son histogramme. Pour chaque pixel, on compte 3 étapes:
-On met à jours l'histogramme le plus à droite en deux opérations: 1 addition et 1 soustraction : O(1)
-On ajoute l'histogramme de la colonne droite et on enlève l'histogramme de la colonne gauche qui n'a plus d'effet dans la fenêtre: 256 additions et 256 soustractions pour des images 8 bits: O(1)
-128 comparaisons et 127 additions en moyenne pour trouver le pixel médian: O(1)
Chaque pixel sera ainsi calculé en un temps constant indépendant de r.

En ce qui concerne l'initialisation, elle se fait en O(r) mais cela juste pour les premières lignes. Pour des grandes images, ce temps est négligeable.

Effectivement, la complexité dépend donc de la profondeur de l'image et pas du rayon.

J'espère avoir été assez clair...
N'hésiter pas à demander s'il y a encore des points obscures.

Commentaire de verdy_p le 22/03/2009 21:28:47

Il fautdrait tout de même noter que l'optimisation n'est intéressante QUE si la taille de fenêtre est bien plus grande que le nombre "constant" d'opérations (qui est, lui, proportionnel au produit du nombre de plans de couleurs par l'exponentielle de la profondeur des plans couleurs).

L'exemple prend une image 8 bits (ce qui est maintenant assez rare dans l'imagerie actuelle, la plupart des filtres pour la photo, la vidéo ou les scènes 3D demandant des scènes en 32 bits (hormis les rares cas d'images monochromatiques à faible résolution). L'optilisation dans ce cas ne sera valide que si la taille de fenêtre est supérieure à 512 pixels (total des opérations de somme et différences des histogrammes, augmenté en fait des comparaisons et additions). Hors, une fenêtre de largeur 512 pour le filtre est très supérieure à l'utilisation classique de ce type de filtre.

Le graphique de comparaison est trompeur et n'est sans doûte pas à l'échelle (pour moi, en dessous de r=17 environ, la méthode classique est plus rapide, c'est pourtant un cas trs courant de l'usage de ce genre de filtres pour les images usuelles, par exemple pour éliminer ou lisser les artefacts de compression JPEG ou MPEG ou corriger de blocs manquants dans une vidéo, sans que ces blocs ont une taille fixe 16x16 et dans ce cas le rayon r reste inférieure à cette limite) Hors le graphique montre tout le contraire, même pour r=1 où il est évident que la méthode classique (sans les multiples histogrammes à mettre à jour) sera plus rapide; la mesure serait donc fausse par des lectures multiples des pixels dans l'image source avec une API lente. (noter que la parallélisation reste encore possible même avec un filtre classique en O(r), seul le noyau changeant réellement mais pas les possibilités d'en utiliser un grand nombre d'instances en parallèle pour traiter toute l'image).

Cette optimisation ne peut donc résoudre que des cas particuliers très rares, à fort facteur de réduction sur des images très grandes et en faible résolution de couleur.

Ce cas serait celui de l'imagerie médicale de type des clichés radios, mais les filtres à effet de seuil aussi brutal que le filtre à médiane est en général inadapté car cela doit traiter des images réelles provenant de mesures dont la distribution de bruit ne subit pas un effet de seuil (de type loi de Poisson par exemple) mais plutôt de type Gaussien (il faut donc un filtre continu).

L'exemple donné (l'icône de Windows) n'est certainement pas celui le plus significatif: il est bien plus rapide d'utiliser la méthode à tri, d'autant qu'il est évident que la taille de fenêtre dans cet exemple est très réduite: le choix de la méthode de tri est important puique le tri ne part jamais d'une situation aléatoire après chaque pixel, mais ce tri subit un changement unique après chaque pixel: un tri par insertion dichotomique suffit (la suppression étant directe si on utilise une file de priorité parallèle au vecteur de tri), et donc sur une fenêtre de 16 pixels, il suffira de 4 ou 5 comparaisons au maximum pour réaliser l'insertion. On est alors très loin des 512 opérations par pixel...

Pourrais-tu préciser pour quel type d'images ce filtre est utilisé et en quoi le filtre discontinu à médiane s'avère plus adapté au problème à résoudre qu'un bien plus simple filtre continu à moyenne dont le comportement linéaire est réellement O(1) dans tous les cas et totalement indépendant à la fois de la taille de fenêtre comme à la profondeur binaire des plans de couleur, et ne nécessite pratiquement aucune mémoire annexe (pas besoin d'histogrammes)?

Sinon bravo pour la qualité du code et l'effort d'optimisation du kernel parallélisable sur un seul processeur avec ALTIVEC ou MMX ou SSE (mais il y a aussi des possibilité de parallélisation nettement plus rapides par GPU, ou par certains processeurs de signal de cartes audio, capable de paralléliser un nombre bien plus élevé de kernels, et il y a même des librairies communes permettant de le faire indépendamment des processeurs hôtes ou esclaves parallèles utilisés dans les différents matériels possibles).

Commentaire de Forman le 22/03/2009 21:56:51

Verdy_p: tu écris que "la plupart des filtres pour la photo, la vidéo ou les scènes 3D demandant des scènes en 32 bits" mais si je ne m'abuse, le filtre médian s'applique séparément sur chaque canal. À moins que tu ne veuilles parler d'images codées en 32 bits par canal (donc 12 octets par pixels en RGB) ou que tu connaisses une relation d'ordre total raisonnable pour classer les valeurs RGB entre elles (mais là je doute que ça existe!). Mais c'est vrai que quelquefois, on utilise des images en niveau de gris codés sur 16 bits.

En ce qui concerne le filtre médian en lui-même, je ne comprends pas ce que tu veux dire par "discontinu". Faut-il comprendre "non linéaire"? Il est utilisé généralement pour ôter certains types de bruit, tout en respectant les frontières à fort contraste. C'est d'ailleurs son avantage par rapport aux noyaux régularisants (linéaires) que tu évoques: il ne crée pas une zone de flou au bord des objets. On l'utilise parfois (lui ou d'autres filtres non linéaires du même genre) pour localiser avec précision certains types de bord sur des images bruitées.

Ceci dit je suis d'accord avec le fait qu'on utilise rarement une taille de filtre de 512x512... Mais avec une taille de 16x16 (qui est beaucoup plus raisonnable) on a 256 valeurs à trier par pixel, et a priori je pense que la boucle à 256 itérations proposée devrait faire au moins aussi bien sinon mieux que la méthode "classique".

Commentaire de verdy_p le 23/03/2009 03:41:19

ce que je voulais dire par filtre "discontinu" ce n'est pas "non linéaire". Il y a des filtres non linéaires qui restent continus; par exemple les filtres de flou gaussien, ou en vaguelettes sin(n.x)/x (ce type de filtre sert à la compression d'images comme à son filtrage, avec l'énorme avantage par rapport aux transformées FFT et dérivées que sont les transformées en sinus ou cosinus, que le filtrage permet de borner pratiquement exactement la bande passante du signal résultant dans une fenêtre presque parfaitement rectangulaire, sans aucun repli, tout en conservant la possibilité de restreindre cette bande passante pour aténuer certains détails sans trop de pertes au plan qualitatif, mais aussi en réduisant le bruit). Dans un filtre discontinu, tous les points du kernel ne contribuent pas avec un poids constant (et donc de façon indépendante des autres points) au signal final. La présence dans le filtre médian d'un opérateur discontinu est celle de l'opérateur binaire ">" (équivalent aussi à la somme d'un opérateur linéaire et de la valeur absolue d'une différene linéaire, cette valeur absolue étant cette fois l'opérateur non linéaire). D'autres opértateurs non linéaires utilisent des poids de degré supérieur à 1 (le poids total est à évaluer en fonction de la valeur de tous les points voisins dans le kernel).

Concernant la limite de précision, on peut noter aussi que c'est sur un filtre de rayon supérieur ou égal à 128 (et non 512) qu'il faut aussi augmenter cette précision dans le bin: l'algorithme ne le vérifie pas et les valeurs 16bits d'histogrammes peuvent donc déborder dans certains cas non "pathologiques" comme les plages toutes blanches ou toutes noires qui surviennent sur des zones saturées de l'image (image l'effet sur une photo avec un soleil, ou des zones d'ombre bien contrastées qu'on bien bien noires, notamment sur des scans en très haute résolution comme des radios ou images scanners).

A l'inverse, pour des rayons inférieurs à 8, le nombre de pixels dans le noyau est nécessairement sous 256, et utiliser 16 bits par valeur d'histogramme est un gaspillage; on pourrait à la place utiliser un codage 8-bits avec une meilleure parallélisation (aussi bien en MMX, que 3DNow, SSE, SSE2, Altivec...) Pour le filtrage du bruit de suantification des appareils photos à cellule CCS, les rayons seront nécessairement faibles, et peuvent énormément améliorer la compression JPEG ultérieure tout en réduisant les pertes et artéfacts. Pour l'élimination des artéfacts "carrés" de la compresion JPEG/MPEG ou pour la correction des blocs manquants dans une vidéo MPEG, un filtre médian de rayon 3 ou 4 suffit amplement, et donc là encore les histogrammes sur 8 bits seraient suffisants et encore plus rapides (mais toujours à coût quasi constant).

Pour le filtrage numérique du son en revanche, les échantillons 8 bits ne suffisent pas, même si l'entrée est seulement sur 8 bits, car ceux-ci sont dénormalisés par une transformation non linéaire (de type "loi-A" pour les anciens formats audio américains ou "loi-mu" presque partout ailleurs): souvent il faut avant tout traitement les convertir dans un espace linéaire sur 12 bits minimum (souvent 16 bits). Le filtrage du bruit se fait en plus dans un espace transformé (bidimensionnel celui-là, en temps et en fréquence) demandant une plus grande précision pour les fréquences centrales du spectre, là où aussi le bruit de quantification ou de mesure est le plus important. Hors, avec 16 bits de précision, la largeur des histogrammes devient prohibitive.

C'est pourtant essentiel dans la construction matérielle d'involuteurs en temps réel (par exemple dans les modems de type DSL) qui nécessitent des filtres d'élimination du bruit pas trop destructeurs d'information (car ce type de destruvtion supprime une autre chose essentielle, la phase du signal, pour n'en garder que l'amplitude moyenne dans une fréquence ou couleur donnée).

Bref, cet algo est très bien mais il faut en comprendre les limites, et l'adapter au cas à traiter. Les optimisations MMX/SSE/Altivec ne sont pas si essentielles que ça (et pas forcément péreines non plus) et masquent l'algorithme réel dont le principe est en fait bien plus simple que ne le laisse transparaitre le source.

Aussi il est effectivement ESSENTIEL de se rapporter au document de référence, et merci d'avoir mentionné la source (consulter l'article paru dans les communications IEEE au format PDF pour les détails, il est facile à comprendre même s'il est rédigé en anglais avec des schémas qui rendent le principe finalement très facile à comprendre), même s'il manque une visualisation du principe d'utilisation d'un histogramme au lieu du tri pour décider la valeur médiane:

Ce n'est pas évident de conclure que cette méthode a un temps constant, puisque la méthode utilise en fait une boucle d'intégration progressive de l'histogramme, la "courbe" de l'histograme n'ayant rien de linéaire ni nécessairement distribué de façon uniforme dans les cas réels: le cas moyen n'est valide que pour une image toute grise, le cas le plus favorable est l'image toute noire, le plus défavorable étant l'image toute blanche, et le temps effectif dépendant donc directement de la valeur médiane obtenue dans l'image source elle-même, avec de grosses variations de vitesse en fonction de l'éclairage moyen sur une photo dont la balance des blancs n'est pas nécessairement bien équilibrée sur la photo entière).

Le résultat de cet algorithme n'est donc pas un temps "constant" mais un temps "borné" entre deux bornes dans un rapport fini (jamais plus que le simple au double) qui ne dépend pas du rayon du filtre mais de la précision binaire de l'image source (c'est ce que veut dire "O(1)": ce n'est PAS une constante). Si on regarde les courbes affichées d'ailleurs, on voit que le temps n'est pas une droite parfaite mais connait des petites irrégularités en fonction du rayon sur une image réelle, et cette variation de temps est attendue mais limitée (ce qui permet de garantir par exemple un débit utile sur une vidéo avec une marge de variabilité étroite)

Commentaire de Forman le 23/03/2009 05:58:28

verdy_p: ce qu'on appelle "filtres linéaires" ce sont des filtres définis par un opérateur mathématique linéaire, c'est à dire par une application linéaire "L" d'un espace de fonctions dans lui-même. Par exemple, une image peut être vue comme une fonction définie sur le plan et à valeurs dans un espace de couleurs. Dans ce cas, un opérateur linéaire transforme une image en une autre image, et est tel que pour deux images f et g et pour tout nombre "a" les identité suivantes sont vérifiées:
L(af)=aL(f) et L(f+g)=L(f)+L(g).

En général, il s'agit d'une convolution par un noyau "u" (régularisant ou non):
L(f)(x)=f*u(x)="intégrale sur t de f(x-t)u(t)dt"

En remplaçant f par af ou f+g on retrouve immédiatement les deux propriétés plus haut. Le filtre médian (appelons-le "M") vérifie toujours que M(af)=aM(f) mais ne vérifie pas en général que M(f+g)=M(f)+M(g), et pour cette raison n'est pas linéaire.

Les filtres que tu évoques (filtre passe-bas en sinus cardinal pour supprimer les effets de repliement de spectre) sont linéaires, au sens où ils sont définis par une convolution.

En ce qui concerne la continuité d'un opérateur, pour moi il s'agit de sa sensibilité aux variations. Pour faire simple (difficile d'écrire des formules en ASCII!) un opérateur est continu si en appliquant une petite variation au signal d'entrée on obtient une petite variation sur le signal de sortie. On peut vérifier que c'est le cas du filtre médian.

Dans tous les cas la linéarité d'un opérateur n'implique pas sa continuité (en fait ça n'a rien à voir). Par exemple, l'opérateur de dérivation (en x) n'est pas continu, mais il est linéaire. On peut en calculer une version approximative avec un filtre de la forme [-1,1] (il suffit de soustraire la valeur du pixel de gauche à chaque pixel de l'image) même si dans la pratique on utilise des fenêtre un peu plus large pour diminuer la sensibilité au bruit. En généralisant un peu brutalement, on pourrait dire que les filtres passe-bas (ou régularisant) sont continus, et les passes-hauts discontinus.

En ce qui concerne le traitement du signal audio, est-ce que tu parlais d'appliquer le filtre au signal échantillonné (représentation numérique de la position au cours du temps de la membrane d'un haut-parleur, qui est un signal monodimensionnel dépendant uniquement du temps, comme dans le format WAVE) ou à sa transformée de Fourier (qui effectivement peut être vue comme un signal bidimensionnel, en temps et en fréquence)? Dans le second cas, il me semble évident qu'on ne peut pas se permettre de filtrer les valeurs telles quelles! Mais dans le premier cas, je pense que ce type de filtre peut donner de bons résultats pour gommer certaines imperfections du signal, comme les "clics" d'enregistrement. Le format WAVE, par exemple, peut être encodé en 8 bits par échantillon, et il me semble que la méthode pourrait tout à fait s'appliquer dans ce genre de cas.

Enfin en ce qui concerne la complexité algorithmique, comme tu le dis toi-même, on peut mettre une borne supérieure uniforme (qui ne dépend pas des données à traiter, la variable concernée étant ici le rayon du filtre) sur le temps d'exécution. Il me semble que c'est exactement la définition d'un algorithme à "temps constant".

Commentaire de ibmnoussa le 15/04/2010 17:11:09

Bonsoir
J'ai essayé d'exécuter le code que j'ai télécharger a partir de .zip mais j'ai 3 erreurs lors de la compilation
s'il vous plait pouvez vous m'aider c'est urgent
Ines

Commentaire de Pistol_Pete le 15/04/2010 17:17:43

Salut
Je ne vois pas comment d'aider si tu n'indiques même pas les erreurs que tu as...
A+

Commentaire de ibmnoussa le 15/04/2010 17:26:32

Voici les erreurs que j'ai
C:\Documents and Settings\Ines\Bureau\median\FastMedian\CImage.cpp|197|error: jump to label `errPicture'|
C:\Documents and Settings\Ines\Bureau\median\FastMedian\CImage.cpp|181|error:   from here|
C:\Documents and Settings\Ines\Bureau\median\FastMedian\CImage.cpp|183|error:   crosses initialization of `IPicture*picture'|

J'utilise code:Blocks 8.02 sous windows
Merci

Commentaire de Pistol_Pete le 15/04/2010 17:39:03

Remplace la fonction par celle là:
HBITMAP PictureToBitmap(LPBYTE pmem, DWORD nSize)
{
HRESULT hr;
CoInitialize(0);
HBITMAP hbmp_dst = 0;
IStream* stream = 0;
IPicture* picture = 0;
HBITMAP hbmp_src;
BITMAP bmp;
HGLOBAL hgbl =(HGLOBAL)GlobalAlloc(GMEM_FIXED, nSize);

memcpy(hgbl, pmem, nSize);

hr = CreateStreamOnHGlobal(hgbl, FALSE, &stream);
if(!SUCCEEDED(hr) || !stream) goto errPicture;

hr = OleLoadPicture(stream, nSize, 0, IID_IPicture, (void**)&picture);
if(!SUCCEEDED(hr) || !picture) goto errPicture;

picture->get_Handle((OLE_HANDLE *)&hbmp_src);
if(!SUCCEEDED(hr) || !picture) goto errHandle;

GetObject(hbmp_src, sizeof bmp, &bmp);
hbmp_dst = (HBITMAP)CopyImage(hbmp_src, IMAGE_BITMAP, 0, 0, 0);

errHandle:
picture->Release();
errPicture:
stream->Release();
GlobalFree(hgbl);
CoUninitialize();
return hbmp_dst;
}

Cela doit solutionner tes erreurs.
A+

Commentaire de ibmnoussa le 15/04/2010 17:52:20

Merci mais ca ne marche pas aussi et j'ai encore plus d'erreur
C:\Documents and Settings\Ines\Bureau\median\FastMedian\main.cpp|75|undefined reference to `_SetBkColor@8'|
C:\Documents and Settings\Ines\Bureau\median\FastMedian\main.cpp|76|undefined reference to `_TextOutA@20'|

Je vous explique comment j'ai fait j'ai tout d'abord crée un bouveau projet C++; puis j'ai fait copier coller
votre main dans le main du nouveau projet et puis j'ai ajouté les autres classes
Je veux exécuter un exemple de ctmf.cpp je veux donner une image et le résultat c'est l'image destination après application du constant median filter
Merci d'avance

Commentaire de ibmnoussa le 15/04/2010 17:56:45

de + je ne trouve pas ou je dois indiquer le nom du fichier de l'image que je
veux utiliser

Commentaire de Pistol_Pete le 15/04/2010 18:08:43

Si ca marche! Cela veux juste dire que tu n'as pas installé la platform SDK. Sans elle, tu ne pourras pas programmer d'interface sous windows. (et donc tu ne pourras pas exécuter mon prog)
Si tu veux juste tester le programme, j'ai fourni l'exe.
Si tu veux le compiler, change de compilateur et télécharge VS2005 ou VS 2008 express. Ils sont gratuits.
Si tu veux garder code block, je ne sais pas!

 Ajouter un commentaire


Discussions en rapport avec ce code source dans le forum

génération d'un filtre median en C [ par marouene2706 ] bon comme le titre l'indique, j'ai rencontré un petit probleme lors de la programmation d'un filtre median en C (image 400*300) j'aimerai avoir de l'a filtrage [ par sousoi ] Bonjour, Je connais beaucoup mieux le C que le C++, et j'aimerais implémenter un filtre médian pour faire le filtrage d'une image. J'ai un tableau pou traitement et prédiction de vidéo [ par celinebac ] Si on a à pointer sur une vidéo puis detecter l'ensemble de ses images, ensuite prendre chaque image à part et la traiter pixel par pixel et classer c Besoin d'un cop de pouce ! TRAITEMENT D'IMAGE [ par owenp ] Bonjour tout le monde , je suis actuellement étudiante en 3ème année licence informatique , et je prépare un projet de fin d'étude sous le thème de traitement d'image : binarisation , anamorphoese et reconnaissance de bordure [ par addoudi ] bonjour je suis sensé avoir une photo d'un cadre dessinée sur feuille blanche mais pris en photos sous de mauvaises condition , je dois redresser l'im Reconnaissance de forme -Traitement de l'image [ par macslide ] Bonjour, je suis étudiant en école d'ingénieur et nous avons un projet consistant à reconnaître à partir d'une image base un pièce usiné les formes et traitetement d'image [ par microhard ] slt j'ai un enorme blem avec mon projet de traitement d'image. j'ai créé une classe Image dont la donnée membre image est de type IPicture; mais je ne C et traitement d'image [ par abdobergach ] bonjour à tous je veux quelqu'un pour m'aider à charger une image bitmap et effectuer des traitement sur cette image (zoom in et zoom out) sans utilis traitement d'image [ par bobob ] Bonsoir j'espere que quelqu'un pourra m'aider à résoudre mon problème qui me bloque depuis des jours :( je programme en C , j'ai chargé mon image a détéction des blobs(contours) et filtre median-traitement d'image- [ par marouene2706 ] bonjour comme le titre l'indique, je cherche de l'aide pour la creation d'un filtre median en C d'une part, et d'une autre part , une idée pour la dét


Nos sponsors


Sondage...

Comparez les prix

CalendriCode

Février 2012
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
272829    

Consulter la suite du CalendriCode

 
Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils.
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés

Google Coop CodeS-SourceS Google Coop CodeS-SourceS
Temps d'éxécution de la page : 1,669 sec (3)

Nous contacter | Annoncer sur CodeS-SourceS | Mentions légales