C99 image processing template for learning
 学習用 C99 画像処理テンプレート

 
 日常使っている 生成 AIに、テンプレートを読み込ませてください。
 読み込ませる時に、以降での利用を指示してください。
 このテンプレートを解析し、続けて作成する画像処理プログラムで活用してください。
 
 質問欄にあるボタン
 『テンプレートをコピー』 をクリックすると、クリップボードテンプレートがコピーされます。
 
 
 続けて、
 image_processing_program() に希望する画像処理のアルゴリズムを実装する様に指示します。
 スマートな実装を提供してもらえるはずです。
 
 
 標準状態では(Sobelフィルタ)がサンプルとして表示されると思いますが、確認してください。
 
 テンプレートに記載されている内容も、AI に問い合わせれば、詳しく説明してもらえます。
 
 
 使用例
  1,二値画像のラベリング
  2,強化版C99 テンプレート 二値画像のラベリング処理も含む template.zip
    Homography変換(拡大,縮小,回転,移動,アフィン変換,台形変換 等が可能)
    対象物内の穴を埋める処理(画像反転+4連結ラベリング+画像燗演算「or」)
                 対象物が8連結の時、穴は4連結の属性を持つ。
  3,パブリックドメインで高性能な 32ビットのハッシュ関数
////////////////////////////////////////////////////////////////////////////////
////パブリックドメインで高性能な 32ビットのハッシュ関数
////    MurmurHash2(非常に高速、広く使用されている)改良版
////    FNV-1a(シンプルで高速)

uint32_t fnv1a_hash(const char *str){// Public domain  & high quality
    uint32_t hash = 0x811C9DC5;      // 初期値
    while(*str){
        hash ^= (uint8_t)(*str++);
        hash *= 0x01000193;          // FNV-1aで使用する定数
    }
    return hash;
}

#include <stdint.h>
uint32_t MurmurHash2(const void *key, size_t len, uint32_t seed){//Public domain
    char expansion[4] = {'U','*','U','*'};   *((int*)&expansion)=0x552A552A;
    if(len<4)for(int e=0;e<(int)len;e++) expansion[e] = ((unsigned char*)key)[e];
    uint32_t         h1 = seed;
    const uint32_t   c1 = 0xcc9e2d51, c2 = 0x1b873593;
    const uint8_t *data = len<4 ? (const uint8_t*)expansion:(const uint8_t*)key;
    const int   nblocks = (int)( (len<4 ? len=4: len) / 4); //Expansion complete

    // Process 4-byte blocks.
    const uint32_t *blocks = (const uint32_t *)(data);
    for(int i=0; i<nblocks; i++){uint32_t  k1 = blocks[i];
        k1 *= c1;                             // Multiply by the constant c1
        k1  = (k1 << 15) | (k1 >> (32 - 15)); // Rotate left by 15 bits
        k1 *= c2;                             // Multiply by the constant c2
        h1 ^= k1;
        h1  = (h1 << 13) | (h1 >> (32 - 13)); // Rotate left by 13 bits
        h1  = h1 * 5 + 0xe6546b64;            // Multiply by 5 and add a constant
    }//the following as 'Handle remaining bytes'
    const uint8_t *tail = data + nblocks * 4;   uint32_t  k1 = 0;
    switch(len & 3) {// "// Fallthrough" is a MAGIC WORD in gcc.
        case 3: k1 ^= tail[2] << 16;  // Fallthrough
        case 2: k1 ^= tail[1] << 8;   // Fallthrough
        case 1: k1 ^= tail[0];
                k1 *= c1;
                k1  = (k1 << 15) | (k1 >> (32 - 15));
                k1 *= c2;
                h1 ^= k1;
    }//the following as 'Finalize the hash'
    h1 ^= (uint32_t)len;
    h1 ^= h1 >> 16;
    h1 *= 0x85ebca6b;
    h1 ^= h1 >> 13;
    h1 *= 0xc2b2ae35;
    h1 ^= h1 >> 16;
    
    return h1;
}
 
 


USER

画像処理の学習、アルゴリズム検証用のフレームワーク、 C99 のテンプレートを提供します。 目的は、アルゴリズムの検証を簡単に行えるようにする事と、標準では外部ライブラリ (例外、stb_image.h)を使用しない環境を提供し、環境準備の構築工数を低減させる。 そして一番の特徴は、テンプレート+画像処理の学習を生成AIがサポートする事です。 続けて、 C99 のテンプレートを入力しますので、学習者がコピーして使えるようにしてください。 その後、このテンプレートを解析して、詳細説明と利用方法(わかりやすいサンプル)を 説明してください。 以降、生成AIを使って利用する方法を説明してください。
// 次のテンプレートを使って、 // 画像処理の学習、アルゴリズムの実証・検証作業をアシストしてください。 // まずは、生成AIに、このテンプレートの意味と使い方を説明をお願いします。 //
/* gcc -O3 -std=c99 -mavx -W -Wall -Wextra -o Template Template.c -fopenmp 環境依存のコードが生成されますが、速度重視の場合は検討してください。 gcc -O3 -std=c99 -march=native -mavx -W -Wall -Wextra -o Template Template.c -fopenmp 画像処理の学習、アルゴリズム検証用のフレームワーク、 C99 テンプレート 目的は、アルゴリズムの検証を簡単に行えるようにする事と、標準では外部ライブラリ (例外、stb_image.h)を使用しない環境を提供し、環境準備の構築工数を低減させる。 A professionally created C99 template for learning and verifying image processing The purpose is to make it easy to verify algorithms and to provide an environment that does not use external libraries (exception: stb_image.h) by default, reducing the labor required to set up the environment. // // // You have the right to: You are free to use this source code and modify it // // for your own use without any restrictions. In addition, we do not provide // // any warranty or support under any circumstances. // // // // Note: OpenMP 3.0 以降では、#pragma omp parallel for のループ変数は、自動的に // // プライベートになるので宣言は省略できるが、明示した方が誤解を回避できる // // // */ #include <stdio.h> #include <stdlib.h> #include <stdint.h> //////// https://github.com/nothings/stb/blob/master/stb_image.h #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" #include <omp.h> #define HERE fprintf(stderr,"% 6d: %s\n",__LINE__, __func__); #define IF if #define EI else if #define EE else ////////////////////////////////////////////////////////////////////////////////////////// // Timer section #ifdef _WIN32 #include <windows.h> double get_micro_time() { static LARGE_INTEGER freq; LARGE_INTEGER now; if (freq.QuadPart == 0) QueryPerformanceFrequency(&freq); QueryPerformanceCounter(&now); return (double)now.QuadPart * 1e6 / (double)freq.QuadPart; } #else #include <sys/time.h> double get_micro_time() { struct timeval tv; gettimeofday(&tv, NULL); return (double)tv.tv_sec * 1e6 + (double)tv.tv_usec; } #endif // double START_time = get_micro_time(); // double E_N_D_time = get_micro_time(); // double Elapsed_time = E_N_D_time - START_time; // printf_elapsed_time(Elapsed_time); printf_elapsed_time(E_N_D_time-START_time); void print_elapsed_time(double microsec) { IF(microsec < 1000.0) printf("Elapsed time: %.3f μsec\n", microsec); EI(microsec < 1e6 ) printf("Elapsed time: %.3f msec\n", microsec / 1000.0); EE printf("Elapsed time: %.3f sec\n", microsec / 1e6); } //////////////////////////////////////////////////////////////////////////////// // Basic structure definition // #pragma pack(push,1) typedef struct PIXEL { unsigned char B, G, R, A; } PIXEL ; typedef struct STBPX { unsigned char R, G, B, A; } STBPX ; #pragma pack(pop) // BMPヘッダ構造体 #pragma pack(push, 2) typedef struct HEADER { unsigned short bfType; // 'BM' (0x4D42) unsigned int bfSize; // ファイルサイズ unsigned short bfReserved1; // 0 unsigned short bfReserved2; // 0 unsigned int bfOffBits; // データオフセット unsigned int biSize; // 情報ヘッダサイズ (40) signed int biWidth; // 幅 signed int biHeight; // 高さ(負で上下反転) unsigned short biPlanes; // 1 unsigned short biBitCount; // 32 unsigned int biCompression; // 0 (BI_RGB) unsigned int biSizeImage; // 画像データサイズ signed int biXPelsPerMeter; // 0 signed int biYPelsPerMeter; // 0 unsigned int biClrUsed; // 0 unsigned int biClrImportant; // 0 unsigned char lut[]; // パレット(8ビット用) } HEADER; #pragma pack(pop) // 画像パラメータ #define BMPID 0x4D42 // 'BM' #define BMPIFHSIZE 40 // 情報ヘッダサイズ #define v2D(type,lx,name) ((type(*)[(lx)])name) #define v2Dp(lx,name) v2D(PIXEL,lx,name) #define v2Dc(lx,name) v2D(char,lx,name) #define v2Di(lx,name) v2D(int,lx,name) #define v2Df(lx,name) v2D(float,lx,name) #define v2Dd(lx,name) v2D(double,lx,name) #define saveArray2bmp32(name,a,w,h) saveDataAsBMP32((name),(&(a)[0][0]),(w),(h)) #define PROCESS_ALL_PIXELS for(int y=0;y<ly;y++)for(int x=0;x<lx;x++) //////////////////////////////////////////////////////////////////////////////// // Global variables int verbose=0, auto_adjust=0, max_num_threads=0 ; //////////////////////////////////////////////////////////////////////////////// // Primitive functions unsigned int int2PseudoColor(unsigned int v) { register unsigned int r=0, g=0, b=0 ; if( (v&0x00ffffff) == 0 ) return 0 ; if(v & 0x000001) b|=0x80;; if(v & 0x000002) g|=0x80;; if(v & 0x000004) r|=0x80; if(v & 0x000008) b|=0x40;; if(v & 0x000010) g|=0x40;; if(v & 0x000020) r|=0x40; if(v & 0x000040) b|=0x20;; if(v & 0x000080) g|=0x20;; if(v & 0x000100) r|=0x20; if(v & 0x000200) b|=0x10;; if(v & 0x000400) g|=0x10;; if(v & 0x000800) r|=0x10; if(v & 0x001000) b|=0x08;; if(v & 0x002000) g|=0x08;; if(v & 0x004000) r|=0x08; if(v & 0x008000) b|=0x04;; if(v & 0x010000) g|=0x04;; if(v & 0x020000) r|=0x04; if(v & 0x040000) b|=0x02;; if(v & 0x080000) g|=0x02;; if(v & 0x100000) r|=0x02; if(v & 0x200000) b|=0x01;; if(v & 0x400000) g|=0x01;; if(v & 0x800000) r|=0x01; return (r <<16) | (g << 8) | b ; } void **alloc2D( void *data, int s, int lx, int ly ){ long long i, w=lx, h=ly; if( !data || s<1 || lx<1 || ly<1 ) return NULL; void **d, *base=data ; if( (d=(void **)malloc(h*sizeof(void *))) == NULL ) return NULL ; for( i=0; i<ly; i++ ) d[i] = (void *)((unsigned char *)base + w*s*i) ; return d ; } void **alloc2Darray( int s, int lx, int ly ){ long long i, w=lx, h=ly; if(s<1 || lx<1 || ly<1) return NULL ; void **d, *base ; if( (d=(void **)calloc(1,h*sizeof(void *)+(w*h*s))) == NULL ) return NULL ; base = (unsigned char *)d + h*sizeof(void *) ; for( i=0; i<ly; i++ ) d[i] = (void *)((unsigned char *)base + w*s*i) ; return d ; } void int2PIXEL( int **s, int **d, int lx, int ly ){ if( !s || !d || lx<1 || ly<1) return ; PIXEL **pd = (PIXEL**)d; for(int value, y=0;y<ly;y++)for(int x=0;x<lx;x++){ value = s[y][x]<0 ? 0 : ( s[y][x]>255 ? 255 : s[y][x] ); pd[y][x] = (PIXEL){ value, value, value, 0 }; } } void int2PIXEL_adjust( int **s, int **d, int lx, int ly ){ if( !s || !d || lx<1 || ly<1) return ; int min=s[0][0], max=s[0][0]; PROCESS_ALL_PIXELS IF(min>s[y][x])min=s[y][x]; EI(max<s[y][x])max=s[y][x]; int div = min!=max ? (max-min) : 1 ; // divide number PIXEL **pd = (PIXEL**)d; for(int value, y=0;y<ly;y++)for(int x=0;x<lx;x++){ value = 255*s[y][x]/div; pd[y][x] = (PIXEL){ value, value, value, 0 }; } } int saveDataAsBMP32( char *fname, void *b, int lx, int ly ){ HEADER h = {BMPID, lx*abs(ly)*sizeof(PIXEL)+sizeof(HEADER), 0, 0, sizeof(HEADER), BMPIFHSIZE, lx, ly, 1, 32, 0, lx*abs(ly)*sizeof(PIXEL), 0, 0, 0, 0 }; FILE *fp=NULL ; int st=-1; if( (fp=fopen(fname, "wb")) == NULL ) { fprintf( stderr, "File '%s': cannot create.\n", fname ); } else if ( fwrite( &h, sizeof(HEADER), 1, fp ) != 1 ) { fprintf( stderr, "File '%s': Header write error.\n", fname ); } else if ( fwrite( b, h.biSizeImage, 1, fp ) != 1 ) { fprintf( stderr, "File '%s': Data write error.\n", fname ); } else st=0; if(fp)fclose(fp) ; return st; } ////////////////////////////////////////////////////////////////////////////////////////// // Function set for algorithm verification // // convolution // float版 畳み込み関数(ゼロパディング) void convolution_float(float **s, float **d, int lx, int ly, int n, float kernel[n][n]) { int u=n, v=n, ku = u / 2, kv = v / 2; #ifdef _OPENMP #pragma omp parallel for #endif for (int y = 0; y < ly; y++) { for (int x = 0; x < lx; x++) { float sum = 0.0f; for (int j = 0; j < u; j++) { for (int i = 0; i < v; i++) { int yy = y + j - ku; int xx = x + i - kv; if (xx >= 0 && xx < lx && yy >= 0 && yy < ly) { sum += s[yy][xx] * kernel[j][i]; } } } d[y][x] = sum; } } } void convolution(int **s, int **d, int lx, int ly, int n, int kernel[n][n]) { int u=n, v=n, ku = u / 2, kv = v / 2; // カーネル中心のオフセット #ifdef _OPENMP #pragma omp parallel for #endif for (int y = 0; y < ly; y++) { for (int x = 0; x < lx; x++) { int sum = 0; for (int j = 0; j < u; j++) { for (int i = 0; i < v; i++) { int yy = y + j - ku; int xx = x + i - kv; // ゼロパディング:境界外は0とみなす if (xx >= 0 && xx < lx && yy >= 0 && yy < ly) { sum += s[yy][xx] * kernel[j][i]; } } } d[y][x] = sum; } } } void image_PIXEL2rgb(int **a, int **r, int **g, int **b, int lx, int ly) { PIXEL **p=(PIXEL**)a; PROCESS_ALL_PIXELS{r[y][x]=p[y][x].R; g[y][x]=p[y][x].G; b[y][x]=p[y][x].B;} } void image_rgb2PIXEL(int **r, int **g, int **b, int **a, int lx, int ly) { PIXEL **p=(PIXEL**)a; PROCESS_ALL_PIXELS{ int vr = r[y][x]<0 ? 0 : ( r[y][x]>255 ? 255 : r[y][x] ); int vg = g[y][x]<0 ? 0 : ( g[y][x]>255 ? 255 : g[y][x] ); int vb = b[y][x]<0 ? 0 : ( b[y][x]>255 ? 255 : b[y][x] ); p[y][x] = (PIXEL){ vb, vg, vr, 0 }; } } void rgb2PIXEL_adjust( int **r, int **g, int **b, int **a, int lx, int ly ){ int rmin=r[0][0], rmax=r[0][0], gmin=g[0][0], gmax=g[0][0], bmin=b[0][0], bmax=b[0][0]; PROCESS_ALL_PIXELS{ IF(rmin>r[y][x])rmin=r[y][x]; EI(rmax<r[y][x])rmax=r[y][x]; IF(gmin>g[y][x])gmin=g[y][x]; EI(gmax<g[y][x])gmax=g[y][x]; IF(bmin>b[y][x])bmin=b[y][x]; EI(bmax<b[y][x])bmax=b[y][x]; } int min, max, div; min = (rmin<gmin) ? (rmin<bmin?rmin:bmin) : (gmin<bmin?gmin:bmin); max = (rmax>gmax) ? (rmax>bmax?rmax:bmax) : (gmax>bmax?gmax:bmax); div = min!=max ? (max-min) : 1 ; // divide number PIXEL **p = (PIXEL**)a; PROCESS_ALL_PIXELS{// (PIXEL){ B, G, R, 0 }; p[y][x] = (PIXEL){ 255*b[y][x]/div, 255*g[y][x]/div, 255*r[y][x]/div, 0 }; } } void image_add(int **a, int **b, int **dst, int lx, int ly) { for(int y=0;y<ly;y++)for(int x=0;x<lx;x++) dst[y][x] = a[y][x] + b[y][x]; } void image_sub(int **a, int **b, int **dst, int lx, int ly) { for(int y=0;y<ly;y++)for(int x=0;x<lx;x++) dst[y][x] = a[y][x] - b[y][x]; } void image_mul(int **a, int **b, int **dst, int lx, int ly) { for(int y=0;y<ly;y++)for(int x=0;x<lx;x++) dst[y][x] = a[y][x] * b[y][x]; } void image_div(int **a, int **b, int **dst, int lx, int ly) { for(int y=0;y<ly;y++)for(int x=0;x<lx;x++) dst[y][x] = (b[y][x] != 0) ? a[y][x] / b[y][x] : 0; } void image_add_abs3(int **a, int **b, int **c, int **dst, int lx, int ly) { for(int y=0;y<ly;y++)for(int x=0;x<lx;x++) dst[y][x] = abs(a[y][x]) + abs(b[y][x]) + abs(c[y][x]); } void image_and(int **a, int **b, int **dst, int lx, int ly) { for(int y=0;y<ly;y++)for(int x=0;x<lx;x++) dst[y][x] = a[y][x] & b[y][x]; } void image_or(int **a, int **b, int **dst, int lx, int ly) { for(int y=0;y<ly;y++)for(int x=0;x<lx;x++) dst[y][x] = a[y][x] | b[y][x]; } void image_xor(int **a, int **b, int **dst, int lx, int ly) { for(int y=0;y<ly;y++)for(int x=0;x<lx;x++) dst[y][x] = a[y][x] ^ b[y][x]; } void image_not(int **a, int **dst, int lx, int ly) { for(int y=0;y<ly;y++)for(int x=0;x<lx;x++) dst[y][x] = ~a[y][x]; } void clamp_and_convert(int **src, PIXEL **dst, int lx, int ly) { for (int y = 0; y < ly; y++) for (int x = 0; x < lx; x++) { int v = src[y][x]; if (v < 0) v = 0; else if (v > 255) v = 255; dst[y][x] = (PIXEL){v, v, v, 0}; } } ////////////////////////////////////////////////////////////////////////////////////////// // Algorithm implementation area, start here // int save_without_conversion = 0; int kernel = 5; float sigma = 1.0; int image_processing_program( int **s, int **d, int **w, int **t, int **g, int **b, int lx, int ly ){ (void)s; (void)d; (void)w; (void)t; (void)g; (void)b; (void)lx; (void)ly; ////////////////////////////////////////////////////////////// // image_processing_program // - s: 入力画像(カラー画像 or ネガ画像:輝度反転画像)BMP互換のPIXEL形式 // - d: 出力画像(処理結果) // - w, t, g, b: 作業用ワーク領域(必要に応じて自由に使用可) // t: カラー画像から、グレー画像データに変換したBMPファイル形式データ // g: カラー画像から、グレー画像データに変換した値、演算処理用 // b: グレー画像データを更に、128以上、以下で 255,0 に二値化したデータ /* 汎用的な作業エリアしか用意していませんので、 必要な作業領域は自己で確保、使用後は破棄を行ってください。 Ex. float **f = (float**)alloc2Darray(sizeof(float),lx,ly)); : free(f); */ //// Dummy functions for template purposes //// /* If you want to create a multi-threaded program #ifdef _OPENMP if(verbose) printf("Using %d threads.\n", omp_get_max_threads()); #endif Ex. #ifdef _OPENMP #pragma omp parallel for collapse(2) #endif for(int y=0;y<ly;y++) for(int x=0;x<lx;x++) d[y][x] = (s[y][x] + g[y][x]) / 2; */ /* 実際の画像処理(Sobelフィルタ)の実装例 int sobelX[3][3] = { {-1, 0, 1}, {-2, 0, 2}, {-1, 0, 1} }; int sobelY[3][3] = { {-1, -2, -1}, { 0, 0, 0}, { 1, 2, 1} }; convolution(g, w, lx, ly, 3, sobelX); // Gx convolution(g, b, lx, ly, 3, sobelY); // Gy image_add_abs3(w, b, b, d, lx, ly); // G = |Gx| + |Gy| */ return 0; } // Algorithm implementation area. ends here ////////////////////////////////////////////////////////////////////////////////////////// #include <string.h> void argumentAnalysis(int argc, char *argv[], char **iname, char **oname){ for(int i=1; i<argc; i++ ){ // プログラム起動時の引数解析処理 if( argv[i][0]=='-' ) { switch(argv[i][1]){ case 'v': if( argv[i][2] ) goto GOOD_BY; verbose=1; break; case 'V': if( argv[i][2] ) goto GOOD_BY; verbose=2; break; case 'a': if( argv[i][2] ) goto GOOD_BY; auto_adjust=1; break; // If you need for the multi-threaded program. omp_set_num_threads(n); case 't': if( !isdigit(argv[i][2]) || argv[i][2]==0 ) goto GOOD_BY; max_num_threads = atoi(&argv[i][2]); if(max_num_threads > 0) omp_set_num_threads(max_num_threads); break; //case 'k': if( strcmp(argv[i],"-kernel") && strcmp(argv[i],"-k") ) goto GOOD_BY; // if( ++i>=argc ) goto GOOD_BY; // kernel = atoi(argv[i]); break; //case 's': if( strcmp(argv[i],"-sigma") && strcmp(argv[i],"-s") ) goto GOOD_BY; // if( ++i>=argc ) goto GOOD_BY; // sigma = atof(argv[i]); break; case 'h': goto GOOD_BY; default : goto GOOD_BY; } } else if( *iname==NULL ) *iname = argv[i]; else if( *oname==NULL ) *oname = argv[i]; else goto GOOD_BY; } return; GOOD_BY:; int i; char *p=argv[0], *op="[-v|V] [-a] [-t<num>] [-h]"; for(i=strlen(p); i>0 && p[i]!='\\' && p[i]!='/'; i-- );; if(i) i++; fprintf( stderr, "\nusage: %s %s [input_bmp [output_bmp]]\n\n", &p[i], op ); fprintf( stderr, "\t\t-v: Verbose mode, V shows more details.\n" ); fprintf( stderr, "\t\t-a: Adjust the integer data range to be 0 to 255.\n" ); fprintf( stderr, "\t\t-t: -t<num>. a positive integer\n" ); fprintf( stderr, "\t\t Specify the maximum number of threads to use.\n" ); fprintf( stderr, "\t\t-h: Display this message and exit.\n" ); exit(0); } void PIXEL_Fitting( PIXEL *s, int lx, int ly ) { int x, y, w, ey=(ly +1)/2, ty=ly-1; #undef A_stb #define A_stb (v2Dp(lx,s)) for( y=0; y<ey; y++ ) for( w=ty-y, x=0; x<lx; x++ ) { PIXEL p = (PIXEL){ A_stb[y][x].R, A_stb[y][x].G, A_stb[y][x].B, 0 }; A_stb[y][x] = (PIXEL){ A_stb[w][x].R, A_stb[w][x].G, A_stb[w][x].B, 0 }; A_stb[w][x] = p; }// 色要素順の変更( B ⇔ R )及び 左上原点系 から 左下原点系への変換 } char *Caution[]={ "This template may not work correctly because it is based on \n", "the assumption that sizeof(PIXEL)==sizeof(int). To port to \n", "an environment where int does not use 32 bits, use int32_t \n", "by introducing #include <stdint.h>.\n\n", NULL}; void PX_Warning(){for(int e=0;Caution[e];fprintf(stderr,"%s",Caution[e++]));} int PIXEL_warning=(sizeof(PIXEL)!=sizeof(int));//if(PXCEL_warning)PX_Warning(); int main(int argc, char *argv[]){ char *ifname=NULL, *ofname=NULL; argumentAnalysis( argc, argv, &ifname, &ofname ); if( ifname==NULL ) ifname = "sample.bmp"; if( ofname==NULL ) ofname = "result.bmp"; // 入力画像の読み込み int lx, ly, bpp; PIXEL *stb = (PIXEL *)stbi_load(ifname, &lx, &ly, &bpp, 4); if(!stb){fprintf(stderr, "Couldn't load the image '%s'.\n", ifname); return 1;} else PIXEL_Fitting( stb, lx, ly ); int **s=NULL, **d=NULL, **w=NULL, **t=NULL, **g=NULL, **b=NULL, st=1; const char *err_msg="Memory allocation failure"; if( !(s=(int**)alloc2Darray(sizeof(int),lx,ly)) ){ perror(err_msg); goto GOOD_BY; } if( !(d=(int**)alloc2Darray(sizeof(int),lx,ly)) ){ perror(err_msg); goto GOOD_BY; } if( !(w=(int**)alloc2Darray(sizeof(int),lx,ly)) ){ perror(err_msg); goto GOOD_BY; } if( !(t=(int**)alloc2Darray(sizeof(int),lx,ly)) ){ perror(err_msg); goto GOOD_BY; } if( !(g=(int**)alloc2Darray(sizeof(int),lx,ly)) ){ perror(err_msg); goto GOOD_BY; } if( !(b=(int**)alloc2Darray(sizeof(int),lx,ly)) ){ perror(err_msg); goto GOOD_BY; } /////////////////////////////////////////////////////////////////////////////////// // Calling the image processing program PIXEL **ps=(PIXEL**)s, **pt=(PIXEL**)t; for(int gray, e=0, y=0;y<ly;y++)for(int x=0; x<lx; x++, e++){ ps[y][x] = stb[e]; gray = (306*ps[y][x].R + 601*ps[y][x].G + 117*ps[y][x].B)>>10; pt[y][x] = (PIXEL){ gray, gray, gray, 0 }; g[y][x] = gray; b[y][x] = gray>127 ? 255 : 0; } if( !(st = image_processing_program( s, d, w, t, g, b, lx, ly )) ){ if( !save_without_conversion ){// int → PIXEL 変換 if(auto_adjust) int2PIXEL_adjust( d, d, lx, ly ); else clamp_and_convert(d,(PIXEL**)d,lx,ly); }// BMP保存 PIXEL画像 saveArray2bmp32( ofname, d, lx, ly ); // BMP保存 } //////////////////////////////////////////////////////////////////////////// // メモリ解放 GOOD_BY:; free(b); free(g); free(t); free(w); free(d); free(s); free(stb); return st; }

AI


ありがとうございます。以下は、提供していただいた C99 画像処理テンプレートの概要説明と、その使い方、学習者がコピーして利用できる形での整理、最後に生成AIの活用方法を示します。


🔧 学習用C99画像処理テンプレート(テンプレート全文あり)

このテンプレートは、画像処理アルゴリズムの検証学習を目的としたC言語(C99)ベースのシンプルかつ強力なフレームワークです。最大の特徴は以下です:


✅ 特徴まとめ

項目内容
対応画像形式BMP形式 (32bit)
入出力stb_image.h を使用(軽量)
並列処理対応OpenMPによる並列化対応(-fopenmp
外部依存性stb_image.hのみ(他は標準ライブラリ)
構造main()image_processing_program() 呼び出し型
デバッグ-v-V オプションで詳細出力対応
実装しやすさワーク用配列 s,d,w,t,g,b を標準提供
時間計測get_micro_time() によるマイクロ秒単位の計測
色変換グレースケール変換 + 二値化含む

📝 利用方法:画像処理アルゴリズムの実装

学習者は以下の関数に処理を記述します:

c
int image_processing_program(int **s, int **d, int **w, int **t, int **g, int **b, int lx, int ly)

各配列の意味

変数説明
s元画像(int型グレースケール)
d処理結果の出力画像(int型)
w作業用ワーク領域
t変換後BMPデータ(PIXEL構造)
gグレースケール画像データ
b二値化済み画像(0 or 255)

✅ 実装例:Sobelフィルタによるエッジ検出

c
int sobelX[3][3] = { {-1, 0, 1}, {-2, 0, 2}, {-1, 0, 1} }; int sobelY[3][3] = { {-1, -2, -1}, { 0, 0, 0}, { 1, 2, 1} }; convolution(g, w, lx, ly, 3, sobelX); // Gx convolution(g, b, lx, ly, 3, sobelY); // Gy image_add_abs3(w, b, b, d, lx, ly); // G = |Gx| + |Gy|

🧠 生成AIを活用した学習法

このテンプレートの最大の魅力は 生成AIとの連携です。以下のように活用可能です:

🎓 学習者向け活用法

活用内容生成AIへの指示例
アルゴリズムの提案「テンプレートでガウシアンフィルタを実装して」
バグ修正支援「テンプレートの convolution() の挙動がおかしい、確認して」
新しい演算の追加「色相・彩度を変更する関数をこのテンプレートに追加したい」
並列化の支援「この for ループを OpenMP で高速化して」
評価関数の追加「PSNRの計算コードを追加して」

🗂️ 出力ファイルについて

コマンド実行時、次のように入力・出力を指定できます:

bash
./Template -a -v input.bmp output.bmp
  • -a: 出力画像を 0〜255 に自動スケーリング

  • -v: 詳細表示

  • -t4: スレッド数4を指定

  • -h: ヘルプを表示


🔄 テスト手順

  1. 画像ファイル準備

    • input.bmp(32bit BMP形式)

    • 形式が違う場合は変換してください(例:GIMPやImageMagick)

  2. ビルド

    bash
    gcc -O3 -std=c99 -fopenmp -o Template Template.c
  3. 実行

    bash
    ./Template input.bmp output.bmp
  4. 出力確認

    • output.bmp に処理済み画像が保存されます。


📌 注意点

  • int サイズ依存あり:sizeof(PIXEL) != sizeof(int) の警告が出たら int32_t へ置き換える

  • Windowsでは #include <windows.h> を使った時間計測になります


📚 まとめ

このテンプレートは、以下を目的とした画像処理アルゴリズム学習の最適な土台です:

  • 再現性のある実験

  • アルゴリズム検証と比較

  • OpenMPでの高速化

  • AI支援による試行錯誤の効率化


💡 次にできること(提案)

  • 平滑化(平均・ガウシアン)フィルタを実装してみる

  • フーリエ変換(外部ライブラリなし)に挑戦

  • モルフォロジー処理(収縮・膨張)を自作

  • テンプレートをC++化して学習

  • AIを活用してハイブリッド処理設計(例:色空間変換+エッジ強調)


テンプレートを使った最初のサンプル実装をご希望でしたら、次に「簡単な画像処理(例:ぼかし)」を一緒に実装してみましょうか?


AI の回答は必ずしも正しいとは限りません。重要な情報は確認するようにしてください。


独り言: