• 検索結果がありません。

Microsoft PowerPoint - handout09.ppt

N/A
N/A
Protected

Academic year: 2021

シェア "Microsoft PowerPoint - handout09.ppt"

Copied!
8
0
0

読み込み中.... (全文を見る)

全文

(1)

応用プログラミング 第9回

~プログラミングの応用

画像処理その2

電気通信大学電子工学専攻

Intelligent Electronic Systems Group

長井 隆行

本日の内容

1.

まずは課題

2.

画像処理の色々

3.

色々とプログラムを手直ししよう

4.

上下左右の入れ替え・回転

5.

縮小

6.

拡大

7.

拡大するのは難しい

課題

bitmap.bmp 赤(R,G,B)=(255,0,0) 橙(R,G,B)=(255,127,0) サイズ:16×16 各画素:24bit (R,G,B) (但し、bitmapはBGRの順で並んでいる) 茶(R,G,B)=(127,127,0)

各自で試してみる

提出しなくてよい

bitmap.bmpをコンソールに表示してみましょう!

†

ビットマップのフォーマットに注意

†

各画素がある条件を満たせば特定の文字を表示する(例

えば、(255,0,0)なら"赤"など)

†

ヒントプログラムp9-0forInt.c or p9-0forMot.cをDL

可能

†

表示する(printfを使う)だけなので、プログラム中で画像

を保存する必要はない

課題 続き

注意点

†

ビットマップのフォーマッ

トに注意

„

左下からデータが並んで

いる

„

RGBではなく、BGRの順

„

全角か半角かを気にする

(特にスペース)

„

文字はアスキーでもよい

うまくいけばこんな感じで表示できます

(2)

画像処理とは何か?

†

画像をみやすくする

„

明るさ・コントラスト・色調などの補正

„

回転・サイズの変更

„

ノイズを除去する

†

画像を圧縮する

„

JPEG、MPEG

†

画像を合成する

„

CG

†

画像を認識する

„

文字認識(OCR)

„

物体認識

„

ロボットビジョン(立体視など)

さっそく画像処理

†

と、その前に大事な事・・・

1.

画像の入出力はいつも同じなので

関数

にしておこう!

2.

その際に

BGRをRGBに入れ替えよう!

3.

さらにピクセルが左下からのものを左上からに並べ替えよう!

4.

カラー画像を処理するためには、

RGBそれぞれについて同じ

処理をする必要がある

⇒R、G、Bそれぞれを別の箱にしまった方が便利!

関数化

(ビットマップの読み込み)

unsigned char* LoadBitmap(char* filename, int* width, int* height) {

int i, size; FILE *fp;

unsigned char *buffer; /*入力画像用メモリのポインタ*/ unsigned char tmp_pix;

BITMAPFILEHEADER bmfh; BITMAPINFOHEADER bmih;

fp = fopen( filename, "rb" ); /*ファイルを開く*/

fread ( &bmfh , sizeof(BITMAPFILEHEADER) , 1 , fp ); /*ビットマップのヘッダーを読み込む*/ fread ( &bmih , sizeof(BITMAPINFOHEADER) , 1 , fp );

*width = bmih.biWidth; *height = bmih.biHeight;

size = (*width) *( *height); /*ビットマップのサイズを算出*/ buffer = (unsigned char*)malloc( 3*size ); /*必要なサイズのメモリを確保*/

for ( i = (*height) - 1; i >= 0; i-- ) /*上下を入れ替えながら読み込む*/ {

fread( buffer + (*width) * 3 * i, 1, (*width) * 3, fp ); }

fclose( fp ); /*読み終わったのでファイルを閉じる*/ for ( i = 0; i < size; i++ ) /*BGRをRGBへ変換(入れ替え)*/ { tmp_pix = buffer[ i * 3 ]; buffer[ i * 3 ] = buffer[ i * 3 + 2 ]; buffer[ i * 3 + 2 ] = tmp_pix; } return buffer; } p9-1.c

関数化

(ビットマップの書き込み)

次のページに続く

void SaveBitmap(char* filename, unsigned char* buffer, int width, int height) { int i; FILE *fp; BITMAPFILEHEADER bmfh; BITMAPINFOHEADER bmih; char *tmp;

unsigned char tmp_pix; /*ファイルを書き込み用で開く*/ fp = fopen( filename , "wb" ); /*出力画像のヘッダを生成する*/ memset(&bmfh,0, sizeof(bmfh)); memset(&bmih,0, sizeof(bmih)); tmp = (char*) &(bmfh.bfType); tmp[0]='B'; tmp[1]='M'; bmfh.bfOffBits = BMP_HEADER_SIZE;

(3)

関数化

(ビットマップの書き込み)続き

bmih.biSize=BMP_HEADER_SIZE - 14; bmih.biWidth = width; bmih.biHeight = height; bmih.biPlanes = 1; bmih.biBitCount = 24; bmih.biCompression = 0; bmih.biSizeImage = width*height*3; /*RGBをBGRに変換する*/ for ( i = 0; i < width * height; i++ ) {

tmp_pix = buffer[ i * 3 ]; buffer[ i * 3 ] = buffer[ i * 3 + 2 ]; buffer[ i * 3 + 2 ] = tmp_pix; }

fwrite( &bmfh , sizeof(BITMAPFILEHEADER) , 1 , fp ); /*ヘッダを書き込む*/ fwrite( &bmih , sizeof(BITMAPINFOHEADER) , 1 , fp );

for ( i = height - 1; i >= 0; i-- ) /*上下を入れ替えながら書き込む*/ {

fwrite( buffer + width * 3 * i, 1, width * 3, fp ); } fclose( fp ); /*ファイルを閉じる*/ return; } p9-1.c

関数化 (main関数)

/*ビットマップを開く関数のプロトタイプ宣言*/

unsigned char* LoadBitmap(char* filename, int* width, int* height); /*ビットマップを保存する関数*/

void SaveBitmap(char* filename, unsigned char* buffer, int width, int height); /*メイン関数*/

int main(void) {

int width, height; int n , x , y;

unsigned char *buffer; /*入力画像用メモリのポインタ*/ /*ファイルを開く*/

buffer = LoadBitmap("girl.bmp", &width, &height);

/*****************画像処理をここで行う********************/ /*今はとりあえず何もしない*/

/*******************ここまで*********************************/ /*書き込み処理*/

/*ここでは入力画像をそのまま書き出す*/

SaveBitmap("result.bmp", buffer, width, height);

/*メモリを解放する*/ free( buffer ); return 0; } p9-1.c

RGBをそれぞれ別の箱にしまう!

int main(void) {

int width, height; int n , x , y;

BYTE *buffer; /*入力画像用メモリのポインタ*/ BYTE *Rbuffer, *Gbuffer, *Bbuffer;

/*ファイルを開く*/

buffer = LoadBitmap("girl.bmp", &width, &height);

Rbuffer = (BYTE*)malloc( width*height ); /*メモリ確保*/ Gbuffer = (BYTE*)malloc( width*height );

Bbuffer = (BYTE*)malloc( width*height );

RGB2Plane(buffer, Rbuffer, Gbuffer, Bbuffer, width, height); /*RGBを色平面に分解*/

/*****************画像処理をここで行う********************/ /*今はとりあえず何もしない*/

/*******************ここまで******************************/ /*書き込み処理*/

Plane2RGB(buffer, Rbuffer, Gbuffer, Bbuffer, width, height); /*色平面をRGBに合成*/

SaveBitmap("result.bmp", buffer, width, height); /*メモリを解放する*/ free( buffer ); free( Rbuffer ); free( Gbuffer ); free( Bbuffer ); return 0; } p9-2.c

ミニテスト(問1)

†

空欄を埋めましょう

/*RGBをR,G,Bに分解*/

void RGB2Plane(BYTE* buffer, BYTE* R, BYTE* G, BYTE* B, int width, int height) {

int i;

/*RGBを各色平面へ変換(R,G,Bはアドレス渡しになっていることに注意)*/ for (i=0; i<width*height; i++)

{

} }

/*R,G,Bに分解したものをRGBに合成*/

void Plane2RGB(BYTE* buffer, BYTE* R, BYTE* G, BYTE* B, int width, int height) {

int i;

/*RGBを各色平面へ変換(R,G,Bはアドレス渡しになっていることに注意)*/ for (i=0; i<width*height; i++)

{ buffer[3*i] = R[i]; buffer[3*i+1] = G[i]; buffer[3*i+2] = B[i]; } }

(4)

エラー処理をする

†

「ファイルを読み込む」や「メモリを確保する」などの処理は、仮に失敗すると後

に続く処理ができなくなるという問題がある。

†

もし「ファイルの読み込みに失敗したら」どうするか?

もし「メモリが足りなくて確保できなかったら」どうするか?

を考えておく必要がある。

fp = fopen( "bitmap.bmp" , "rb" );

if(fp==NULL)

{

printf("ファイルの読み込みに失敗!¥n");

return 0;

/*処理を継続できないので終了する*/

}

†

プリントで示すプログラムには、

スペースの関係

でエラー処理を入れていないの

で注意してください

ヘッダファイル

(.h)にしてしまう

†

main関数以外を「mybmp.h」というファイルに移動する

†

main関数のあるファイルの先頭に

#include “mybmp.h”

を追加

(注)mybmpi.hがインテル用、mybmpm.hがモトローラ用

#include <stdio.h> #include <stdlib.h> #include <memory.h> #include "mybmpi.h" /*自分で作ったヘッダーは""で囲む*/ /*メイン関数*/ int main(void) { 全く同じなので省略 } p9-3.c

結局どうなったかというと?

†

#include “mybmp.h”とすることで、特に何もしなくても以

下のようにすることが可能

buffer = LoadBitmap(“xxx.bmp", &width, &height);

Rbuffer = (BYTE*)malloc( width*height );/*メモリ確保*/

Gbuffer = (BYTE*)malloc( width*height );

Bbuffer = (BYTE*)malloc( width*height );

RGB2Plane(buffer, Rbuffer, Gbuffer, Bbuffer, width, height);

/*色平面をRGBに合成*/

Plane2RGB(buffer, Rbuffer, Gbuffer, Bbuffer, width, height);

SaveBitmap("result.bmp", buffer, width, height);

buffer[i] 左上からRGB順 Rbuffer Gbuffer Bbuffer /*本当はこの間で処理をする*/

さあ処理してみよう

†

画像の左右反転

どうすればよいでしょう?

入力画像 出力画像

(5)

各画素にどのようにアクセスするか

†

色々ありえますが、ここでは2重ループを使って次のよう

に考えます

Rbuffer[0] width height

横がx、縦がyのとき

Rbuffer[y*width+x];

Rbuffer[height*width-1]

x

y

for(y=0; y<height; y++){ for(x=0; x<width; x++){ Rbuffer2[y*width+x] = Rbuffer[y*width+x]; } } 実際のメモリ ・ ・ ・

y

x

0 1 2 ここまででwidthが いくつあるか? y*width+x

画像の左右反転

SaveBitmap(); Rbuffer[i] Rbuffer[0] Rbuffer[width-1] Rbuffer[1] width height ファイルから読み込み Rbuffer2[i] width height LoadBmp(); 指定したファイル名 で保存される Rbuffer2[width-1] Rbuffer2[0] Rbuffer2[width-2] Rbuffer[2] Rbuffer[x] Rbuffer2[width-1-x] Rbuffer2[width-3] 左右反転しながらコピー R,G,Bそれぞれ行う Rbuffer[2*width-1] Rbuffer[height*width-1] Rbuffer2[(height-1)*width]

横がx、縦がyのとき

Rbuffer[y*width+x];

横がwidth-1-x、縦がy

Rbuffer2[??];

左右反転プログラム

int main(void) { /*省略*/ /*出力用*/

Rbuffer2 = (BYTE*)malloc( width*height ); /*メモリ確保*/ Gbuffer2 = (BYTE*)malloc( width*height );

Bbuffer2 = (BYTE*)malloc( width*height );

/*****************画像処理をここで行う********************/ for(y=0; y<height; y++){

for(x=0; x<width; x++){ Rbuffer2[(y+1)*width-1-x] = Rbuffer[y*width+x]; Gbuffer2[(y+1)*width-1-x] = Gbuffer[y*width+x]; Bbuffer2[(y+1)*width-1-x] = Bbuffer[y*width+x]; } } /*******************ここまで******************************/ /*書き込み処理*/

Plane2RGB(buffer, Rbuffer2, Gbuffer2, Bbuffer2, width, height); /*bufferは入力のものを転用する*/ SaveBitmap("result.bmp", buffer, width, height);

/*以下省略*/ } p9-4.c 同じ行の最後まで進める:((y+1)*width-1) x個だけ前に戻る:-x

上下の入れ替えも同じ様に

int main(void) { /*省略*/ /*出力用*/

Rbuffer2 = (BYTE*)malloc( width*height ); /*メモリ確保*/ Gbuffer2 = (BYTE*)malloc( width*height );

Bbuffer2 = (BYTE*)malloc( width*height );

/*****************画像処理をここで行う********************/ for(y=0; y<height; y++){

for(x=0; x<width; x++){ Rbuffer2[(height-y-1)*width+x] = Rbuffer[y*width+x]; Gbuffer2[(height-y-1)*width+x] = Gbuffer[y*width+x]; Bbuffer2[(height-y-1)*width+x] = Bbuffer[y*width+x]; } } /*******************ここまで******************************/ /*書き込み処理*/

Plane2RGB(buffer, Rbuffer2, Gbuffer2, Bbuffer2, width, height); /*bufferは入力のものを転用する*/ SaveBitmap("result.bmp", buffer, width, height);

/*以下省略*/

(6)

ミニテスト(問2)

†

カメラをたてにして撮った次の画像を90度回転したい

Rbuffer[width-1] Rbuffer2[0]

幅と高さの関係は?

Rbuffer[0] Rbuffer2[width2*(height2-1)] x方向 y 方 向 x 方 向 y方向 入れ替わる

ミニテスト(問2) 続き

int main(void) { /*省略*/

int width2, height2;

/*****************画像処理をここで行う********************/

width2=height; height2=width;

for(y=0; y<height; y++){ for(x=0; x<width; x++){

} }

/*******************ここまで******************************/ /*書き込み処理*/

Plane2RGB(buffer, Rbuffer2, Gbuffer2, Bbuffer2, width2, height2);/*bufferは入力のものを転用する*/ SaveBitmap("result.bmp", buffer, width2, height2);

/*以下省略*/ }

画像を縮小する

†

画像を小さくするにはどうすればよいでしょう?

†

例えば1/2のサイズ(縦横それぞれ1/2)にするには?

†

本当はフィルタをかける必要がある(サンプリング定理)

†

2/3倍などはちょっと難しいのでここでは扱わない

画像を縮小する(続き)

†

一つおきに画素を取り出す(残りは捨てる)

†

本当はフィルタをかける必要がある(サンプリング定理)

†

2/3倍などはちょっと難しいのでここでは扱わない

(7)

画像を縮小するプログラム

int main(void) { int ratio; /*省略*/ ratio=2; /*倍率*/ height2=height/ratio; width2=width/ratio; /*出力用*/

Rbuffer2 = (BYTE*)malloc( width2*height2 ); /*メモリ確保*/ Gbuffer2 = (BYTE*)malloc( width2*height2 );

Bbuffer2 = (BYTE*)malloc( width2*height2 );

/*****************画像処理をここで行う********************/ for(y=0; y<height2; y++){

for(x=0; x<width2; x++){ Rbuffer2[y*width2+x] = Rbuffer[y*width*ratio+x*ratio]; Gbuffer2[y*width2+x] = Gbuffer[y*width*ratio+x*ratio]; Bbuffer2[y*width2+x] = Bbuffer[y*width*ratio+x*ratio]; } } /*******************ここまで******************************/ /*省略*/ } p9-7.c

画像を拡大する

†

画像を拡大するにはどうすればよい?

†

縮小の逆をやればいいのでは?

画像を拡大する (続き)

†

画素を一つおきに埋めてゆく

†

あれれ、足りない

†

白いところはどうしよう?

†

白いところの埋め方は色々な方法がある

†

間を埋めることを

補間

という

補間の仕方

†

同じ画素値をコピーする(画素を膨らませる)

補間したきりんちゃん オリジナルのきりんちゃん

(8)

補間の仕方 (線形補間)

†

周りの画素の情報を使う

†

画像は滑らかなので、周りの画素に近い値をとる可能性が高い

周囲4画素の平均 線形補間の結果 p 1-p q 1-q 画素値=(1-p){(1-q)a+ qb} +p{(1-q)c+qd} a b c d a,b,c,d:小さな画像の画素値

補間のプログラム

int main(void) { int ratio; /*省略*/ ratio=2; height2=height/ratio; width2=width/ratio; /*出力用*/

buffer2 = (BYTE*)malloc( width2*height2*3 );/*メモリ確保*/

Rbuffer2 = (BYTE*)malloc( width2*height2 ); Gbuffer2 = (BYTE*)malloc( width2*height2 ); Bbuffer2 = (BYTE*)malloc( width2*height2 );

/*****************画像処理をここで行う********************/ for(y=0; y<height2; y++){

for(x=0; x<width2; x++){

Rbuffer2[y*width2+x] = Rbuffer[((int) (y/ratio))*width + (int) (x/ratio)]; Gbuffer2[y*width2+x] = Gbuffer[((int) (y/ratio))*width + (int) (x/ratio)]; Bbuffer2[y*width2+x] = Bbuffer[((int) (y/ratio))*width + (int) (x/ratio)];

} } /*******************ここまで******************************/ /*省略*/ }

同じ画素値を埋める方法のプログラム

p9-8.c (int) という明示的なキャストは なくてもOK。分かりやすいように つけてある。(

この丸めが実はポイント

参照

関連したドキュメント

Subjective test results show that the proposed echo canceller achieved a 0.37 point higher grading dif- ference in the ITU-R five-grade impairment scale than the conventional

の点を 明 らか にす るに は処 理 後の 細菌 内DNA合... に存 在す る

物語などを読む際には、「構造と内容の把握」、「精査・解釈」に関する指導事項の系統を

BRAdmin Professional 4 を Microsoft Azure に接続するには、Microsoft Azure のサブスクリプションと Microsoft Azure Storage アカウントが必要です。.. BRAdmin Professional

LLVM から Haskell への変換は、各 LLVM 命令をそれと 同等な処理を行う Haskell のプログラムに変換することに より、実現される。

クチャになった.各NFは複数のNF  ServiceのAPI を提供しNFの処理を行う.UDM(Unified  Data  Management) *11 を例にとれば,UDMがNF  Service

 

※1 多核種除去設備或いは逆浸透膜処理装置 ※2 サンプルタンクにて確認するが、念のため、ガンマ線を検出するモニタを設置する。