4.1MB

Processingによる画像処理プログラミング
画像情報処理
http://www.vision.cs.chubu.ac.jp/P5/
藤吉弘亘 (Hironobu Fujiyoshi)
E-mail: [email protected]
Twitter: twitter.com/hf149
1
processing
•
JAVAをベースに開発されたプログラミング環境
– Ben Fry(MIT Media Lab)
– Casey Reas (Design ¦ Media Arts Department at
UCLA)
– ビジュアライゼーションやメディアアートの作品制作等に
利用
See→http://processing.org/
2
processingのダウンロード
– http://processing.org/download/より使用するOSに合った
ものをダウンロード
Windows:解凍後、CもしくはDドライブへコピー
Mac:解凍後、アプリケーションフォルダへコピー
3
processingのインターフェース
実行(play)ボタン
プログラムを実行する際に使用
停止(stop)ボタン
プログラムを停止する際に使用
新規作成(new)ボタン
新しいファイルのことをProcessingではスケッチと呼ぶ
読込み(Opens)ボタン
スケッチを読み込む
保存(Saves)ボタン
インターフェイス上に表示しているスケッチに名前を
つけて保存する際に使用
出力(Exports)ボタン
表示されているスケッチをJavaアプレットとして出力
またその際にJavaアプレットを表示するために必要な
最低限のHTMLタグを書き出す
4
ため,作品をそのまま
上で公開できる簡易さも備えている.
カメラや
ムー
ビーからの読み込みが簡易であり,簡単な動画像処理プログラミングにも適しているといえる.
操作インターフェイス
processingの実行
図
は
のインターフェイスを示している.
図
のインターフェイス
5
URLs
→ http://www.processing.org/
processingのサイト
→ http://www.vision.cs.chubu.ac.jp/P5/
講義資料アーカイブページ
6
Books
Getting Started with Processing
Casey Reas and Ben Fry.
Processing: A Programming Handbook
for Visual Designers and Artists
Casey Reas and Ben Fry
Built with Processing
前川峻志 and 田中孝太郎
7
画像の表示:画像ファイルの用意
1.
2.
imageという名前のスケッチを作成
Sketch→Add Fileより画像を選択
(画像は、jpgまたはgif形式のみ)
スケッチフォルダ内にdataフォルダが作成される
C:¥ Documents and Setteing¥ USER¥ My Documents
8
画像の表示:image( )
PImage b; //外部変数
void setup(){
size(300,300);
b = loadImage("woman.jpg");
}
(0,0)
(150,150)
void draw()
{
//画像を座標(0,0)に表示
image(b, 0, 0);
//画像を(150,150)にサイズ150 150で表示
image(b, 150, 150, 150, 150);
}
PImageはイメージを保持するためのデータタイプ。image関数が実際に画像をスクリーンに描画する。
image(PImage, x, y, width, height);
画像の表示サイズを決める高さ(height)と横幅(width)については省略することが可能である。省略した場
合は元画像のサイズが適用される。
9
画像サイズの取得:image( )
PImage b;
(0,0)
void setup(){
b = loadImage("woman.jpg");
size(b.width, b.height);
}
void draw()
{
image(b, 0, 0);
}
外部変数としてbを登録しておくと、どこからも使用可能
画像(woman.jpg)をロードした際、width, heightにその画像サイズが代入される
→これを利用してスケッチのサイズを決定すれば、サイズが違う画像に入れ変えても自動的に反映される
10
ピクセルデータの取得:get( )
get()は座標(x, y)のピクセルのRGB値を取得
get(x, y)
color c = get(150, 160);
→ RGBカラーを扱う変数cに(255, 200, 131)が代入
・座標(150, 160)の赤色の値だけを取り出すには
int a = red(get(150, 160));
→ aに255が代入される
・座標(150, 160)の緑色の値だけを取り出すには
int a = green(get(150, 160));
→ aに200が代入される
・座標(150, 160)の青色の値だけを取り出すには
int a = blue(get(150, 160));
→ aに131が代入される
11
ピクセルデータのセット:set( )
set()は座標(x, y)のピクセルにRGB値をセット
set(x, y, c)
color c = color(255, 255, 255); → RGBカラー変数cに白色(255,255,255)を代入
set(150, 160, c);
→ 座標(150,160)にカラー変数Cの値をセット
12
画像ピクセルデータの取得:get( )
PImage b;
(0,0)
void setup(){
b = loadImage("woman.jpg");
size(b.width, b.height);
}
void draw()
{
background(255);
for(int i=0; i<b.height; i+=2){
for(int j=0; j<b.width; j+=2){
set(j, i, b.get(j,i));
}
}
}
13
画像ピクセルデータの取得:pixel[ ]
PImage b;
(0,0)
void setup(){
b = loadImage("woman.jpg");
size(b.width, b.height);
}
void draw()
{
background(255);
for(int i=0; i<b.height; i+=2){
for(int j=0; j<b.width; j+=2){
int pos = j * b.width + i;
set(j, i, b.pixel[pos]);
}
}
}
14
反転:Inverse
PImage b;
void setup(){
b = loadImage("woman.jpg");
size(b.width, b.height);
}
void draw()
{
color c;
image(b, 0, 0);
for(int y=0; y<b.height; y++){
for(int x=0; x<b.width; x++){
c = color(255-red(get(x,y)), 255-green(get(x,y)), 255-blue(get(x,y)));
set(x, y, c);
}
}
}
http://www.vision.cs.chubu.ac.jp/p5/app/04/04/
15
2値化:Binarization
PImage b;
void setup(){
b = loadImage("woman.jpg");
size(b.width,b.height);
}
void draw()
{
color c;
image(b, 0, 0);
for(int y=0; y<b.height; y++){
for(int x=0; x<b.width; x++){
if(red(get(x,y)) > 200){
c = color(255,255,255);
}
else {
c = color(0,0,0);
}
set(x, y, c);
}
}
}
http://www.vision.cs.chubu.ac.jp/p5/app/04/05/
16
画像フィルタによる平滑化
PImage a;
void MovingAverageFilter(PImage F, PImage G, int m)
PImage b;
{
for(int y=0; y<F.height; y++) {
void setup(){
for(int x=0; x<F.width; x++) {
a = loadImage("woman.jpg");
int sum=0;
b = a.copy();
int cc=0;
size(b.width,b.height);
for(int j=-m; j<m+1; j++){
}
for(int i=-m; i<m+1; i++){
if(0 < x+i && x+i < F.width &&
int gray(color c){
0 < y+j && y+j < F.width){
float r = red(c);
sum += gray(F.get(x+i,y+j));
float g = green(c);
cc++;
float b = blue(c);
}
return((int)(0.299*r + 0.587*g + 0.114*b));
}
}
}
void draw()
int c = sum/cc;
{
G.set(x,y,color(c,c,c));
background(255);
}
MovingAverageFilter(b,a,2);
image(a, 0, 0);
}
}
}
http://www.vision.cs.chubu.ac.jp/p5/app/04/11/
17
処理結果
m=2
m=5
18
ラプラシアンフィルタによる鮮鋭化
PImage a;
void LapracianFilter(PImage F, PImage G)
PImage b;
{
int m=1;
void setup(){
int[][] H = { {0, 1, 0},
a = loadImage("woman.jpg");
{1, -4, 1},
b = a.copy();
{0, 1, 0} };
size(b.width,b.height);
}
for(int j=m; j<F.height-m; j++) {
for(int i=m; i<F.width-m; i++){
int gray(color c){
int sum = 0;
float r = red(c);
for(int k=-m; k<(m+1); k++) {
float g = green(c);
for(int l=-m; l<(m+1); l++){
float b = blue(c);
sum += grey(F.get(i+k,j+l)) * H[k+m][l+m];
return((int)(0.299*r + 0.587*g + 0.114*b));
}
}
}
void draw()
int c = grey(b.get(i,j)) - sum;
{
G.set(i,j,color(c,c,c));
background(255);
}
LapracianFilter(a,b);
image(b,0,0);
}
}
}
http://www.vision.cs.chubu.ac.jp/p5/app/04/10/
19
処理結果
元画像
ラプラシンフィルタによる鮮鋭化
20
コンボリューション
float [][]Mask={ {1.0/9, 1.0/9, 1.0/9},
{1.0/9, 1.0/9, 1.0/9},
{1.0/9, 1.0/9, 1.0/9} };
void Convolution(PImage F, PImage G, float h[][], int M, int N){
for(int j=0; j<F.height; j++){
for(int i=0; i<F.width; i++){
float r=0, g=0, b=0;
for(int l=-N; l<N+1; l++){
for(int k=-M; k<M+1; k++){
1/9
1/9
1/9
1/9
1/9
1/9
1/9
1/9
1/9
フィルタ(2次元配列)
if(0 < i+k && i+k < F.width && 0 < j+l && j+l < F.height){
r += red(
F.get(i+k, j+l)) * h[l+N][k+M];
g += green(F.get(i+k, j+l)) * h[l+N][k+M];
b += blue( F.get(i+k, j+l)) * h[l+N][k+M];
}
}
}
color c = color(r, g, b);
G.set(i, j, c);
}
}
コンボリューション
}
http://www.vision.cs.chubu.ac.jp/p5/app/ImageProcessing/01/
21
コンボリューション
PImage a, b;
void setup()
{
a = loadImage("NGO.jpg");
b = a.get();
size(b.width*2, b.height);
Convolution(a, b, Mask, Mask[0].length/2, Mask.length/2);
}
void draw()
{
Mask.length
↓
image(a, 0, 0);
3/2=1
image(b, a.width, 0);
}
Mask[0].length→
5/2=2
1/15
1/15
1/15
1/15
1/15
1/15
1/15
1/15
1/15
1/15
1/15
1/15
1/15
1/15
1/15
M=2, N=1
Mask[3][5]の場合
22
処理結果1:平均フィルタによる平滑化
1/25
1/25
1/25
1/25
1/25
1/25
1/25
1/25
1/25
1/25
1/25
1/25
1/25
1/25
1/25
1/25
1/25
1/25
1/25
1/25
1/25
1/25
1/25
1/25
1/25
23
処理結果2 : ガウシンフィルタによる平滑化
1/
4/
6/
4/
1/
256
256
256
256
256
4/
16/
24/
116/
4/
256
256
256
256
256
6/
24/
36/
24/
6/
256
256
256
256
256
4/
16/
24/
16/
4/
256
256
256
256
256
1/
4/
6/
4/
1/
256
256
256
256
256
24
処理結果3:特定方向フィルタによる平滑化
0
0
0
0
1/7
0
0
0
1/7
0
0
0
1/7
0
0
0
1/7
0
0
0
1/7
0
0
0
0
25
処理結果4:ラプラシアンフィルタによる先鋭化
-1
-1
-1
-1
9
-1
-1
-1
-1
26
TiltShift Generator
ミニチュア風写真の原理:
http://d.hatena.ne.jp/htee2006/20090928/1254122943
被写界深度情報とシーンの意味的情報のズレを利用
して、脳を騙すこと。
本来は、レンズを傾けられるティルトレンズを用い
て、上下、あるいは左右の被写界深度を変えて撮影
する。
ティルトレンズ
iOSアプリ→http://itunes.apple.com/jp/app/tiltshift-generator-minichua/id327716311?mt=8
27
ProcessingでTiltShift Generator
平滑化度合:大
平滑化度合:小
m=0
平滑化度合:小
平滑化度合:大
28
P5TiltShiftGenerator 01
PImage a, b;
boolean ON = false;
void draw()
{
background(255);
void setup()
if(ON){
{
TiltShiftGenerator(a, b, mouseY, 30);
a = loadImage("TYO.jpg");
ON = false;
b = a.get();
}
size(b.width, b.height);
image(b, 0, 0);
//size(600, 300);
}
draw_line(mouseY, 30);
}
void draw_line(int y, int w)
{
if(w < y && y < height-w){
stroke(255, 255, 255, 100);
line (0,y-w,width,y-w);
void mousePressed()
{
ON = true;
}
line (0,y+w,width,y+w);
}
}
http://www.vision.cs.chubu.ac.jp/p5/app/ImageProcessing/02/
29
P5TiltShiftGenerator 02
void TiltShiftGenerator(PImage F, PImage G, int center, int w)
{
for(int y=0; y<F.height; y++){
for(int x=0; x<F.width; x++){
if(y < center-w){
MovingAverageFilter_ColorImage(F, G, x, y, ((center-w)-y)/20+1,((center-w)-y)/20+1);
}
else if(y < (center+w)-10){
G.set(x,y,F.get(x,y));
}
else{
MovingAverageFilter_ColorImage(F, G, x, y, (y-(center+w))/20+1,(y-(center+w))/20+1);
}
}
}
}
30
P5TiltShiftGenerator 03
void MovingAverageFilter_ColorImage(PImage F, PImage G, int x, int y, int m, int n)
{
int sum_r = 0;
int sum_g = 0;
int sum_b = 0;
int cc = 0;
for(int j=-n; j<n+1; j++){
for(int i=-m; i<m+1; i++){
if(0 < x+i && x+i < F.width && 0 < y+j && y+j < F.width){
sum_r += red(F.get(x+i, y+j));
sum_g += green(F.get(x+i, y+j));
sum_b += blue(F.get(x+i, y+j));
cc ++;
}
}
}
color c = color(sum_r/cc, sum_g/cc, sum_b/cc);
G.set(x, y, c);
}
31
移動平均の高速化:積分画像の利用
• 矩形領域の輝度値の和を高速に算出可能
O
I(i, j)
B
D
S = A-B-C+D
S
(i, j)
C
A
32
P5TiltShiftGenerator(積分画像)01
PImage a, b;
for(int x=1; x<F.width; x++){
int ii_r[][], ii_g[][], ii_b[][];
ii_r[0][x] = ii_r[0][x-1] + (int)red(F.get(x,0));
ii_g[0][x] = ii_r[0][x-1] + (int)green(F.get(x,0));
boolean ON = false;
ii_b[0][x] = ii_r[0][x-1] + (int)blue(F.get(x,0));
}
void setup()
for(int y=1; y<F.height; y++){
{
int r=(int)red(F.get(0,y));
a = loadImage("TYO.jpg");
int g=(int)green(F.get(0,y));
b = a.get();
int b=(int)blue(F.get(0,y));
ii_r = new int[a.height][a.width];
for(int x=1; x<F.width; x++){
ii_g = new int[a.height][a.width];
r += (int)red(F.get(x,y));
ii_b = new int[a.height][a.width];
g += (int)green(F.get(x,y));
size(541, 541);
b += (int)blue(F.get(x,y));
IntegralImage(a, ii_r, ii_g, ii_b);
ii_r[y][x] = ii_r[y-1][x] + r;
}
ii_g[y][x] = ii_g[y-1][x] + g;
ii_b[y][x] = ii_b[y-1][x] + b;
void IntegralImage(PImage F, int ii_r[][], int ii_g[][],
}
int ii_b[][]){
}
ii_r[0][0] = (int)red(F.get(0,0));
}
ii_g[0][0] = (int)green(F.get(0,0));
ii_b[0][0] = (int)blue(F.get(0,0));
http://www.vision.cs.chubu.ac.jp/p5/app/ImageProcessing/03/
33
P5TiltShiftGenerator(積分画像) 02
void draw_line(int y, int w)
void TiltShiftGenerator(PImage F, PImage G, int
{
center, int w)
if(w < y && y < height-w){
{
stroke(255, 255, 255, 100);
for(int y=0; y<F.height; y++){
line (0,y-w,width,y-w);
for(int x=0; x<F.width; x++){
line (0,y+w,width,y+w);
if(y < center-w){
}
MovingAverageFilter_ColorImage(F, G, x, y,
}
((center-w)-y)/20+1,((center-w)-y)/20+1);
void draw()
}
{
else if(y < center+w){
background(255);
G.set(x,y,F.get(x,y));
if(ON){
}
TiltShiftGenerator(a, b, mouseY, 30);
else{
ON = false;
}
MovingAverageFilter_ColorImage(F, G, x, y, (y(center+w))/20+1,(y-(center+w))/20+1);
image(b, 0, 0);
}
draw_line(mouseY, 40);
}
}
}
void mousePressed()
}
{
ON = true;
}
34
P5TiltShiftGenerator(積分画像) 02
void MovingAverageFilter_ColorImage(PImage F,
else {
PImage G, int x, int y, int m, int n)
for(int j=-n; j<n+1; j++){
{
for(int i=-m; i<m+1; i++){
int sum_r = 0;
int sum_g = 0;
if(0 < x+i && x+i < F.width && 0 < y+j && y+j <
F.width){
int sum_b = 0;
sum_r += red(F.get(x+i, y+j));
int cc = 0;
sum_g += green(F.get(x+i, y+j));
color c;
sum_b += blue(F.get(x+i, y+j));
cc ++;
if(0<x-m && 0<y-n && x+m < F.width && y+n <
}
F.height){
}
sum_r = ii_r[y+n][x+m] - ii_r[y-n][x+m] - ii_r[y+n]
}
[x-m] + ii_r[y-n][x-m];
c = color(sum_r/cc, sum_g/cc, sum_b/cc);
sum_g = ii_g[y+n][x+m] - ii_g[y-n][x+m] - ii_g[y
}
+n][x-m] + ii_g[y-n][x-m];
sum_b = ii_b[y+n][x+m] - ii_b[y-n][x+m] - ii_b[y
G.set(x, y, c);
}
+n][x-m] + ii_b[y-n][x-m];
c = color(sum_r/((2*m)*(2*n)), sum_g/
((2*m)*(2*n)), sum_b/((2*m)*(2*n)));
}
35