LoginSignup
4
4

More than 5 years have passed since last update.

ポケモンの画像でモザイクアートを作ってみた

Last updated at Posted at 2018-09-02

はじめに

 この記事では入力画像から18匹のポケモンの画像を用いてモザイクアートを自動作成する方法を紹介します。

事前準備

  1. ポケモン図鑑から18匹(aqua,black,blue,fuchsia,silver,green,lime,maroon,navy,olive,purple,red,gray,teal, white,yellow,beige,orangeに色が近いポケモン)を選ぶ。
  2. photoshopなどを使って背景色をつけたら40×40にサイズを調整する。
  3. image/とか適当なディレクトリにポケモンの画像を置く。

処理の大まかな流れ

もっと賢い方法あるかもしれませんが、、

  1. 入力画像を40×40に分割 PhotoMosaic.java
  2. 1でできた画像の色平均をとる GetAverage.java
  3. 2の色平均をもとに減色 ReduceColor.java
  4. 3で選ばれた色のポケモンを配置PhotoMosaic.java

という感じで実装してみます。

実装

  • 画像の読み込み/書き出し部分は省略します。
PhotoMosaic.java
import java.awt.image.*;
import java.awt.Graphics;

public class PhotoMosaic {

    static BufferedImage execute(BufferedImage before_image) {
        int width = before_image.getWidth();
        int height = before_image.getHeight();
        BufferedImage after_image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
        Graphics g = after_image.getGraphics();
        BufferedImage image[] = new BufferedImage[18];
        image[0] = JpegFileReader.read("image/white.png");
        image[1] = JpegFileReader.read("image/fuchsia.png");
        image[2] = JpegFileReader.read("image/lime.png");
        image[3] = JpegFileReader.read("image/blue.png");
        image[4] = JpegFileReader.read("image/black.png");
        image[5] = JpegFileReader.read("image/maroon.png");
        image[6] = JpegFileReader.read("image/olive.png");
        image[7] = JpegFileReader.read("image/silver.png");
        image[8] = JpegFileReader.read("image/red.png");
        image[9] = JpegFileReader.read("image/green.png");
        image[10] = JpegFileReader.read("image/navy.png");
        image[11] = JpegFileReader.read("image/yellow.png");
        image[12] = JpegFileReader.read("image/aqua.png");
        image[13] = JpegFileReader.read("image/gray.png");
        image[14] = JpegFileReader.read("image/purple.png");
        image[15] = JpegFileReader.read("image/teal.png");
        image[16] = JpegFileReader.read("image/beige.png");
        image[17] = JpegFileReader.read("image/orange.png");

        // 切り取った画像を入れておく配列を宣言
        BufferedImage[][] split_image = new BufferedImage[width/40][height/40];

        for ( int i=0; i*40<width; i++ ) {
            for ( int j=0; j*40<height; j++ ) {
                // 元の画像を40*40に切り取る
                split_image[i][j] = before_image.getSubimage( i*40, j*40, 40, 40 );
                // 切り取った画像の色平均をとる
                int color_num = ReduceColor.detectColor(GetAverage.execute(split_image[i][j]));
                g.drawImage(image[color_num], i*40, j*40, null);
            }
        }
        g.dispose();
        return after_image;
  }

}

GetAverage.java
import java.awt.image.*;
import java.awt.Color;

public class GetAverage{
  static int[] execute(BufferedImage image){
    int R = 0, G = 0, B = 0;
    // Average[0]->Red, Average[1]->Green, Average[2]->Blue
    int Average[] = new int[3];

    // 画素のRGB値をとって合計を求める
    for (int i=0; i<40; i++ ) {
      for (int j=0; j<40; j++ ) {
        int tmp_color = image.getRGB(i,j);
        Color color = new Color(tmp_color);
        R += color.getRed();
        G += color.getGreen();
        B += color.getBlue();
      }
    }

    // 画素の平均値を求める
    Average[0] = (int)R/1600;
    Average[1] = (int)G/1600;
    Average[2] = (int)B/1600;
    return Average;
  }
}

ReduceColor.java
public class ReduceColor{

  static int detectColor(int[] Average){
    int R = getNearestValue(Average[0]);
    int G = getNearestValue(Average[1]);
    int B = getNearestValue(Average[2]);
    // 変換する画像の配列の添え字を入れる配列
    int[][][] v = new int[256][256][256];

    v[64][64][64] = 4;    // black
    v[64][64][128] = 10;  // navy
    v[64][64][192] = 3;   // blue
    v[64][64][255] = 3;   // blue

    v[64][128][64] = 9;   // green
    v[64][128][128] = 15; // teal
    v[64][128][192] = 15; // teal
    v[64][128][255] = 3;  // blue

    v[64][192][64] = 2;   // lime
    v[64][192][128] = 15; // teal
    v[64][192][192] = 12; // aqua
    v[64][192][255] = 3;  // blue

    v[64][255][64] = 2;   // lime
    v[64][255][128] = 2;  // lime
    v[64][255][192] = 12; // aqua
    v[64][255][255] = 12; // aqua

    v[128][64][64] = 5;   // maroon
    v[128][64][128] = 14; // purple
    v[128][64][192] = 14; // purple
    v[128][64][255] = 3;  // blue

    v[128][128][64] = 6;  // olive
    v[128][128][128] = 13;// gray
    v[128][128][192] = 3; // blue
    v[128][128][255] = 3; // blue

    v[128][192][64] = 9;  // green
    v[128][192][128] = 2; // lime
    v[128][192][192] = 12;// aqua
    v[128][192][255] = 12;// aqua

    v[128][255][64] = 2;  // lime
    v[128][255][128] = 2; // lime
    v[128][255][192] = 2; // lime
    v[128][255][255] = 12;// aqua

    v[192][64][64] = 8;   // red
    v[192][64][128] = 1;  // fuchsia
    v[192][64][192] = 1;  // fuchsia
    v[192][64][255] = 14; // purple

    v[192][128][64] = 17; // orange
    v[192][128][128] = 16;// beige
    v[192][128][192] = 1; // fuchsia
    v[192][128][255] = 1; // fuchsia

    v[192][192][64] = 11;  // yellow
    v[192][192][128] = 11; // yellow
    v[192][192][192] = 0; // white
    v[192][192][255] = 12;// aqua

    v[192][255][64] = 12; // aqua
    v[192][255][128] = 2; // lime
    v[192][255][192] = 0; // white
    v[192][255][255] = 12;// aqua

    v[255][64][64] = 8;   // red
    v[255][64][128] = 8;  // red
    v[255][64][192] = 1;  // fuchsia
    v[255][64][255] = 1;  // fuchsia

    v[255][128][64] = 17; // orange
    v[255][128][128] = 16;// beige
    v[255][128][192] = 1; // fuchsia
    v[255][128][255] = 1; // fuchsia

    v[255][192][64] = 11; // yellow
    v[255][192][128] = 11;// yellow
    v[255][192][192] = 16;// beige
    v[255][192][255] = 16;// beige

    v[255][255][64] = 11; // yellow
    v[255][255][128] = 11;// yellow
    v[255][255][192] = 0; // white
    v[255][255][255] = 0; // white

    return v[R][G][B];
  }

  public static int getNearestValue(int v){
        int num=0;  // 配列の添え字
        int difference; // 配列値-RGB値vの絶対値
    int[] list = {64,128,192,192};
    // System.out.println("vの値は"+v);
        difference = Math.abs( list[0] - v );
        for ( int i = 1; i < list.length; i++ ) {
            if ( Math.abs( list[i] - v ) < difference ) {
                num = i;
                difference = Math.abs( list[i] - v );
            }
        }
        return list[num];
  }

}

結果

(クリックして拡大)

  • 減色するのが結構難しかった。特に水色だと白だと判断されてうまくいかなかった。
  • 赤系は割といい感じにできた。
  • 18色しかないので風景写真は厳しい。

参考になった記事

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4