チーム17F1
課題名
アベレージフィルタを用いた画像の平滑化と,FFTによるスペクトル検出
研究者名
3-15-32 Yuichiro Suzuki
概要
画像に4ポイントのアベレージフィルタを繰り返し適用し,平滑化を行う(参考:チーム17C3).
また,これらの画像をFFTによって解析し,平滑化の前後で検出スペクトルがどのように変化するか観察する(参考:チーム15C2).
言語にはJavaを使用した.
ソースコード
Complex.java(参照:チーム15C2)
FFT.java(参照:チーム15C2)
FFT2D.java(参照:チーム15C2)
ImageFFT.java
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
/**
* 2次元FFTを用いて、グレースケール画像のパワースペクトルを表示する */
public class ImageFFT {
/** * mainメソッド。 * 画像を読み込み、アベレージフィルタを適用してからFFTの結果をファイルに出力する。 */ public static void main(String[] args) { try { // 画像の読み込み BufferedImage src = ImageIO.read(new File("src.jpg")); ImageIO.write(transform(src), "jpg", new File("dst.jpg"));
// アベレージフィルタを1回通してからFFTを施す BufferedImage src1 = toAvarageFilter(src, 4); ImageIO.write(src1, "jpg", new File("src1.jpg")); ImageIO.write(transform(src1), "jpg", new File("dst1.jpg"));
// アベレージフィルタを10回通してからFFTを施す BufferedImage src10 = src; for (int i = 0; i < 10; i++) src10 = toAvarageFilter(src10, 4); ImageIO.write(src10, "jpg", new File("src10.jpg")); ImageIO.write(transform(src10), "jpg", new File("dst10.jpg"));
// アベレージフィルタを100回通してからFFTを施す BufferedImage src100 = src; for (int i = 0; i < 100; i++) src100 = toAvarageFilter(src100, 4); ImageIO.write(src100, "jpg", new File("src100.jpg")); ImageIO.write(transform(src100), "jpg", new File("dst100.jpg"));
// アベレージフィルタを1000回通してからFFTを施す BufferedImage src1000 = src; for (int i = 0; i < 1000; i++) src1000 = toAvarageFilter(src1000, 4); ImageIO.write(src1000, "jpg", new File("src1000.jpg")); ImageIO.write(transform(src1000), "jpg", new File("dst1000.jpg"));
} catch (IOException e) { e.printStackTrace(); } }
/** * 入力画像をFFTにかけて、解析スペクトルを画像として返す。 * * @param src 入力画像 * @return 解析スペクトルの画像 */ private static BufferedImage transform(BufferedImage src) { // 画像をグレースケールに変換 BufferedImage gray = toGrayScale(src);
// 画像を2の累乗の解像度にリサイズする BufferedImage expandedSrc = resizePowerOfTwo(gray);
// 画像を複素数配列に変換して,離散フーリエ変換する Complex c[][] = imageToComplexArray(expandedSrc); FFT2D ft = new FFT2D(c.length, c[0].length); ft.fft(c);
// 得られた周波数領域の複素数二次配列を,画像に変換する return toFreq(c); }
/** * srcにnタップのアベレージフィルタを適用し、得られた画像を返す * * @param src * 入力画像 * @param n * フィルタのタップ数 * @return フィルタ適用後の画像 */ private static BufferedImage toAvarageFilter(final BufferedImage src, int n) { final int width = src.getWidth(); final int height = src.getHeight(); final BufferedImage dest = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
if (n % 2 == 1) n++; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { int aveR = 0, aveG = 0, aveB = 0; for (int k = -n / 2; k < n / 2; k++) { int rgb; if (j + k >= width | | j + k < 0) { rgb = src.getRGB(j, i); } else { rgb = src.getRGB(j + k, i); } int r = (rgb & 0x000000FF) >> 0; int g = (rgb & 0x0000FF00) >> 8; int b = (rgb & 0x00FF0000) >> 16; aveR += r; aveG += g; aveB += b; } for (int k = 0 - n / 2; k < n / 2; k++) { int rgb; if (i + k >= height | | i + k < 0) { rgb = src.getRGB(j, i); } else { rgb = src.getRGB(j, i + k); } int r = (rgb & 0x000000FF) >> 0; int g = (rgb & 0x0000FF00) >> 8; int b = (rgb & 0x00FF0000) >> 16; aveR += r; aveG += g; aveB += b; } aveR /= 2 * n; aveG /= 2 * n; aveB /= 2 * n; int rgb = aveR | (aveG << 8) | (aveB << 16); dest.setRGB(j, i, rgb); } } return dest; }
/** * nより大きい最少の2の累乗の値を返す. */ private static int smallestPowerOfTwolargerThan(final int n) { int i; for (i = 1; i < n; i *= 2) ; return i; }
/** * 画像の解像度が2の累乗になるようリサイズする. */ private static BufferedImage resizePowerOfTwo(final BufferedImage src) { assert src != null;
final int srcWidth = src.getWidth(); final int srcHeight = src.getHeight(); final int dstWidth = smallestPowerOfTwolargerThan(srcWidth); final int dstHeight = smallestPowerOfTwolargerThan(srcHeight);
final BufferedImage dst = new BufferedImage(dstWidth, dstHeight, BufferedImage.TYPE_INT_BGR);
for (int i = 0; i < srcHeight; i++) { for (int j = 0; j < srcWidth; j++) { dst.setRGB(j, i, src.getRGB(j, i)); } }
return dst; }
/** * 画像を二次元複素数配列に変換する. RGBのうち,B成分のみに着目して変換する. */ private static Complex[][] imageToComplexArray(final BufferedImage img) { assert img != null;
final int width = img.getWidth(); final int height = img.getHeight();
BufferedImage gray = toGrayScale(img);
Complex c[][] = new Complex[height][width]; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { int sig = gray.getRGB(j, i) & 0xFF; c[i][j] = new Complex(sig); } } return c; }
/** * 画像をグレイスケールに変換する. */ private static BufferedImage toGrayScale(final BufferedImage src) { assert src != null;
final int width = src.getWidth(); final int height = src.getHeight(); final BufferedImage dst = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { int rgb = src.getRGB(j, i); int r = (rgb & 0x000000FF) >> 0; int g = (rgb & 0x0000FF00) >> 8; int b = (rgb & 0x00FF0000) >> 16; int y = (int) (0.299 * r + 0.587 * g + 0.114 * b); int gray = y | (y << 8) | (y << 16); dst.setRGB(j, i, gray); } } return dst; }
/** * 二次元複素数配列のうち,ノルムの最大値を返す. */ private static double maxNormOf(Complex x[][]) { assert x != null;
double max = x[0][0].norm(); for (int i = 0; i < x.length; i++) { for (int j = 0; j < x[i].length; j++) { double tmp = x[i][j].norm(); if (max < tmp) { max = tmp; } } } return max; }
/** * 複素数配列を画像に変換する. 複素数のノルムをもとに画像を生成する. ノルムの大きさをHSB色空間の色相で表現する. * 大きい値は赤,小さい値は青で示す. */ private static BufferedImage toFreq(final Complex[][] x) { assert x != null;
final int width = x[0].length; final int height = x.length;
final BufferedImage dst = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
double max = maxNormOf(x); double k = 1.0 / Math.sqrt(max); for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { double rate = k * Math.sqrt(x[i][j].norm() * 128); if (rate > 1) { rate = 1; } int rgb = Color.HSBtoRGB((float) (0.66 * (1 - rate)), 1.0f, 1.0f); dst.setRGB(j, i, rgb); } } return dst; }
}
実行結果
元画像
(95KB)(172KB)
平滑化(1回)
(35KB)(51KB)
平滑化(10回)
(21KB)(25KB)
平滑化(100回)
(13KB)(13KB)
平滑化(1,000回)
(5KB)(5KB)
考察
アベレージフィルタをかけ続ける事で,検出スペクトルの数が減少していくことがわかる.
このことから,アベレージフィルタはLPFの役割を果たしているといえる.
最終的には画像は単色となり,スペクトルは検出できなくなる.
これは,アベレージフィルタにより信号の振幅が減少し,最終的に定数となる特性に由来する.
また,アベレージフィルタの適用回数が増えるにつれて,画像のファイルサイズは小さくなっていった.
したがって,検出スペクトル数が少ないほど,ファイルサイズも減少するとわかる.
以上より,画像のファイルサイズを圧縮するには,FFT結果に応じて適切なスペクトルを削除することが重要だと考える.
- 最終更新:2018-01-15 15:48:54