Java OpenCv Lv1 〜入門: 写真の表示〜

イントロダクション

前々回に、家計簿アプリのグラフ部分を作成が一区切りついたので今度は入力部分を作成しようとOpenCvに着手しました。

今までにやってきたこと

そして、入力部分は手入力を行いたくないのでアプリケーションに入力させようと思うのです。つまり、アプリのユーザーはレシートの写真をとるだけ(カテゴリの追加などは別です)という仕様にしようと思い現在に至る次第です。

まずは、OpenCv in Javaと、JUnitのセットアップを行いました。

OpenCvを学ぶ

画像から文字を取得することが最優先なのですが、なんだかんだと初めから学ぶことになりそうなので初っ端から学び始めてしまおうと思います。※OpenGLをやった時に途中から学んだけど初めからやり直しました。。。

画像を表示する

上のツイート画像にある様にその本に書いてあることを実行します。よくわからなかったら、とりあえず進んで見れば良いのです。

※危険な事は神経質になった方が良い、痛い目にあいます。。。

そんなわけで、上記の18ぺーじにC/C++で書かれたソースがあります。そいつをJavaに書き換えて実行します。

駄菓子菓子、OpenCV3.XからはImShowメソッドが、HightGUIが使用できない(しない)仕様になったということで。。。

JavaFXで実装します。続きは以下参照でお願いします。

Eclipse SceneBuilderを追加する – PGボックス

www.youtube.com


JavaFX SceneBuilder 〜EclipseとSceneBuilder連携~ – PGボックス

www.youtube.com

関連ページ一覧

  1. Java OpenCV 環境セットアップ(on Mac)
  2. Java OpenCv Lv1 〜入門: 写真の表示〜
  3. Java OpenCV Lv2 〜画像を表示する〜
  4. Java OpenCV Lv3 〜画像の平滑化(smooth())〜
  5. Java OpenCV Lv3 〜画像にガウシアンフィルタ(GaussianBlur())〜
  6. Java OpenCV Lv3 〜画像に中央値フィルタ(medianBlur())〜
  7. Java OpenCV Lv4 〜画像の中身をみてみる〜
  8. Java OpenCV Lv5 〜Matクラスで描画処理〜
  9. Java OpenCV Lv6 〜Matクラスで背景から作成してみる〜
  10. Java OpenCV Lv7 〜MatクラスでEllipseしてみる〜
  11. Java OpenCV Lv9 〜画像編集「足し算」(cvAdd)〜
  12. Java OpenCV Lv9 〜画像編集「引き算」(cvSubtract)〜
  13. Java OpenCV Lv9 〜画像の掛け算〜
  14. Java OpenCV Lv10 〜行列演算Mat#submat()〜
  15. Java OpenCv Lv10〜画像の平均値をだす〜

JavaFX SceneBuilder 〜EclipseとSceneBuilder連携~

イントロダクション

前回(動画です)は、SceneBuilderをインストールしてEclipseから起動しました。

早速使ってみました。

参考サイト;Java OpenCvのチュートリアルサイト

SceneBuilderを起動する

単体でSceneBuilderを起動する(FXMLを作る時)

EclipseからSceneBuilderを開く場合

Eclipseを開き前回作成したプロジェクトからFXMLを見つけます。(src/zenryokuservice/opnecv/fx/OpenCvTest.fxml)

そして、SceneBuilder起動



  1. BorderPaneを追加
  2. 各プロパティを設定する(id, 表示するテキストetc...)
  3. Buttonを追加(各プロパティ設定)
  4. ImageViewerを追加(各プロパティ設定)

下のコードは、SceneBuilderで生成されたコードです。

<Main.java>

import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.fxml.FXMLLoader;

public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        try {
            BorderPane root = (BorderPane)FXMLLoader.load(getClass().getResource("OpenCvTest.fxml"));
            Scene scene = new Scene(root,400,400);
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}

<OpenCvController.java>

public class OpenCvController {

}

これらが完了したら、次はアクション(イベントハンドラ)を実装していきます。

JavaFX関連ページ

  1. JavaFX SceneBuilder 〜EclipseとSceneBuilder連携~
  2. JavaFX SceneBuilder〜ボタンにメソッドを割り当てるワンポイント〜
  3. Java プロコンゲーム 〜見た目の作成(SceneBuilderの使用)〜
  4. Eclipseのインストール〜java学習フロー

Eclipse SceneBuilderを追加する

イントロダクション

OpenCVの学習を始めるのは良いが、JavaFXを使用する様で。。。(画面の確認などのため)

参照したページはOpenCVのチュートリアルです。
仕方ないのでプラグインを追加することにしました。

SceneBuilderのインストール

オラクルのサイトからダウンロードする様です。

スクリーンショット 2018-11-17 15.20.53.png

自分は、Macを使用しているので「dmg」ファイルを選択します。

スクリーンショット 2018-11-17 15.22.31.png

こんな感じでインストールします。

Eclipseでの起動

Eclipseで設定を開き、JavaFXを選択します。すると右のほうにボタンがあるのでそいつをクリックすると。。。

スクリーンショット 2018-11-17 15.25.42.png

ファイルを選択する様に言われますので、先ほどインストールしたApplicationを参照するとSceneBuilderが見つかります。これを選択して設定します。

スクリーンショット 2018-11-17 15.24.35.png

そしてFXMLファイルを右クリックして起動します。

https://www.youtube.com/watch?v=w9BubZIEGdg&feature=youtu.be

 

JavaFX関連ページ

  1. Eclipse SceneBuilderを追加する
  2. JavaFX SceneBuilder 〜EclipseとSceneBuilder連携~
  3. JavaFX SceneBuilder〜ボタンにメソッドを割り当てるワンポイント〜
  4. Java プロコンゲーム 〜見た目の作成(SceneBuilderの使用)〜

関連ページ一覧

Eclipse セットアップ

  1. Java Install Eclipse〜開発ツールのインストール〜
  2. TensorFlow C++環境〜EclipseCDTをインストール〜
  3. Setup OpenGL with JavaJOGLを使う準備 for Eclipse
  4. Eclipse Meven 開発手順〜プロジェクトの作成〜
  5. Java OpenCV 環境セットアップ(on Mac)
  6. Eclipse SceneBuilderを追加する
  7. JavaFX SceneBuilder EclipseSceneBuilder連携~

Java Basic一覧

  1. Java Basic Level 1 〜Hello Java〜
  2. Java Basic Level2 〜Arithmetic Calculate〜
  3. Java Basic Level3 〜About String class〜
  4. Java Basic Level 4〜Boolean〜
  5. Java Basic Level 5〜If Statement〜
  6. Java Basic Summary from Level1 to 5
  7. Java Basic Level 6 〜Traning of If statement〜
  8. Java Basic Level8 〜How to use for statement〜
  9. Java Basic Level 8.5 〜Array〜
  10. Java Basic Level 9〜Training of for statement〜
  11. Java Basic Level 10 〜While statement 〜
  12. Java Basic Swing〜オブジェクト指向〜
  13. Java Basic Swing Level 2〜オブジェクト指向2〜
  14. サンプル実装〜コンソールゲーム〜
  15. Java Basic インターフェース・抽象クラスの作り方
  16. Java Basic クラスとは〜Step2_1〜
  17. Java Basic JUnit 〜テストスイートの作り方〜

Git関連

  1. Java Git clone in Eclipse 〜サンプルの取得〜
  2. Eclipse Gitリポジトリの取得 GitからソースをPullしよう〜
  3. IntelliJ IDEA GitGitリポジトリからクローン〜

2018/11/15 作業日誌

EclipseにOpenCVのセットアップを行う。

  1. jarファイルとc/c++の資源が必要
  2. jarはビルドパス、cはNativeライブラリに登録する

新しい技術かつ、理論的にも理解出来ないフレームワーク(ライブラリ)は基本をしっかり学ぶ方が早い。なので家計簿アプリはちょいと置いておきOpenCVに集中する。

Java Basic JUnit 〜テストスイートの作り方〜

イントロダクション

今までに触れたことのない技術(OpenCv)を使おうと思ったら色々と動かしてみたいのが人情、そんな時に使えるフレームワークの使い方を記載します。

JUnit

Javaで開発をしていたらすぐに目にすると思うのですが、改めて使い方をみてみようと思います。
<作業動画>


手順

  1. EclipseにJUnit4(JARファイル)をビルドパスにつなぐ
  2. テストクラスを作る
  3. 実行する
  4. あとは色々といじって遊ぶ

サンプルコード

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.junit.runners.JUnit4;
import org.opencv.core.Mat;

import zenryokuservice.gui.lwjgl.kakeibo.opnecv.ReceiptCv;

/** JUnit4を使う宣言 */
@RunWith(JUnit4.class) 
public class ReceiptCvTest {
	/** テスト対象のクラスをフィールドで保持する */
	private ReceiptCv test;
	/**
	 * Bforeアノテーションで各テストを実行する前に
	 * 実行するメソッド
	 * テストのためのデータセットを用意したり、クラスの呼び出しを行なったりする
	 */
	@Before
	public void setup() {
		test  = new ReceiptCv();
	}
	/** テストケース1 */
	@Test
	public void test1() {
		test.helloCv();
	}
	/** テストケース2 */
	@Test
	public void test2() {
		// イメージファイルを読んでみる
		Mat matrix = test.loadImg("download-1.jpg");
		System.out.println(matrix.dump());
	}
}

関連ページ一覧

Java Basic

  1. Java Basic Level 1 〜Hello Java〜
  2. Java Basic Level2 〜Arithmetic Calculate〜
  3. Java Basic Level3 〜About String class〜
  4. Java Basic Level 4〜Boolean〜
  5. Java Basic Level 5〜If Statement〜
  6. Java Basic Summary from Level1 to 5
  7. Java Basic Level 6 〜Traning of If statement〜
  8. Java Basic Level8 〜How to use for statement〜
  9. Java Basic Level 8.5 〜Array〜
  10. Java Basic Level 9〜Training of for statement〜
  11. Java Basic Level 10 〜While statement 〜
  12. Java Basic Swing〜オブジェクト指向〜
  13. Java Basic Swing Level 2〜オブジェクト指向2〜
  14. サンプル実装〜コンソールゲーム〜
  15. Java Basic インターフェース・抽象クラスの作り方
  16. Java Basic クラスとは〜Step2_1〜
  17. Java Basic JUnit 〜テストスイートの作り方〜

Java OpenCv Hello in Java〜OpenCv事始め〜

イントロダクション

ついに最近流行りの技術「人工知能」への入り口画像解析を始めたいと思います。以前、中途半端にPythonでOpenCvを使ったのですが、すごく簡単に扱えて「あ〜したらこ〜なる」なんてことを考えさせられる。。。そんなフレームワークだと思います。

参考サイト:https://www.tutorialspoint.com/opencv/opencv_reading_images.htm

 

事始めということで

年の瀬も近いのに「事始め」とはおかしな話ですが。。。思い立ったが吉日やらないわけには行きません。
そんなわけで早速サンプルコードを見てみます。

import org.opencv.core.Core; 
import org.opencv.core.Mat;  
import org.opencv.imgcodecs.Imgcodecs;
 
public class ReadingImages {
   public static void main(String args[]) { 
      //Loading the OpenCV core library  
      System.loadLibrary( Core.NATIVE_LIBRARY_NAME ); 
     
      //Instantiating the Imagecodecs class 
      Imgcodecs imageCodecs = new Imgcodecs(); 
     
      //Reading the Image from the file  
      String file ="C:/EXAMPLES/OpenCV/sample.jpg"; 
      Mat matrix = imageCodecs.imread(file);
     
      System.out.println("Image Loaded");     
   } 
}

赤字で書いている部分が気になったのでJavaDocAPIを参照してみます。

参考サイトからコードをパクった感じにして以下の様にコードを組みました。

<読み込んだ画像>


<実行結果>

import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs;

/**
 * OpenCVの機能を実装するクラス。
 * 
 * @author takunoji
 *
 * 2018/11/11
 */
public class ReceiptCv {
	Imgcodecs imgCodecs;
	static {
		System.loadLibrary("opencv_java343");
	}

	public static void main(String[] args) {
		ReceiptCv rece = new ReceiptCv();
		rece.loadImg("download-1.jpg");
	}

	/**
	 * コンストラクタ
	 */
	public ReceiptCv() {
		imgCodecs = new Imgcodecs();
		
	}
	
	private void loadImg(String fileName) {
		Mat matrix = Imgcodecs.imread(getClass().getResource("/sampleImages/" + fileName).getPath());
		System.out.println("type: " + matrix.type());
		System.out.println("dims: " + matrix.dims());
		System.out.println("height: " + matrix.height());
		System.out.println("width: " + matrix.width());
		System.out.println("rows: " + matrix.rows());
		System.out.println("cols: " + matrix.cols());
	}

	public void helloCv() {
		Mat m = Mat.eye(3, 3, CvType.CV_8UC1);
		System.out.println("m = " + m.dump());
	}
}

処理の概要

  1. メインメソッドでこのクラスをインスタンス化、loadImg()に「”download-1.jpg"」を私て実行します
  2. "resources/sampleImages/download-1.jpg"を読み込んでMatを返します、
  3. Matの中身をコンソールに表示 

読み込んだ画像は「レシート」です。家計簿を書く時に使うでしょ?(笑)

関連ページ

  1.  Java OpenCV 環境セットアップ(on Mac)
  2. Java Install Eclipse〜開発ツールのインストール〜
  3. Java Doc読解 BufferedReader
  4. Java Doc 読解〜BufferedWriter〜

 

Java OpenCV 環境セットアップ(on Mac)

インントロダクション

画像解析処理を行いたく思い「OpenCV」をセットアップします。

brewコマンドでインストールなどもできる様ですが、GitでPULLするのが一番早いと思いましたので、そのようにやります。

そもそも。。。

機械学習の理解をしようと色々と試みましたが、撃沈。。。(TensorFlow関連では基本的なこと、モデルの作成の理論までは理解できなかった)ので、OpenCVで機械学習を学ぼうとなりました。

OpenCVには色々な「アルゴリズムを使」うとか、ベクトル、パターン認識、特徴量の計算など「機械学習で必要になるであろう」事が記載されていました。

必要な工程

  1. Eclipseのインストールなどのセットアップ
  2. OpenCVのインストール(ここから記載します)
  3. OpenCVのJARファイルとOpenCVのC言語資源(ソース) インストール
  4. OpenCVの起動確認

OpenCVのインストール(makeコマンドを使う)

コマンドを叩いていけばOKですので。。。

参考サイト :本家家元OpenCVのページです。

参考サイト2:JavaでのOpenCVのページ。Javaでやるのでこちらを参照。

 

以下のコマンドを実行します(gitはインストール済みの場合)

>git clone git://github.com/opencv/opencv.git

gitのインストール:このページを参照しました。

>brew install git

そして以下のコマンドと続きます(対象のバージョンは「3.4.3」

>cd opencv
>checkout 3.4.3
>mkdir build
>cd build
>cmake -DBUILD_SHARED_LIBS=OFF ..

そして処理が流れ始め。。。

javaフォルダができたかな?と思いきや。。。「ない」

そんな時は下のコマンドを叩く様です。

>export JAVA_HOME=/usr/lib/jvm/java-6-oracle
>cmake -DBUILD_SHARED_LIBS=OFF ..

「/usr/lib/jvm/java-6-oracle」の部分は自分のマシンで使用するJDKの場所を指定します。自分の場合は以下の様になります。

>export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/
>cmake -DBUILD_SHARED_LIBS=OFF ..
>make -8j

そして、ビルドの結果

javaフォルダがない。。。

と思ったらopencv/binの中にありました。

そんなわけで、以下のコマンドでファイルをビルドパスに繋げやすい様にファイルを移動します。

>cp ./bin/opencv-343.jar ~/Java/OpenCv/
>cp ./lib/libopencv_java343.dylib ~/Java/OpenCv/

リンク先のファイルが「libopencv_java343.dylib」とは違う様ですが、このファイルで良い様です。

Eclipseのプロジェクトを右クリック > Build Pathを選択

Native Libraryを選択して。。。

Native libraryにOpenCVを登録(設定)します。

そして、実行!撃破!!



ソースは下の様になりました。対象のライブラリ部品から定数で参照する値は名前が違う場合がある様です。。。なのでハードコーディング(笑)

import org.opencv.core.CvType;
import org.opencv.core.Mat;

/**
 * OpenCVの部品を実行するクラス。
 * 
 * @author takunoji
 *
 * 2018/11/11
 */
public class ReceiptCv {
	static {
		System.loadLibrary("opencv_java343");
	}
	public static void main(String[] args) {
		new ReceiptCv().helloCv();
	}

	public void helloCv() {
		Mat m = Mat.eye(3, 3, CvType.CV_8UC1);
		System.out.println("m = " + m.dump());
	}
}

そしてImShow(画像表示)→OpenCV3.Xではできません。

OpenCVの3.XからはHightGUIを使用しないようです。なのでJavaFXでイメージの表示を行います。「Java OpenCv Hello in Java〜OpenCv事始め〜

旧バージョンでのImShowメソッドの使用方法

JARファイルをGitから直接ダウンロードします。

Imshow.jarを直接ダウンロードします。

Downloadボタンをクリックしてダウンロードします。

BuildPathをJARファイルに通します。

プロジェクトを右クリック > BuildPath

「Add External Jar」でダウンロードしたファイルを追加します。

実際の作業

次は、画像の表示をするための準備を行います。

ImShowのメソッドが使えないのでJavaFXによる画像表示GUIを作成していきます。

参考サイトはこちらです。

ちなみにチュートリアルでは「シーンビルダー」をインストールして画面の土台を作っています、下のような感じです。

関連ページ一覧

<JavaFXの準備>

  1. Eclipse SceneBuilderを追加する
  2. JavaFX SceneBuilder 〜EclipseとSceneBuilder連携~

<TensorFlow関連>

 家計簿アプリ作成

  1. Eclipse アプリ作成 Lv1〜家計簿を作る準備〜
  2. Eclipse アプリ作成 Lv2〜家計簿を作る土台作り〜
  3. Eclipse アプリ作成 Lv3〜3Dグラフ用Cube作り〜
  4. Eclipse アプリ作成 Lv4〜3Dグラフ用Cubeに高さを与える〜
  5. Eclipse アプリ作成 Lv5〜惨敗:CubeにTextureを貼る〜
  6. Eclipse アプリ作成 Lv7〜Cubeを日付順に並べる〜

Eclipse アプリ作成 Lv7〜Cubeを日付順に並べる〜

イントロダクション

ここ数日の戦いを経て、ようやく先に進めます。※下は戦闘履歴です。

  1. Eclipse アプリ作成 Lv5〜惨敗:CubeにTextureを貼る〜「惨敗」
  2. Java 3DGame LWJGL Retry Lv3 Texture〜動かして理解する3〜「負け越し」
  3. Java 3DGame LWJGL Retry Lv4 デバック〜動かして理解する4〜「黒星」
  4. Java 3DGame LWJGL Retry Lv5 遊んでみる〜動かして理解する5〜「引分け」
  5. Java 3DGame LWJGL Retry Lv6 遊んでみる2〜動かして理解する6〜「白星」

家計簿作成を再開しようと思いますが、その前に戦果の方を見ていただきたく思います。

<現在>

家計簿のためのカレンダー部分が完成しました。

現状では、まだまだわかりづらいのは否めません。。。

しかし、初めに比べれば。。。

<作成当初>

家計簿作成再始動

今回はよりカレンダーらしくするために、カレンダークラスを使用してカレンダーの様にCubeを配置する様にプログラムを変更します。

まずはカレンダー作成

Javaの基本的なクラス(java.util.Calendar)を使用します。作成するクラスは「CalendarPosit」クラスです。

<要件の整理:どんな機能を持たせるか考える>

Cubeを使ってカレンダーを作成しますので。以下の要件を満たす必要があります。→プログラム設計になります。

  1. 対象月の月初の曜日を取得する
  2. 今月の最大日数を取得する
  3. Calendarクラスから取得した曜日が何曜日か判定する
  4. 日曜始まりをデフォルトとして、月曜始まりの場合でも曜日の値(Calendar.DAY_OF_WEEK)を返却する(配列の位置)

実行結果:木のCubeも移動しています

<月曜始まり>の実行結果(isStartMon=true)

<日曜始まり>の実行結果(isStartMon=false)


実装内容:ソースはこちら(Gitでみる)

isStartMonのハンドルを行なっているクラス(DummyGame#init())のソース(2018/11/10現在)

    //// 参照しているフィールド変数(抜粋) ////
    /**
     *  曜日の表示順
     *  true: 月曜始まり false: 日曜始まり
     */
    private boolean isStartMon;
    private static final String[] WEEK_TEXTURE_SUN = new String[]{"Sun", "Mon", "Tue", "Wed", "Thi", "Fri", "Sat"};

    @Override
    public void init(Window window) throws Exception {
        renderer.init(window);
        isStartMon = true;// 月曜始まり、日曜始まりは「false」
        CalendarPosit pos = new CalendarPosit();
        // 月初の曜日
        int firstDayOfWeek = pos.getStartPoint(isStartMon);
        // 今月の最大日数
        int maxDayOfMonth = pos.getMaxDayOfMonth();
        // Cubeの高さ
        ArrayList<float[]> floats = new ArrayList<>();
        floats.add(new float[] {0.0001f, 0.12f, 0.3f, 0.001f, 0.1f, 0.25f, 0.1f});
        floats.add(new float[] {0.15f, 0.19f, 0.23f, 0.2f, 0.08f, 0.13f, 0.12f});
        floats.add(new float[] {0.1f, 0.2f, 0.4f, 0.001f, 0.2f, 0.05f, 0.15f});
        floats.add(new float[] {0.11f, 0.12f, 0.3f, 0.001f, 0.1f, 0.25f, 0.1f});
        floats.add(new float[] {0.12f, 0.13f, 0.14f, 0.015f, 0.16f, 0.17f, 0.18f});
        floats.add(new float[] {0.12f, 0.13f, 0.14f, 0.015f, 0.16f, 0.17f, 0.18f});

        // Cubeの底面のサイズ(正方形)
        final float cubeSize = 0.1f;
        // x軸の初期値
        final float xInit = -0.5f;
        // y軸の初期値
        final float yInit = -0.8f;
        // z軸の初期値
        final float zInit = -2;
        // x軸の増減幅
        final float xWidth = 0.185f;
        // y軸の増減幅
        final float yWidth = 0.033f;
        // z軸の増減幅
        final float zWidth = 0.1f;
        ArrayList arr = new ArrayList();
        
        // マスのカウント
        int box = 0;
        // 日数
        int day = 0;
        // 初めの1回目だけ曜日のテキストプレートを作成する
        boolean isCreateTexture = false;
        // 1ヶ月分(5週間分のマスを作る)
        for(int j = 1; j <= 6; j++) {
        	// 開始点より一列ずらす
        	// X軸の開始点
        	float xStart = xInit - (0.1f * j);
        	// Y軸の開始点
        	float yStart = yInit + (0.06f * j) ;
        	// Z軸の開始点
        	float zStart = zInit - (0.16f * j);
            // 1週間分
        	float[] weekArr = floats.get(j-1);
        	
        	// 1回目だけテクスチャを作成する
        	isCreateTexture = j == 1 ? true: false;
            for(int i = 1; i <= 7; i++) {
            	float xAdd = xWidth * i;
            	float yAdd = yWidth * i;
            	float zAdd = zWidth * i;
        		// 日付をインクリメント
        		box++;
            	if (isCreateTexture) {
            		arr.add(putOnTexturePlate(xStart + xAdd + 0.01f , yStart + yAdd - 0.03f, zStart - zAdd + 0.03f, i-1));
            	}
           	    if (box < firstDayOfWeek) { System.out.println("開始: " + firstDayOfWeek); continue; } day++; if (day >= maxDayOfMonth) {
            		break;
            	}
            	float val = weekArr[i - 1];
        		arr.add(createCube(val,
        				cubeSize, xStart + xAdd, yStart + yAdd, zStart - zAdd));
            }
        }
        // 配列の要素数を指定する
        GameItem[] items = new GameItem[arr.size()];
        // 配列の取り出し
        gameItems = arr.toArray(items);
        
        // DEBUG
        //debug();
    }

ここでのポイントは月曜始まりと、日曜始まりを変更できる様にしたところです。

	/**
	 * 対象月の月初の曜日を取得する
	 * @return Calendar.MONDAY〜SUNDAY
	 */
	public int getStartPoint(boolean isStartMon) {
		int day_week = 0;
		cal.set(Calendar.DATE, 1);
		day_week = cal.get(Calendar.DAY_OF_WEEK);
		if (isStartMon) {
			day_week = day_week == 1 ? 7 : day_week - 1;
		}
		return day_week;
	}

赤字の部分が工夫の部分です、条件文を使って「if文」を使わずに1行で済むし可読性も良い。これを使わないと以下の様なコードになります。(if文を抜粋)

if (isStartMon) {
    if (day_week == 1) {
        day_week = 7;
    } else {
        day_week = day_week -1;
    }
 }

微妙なコードです、なぜかというとif文はネストすると確認しなくてはいけないことが増えるからです。→単純にコード量が多いのもあります。

一応「条件演算子」について、サンプルとして四捨五入の場合です。

/*
 * 返却する値 = 条件式 ? TRUE時の値(式) : FALSE時の値(式)
 * System.in() // 標準入力
 */
int i = System.in;
// 4以下は「0」、5以上は「10」を「response」に代入
int response = i < 5 ? 0 : 10;

そして作成したのが以下のクラス(Gitでみる)です。(ちょっとコード量が多いですが。。。)

package zenryokuservice.gui.lwjgl.kakeibo.util;
import java.util.Calendar;

/**
 * 家計簿の各数値を表示するための領域(日)を作成する
 * @author takunoji
 *
 * 2018/11/10
 */
public class CalendarPosit {
	/** 日本語の曜日 */
	private static final String[] ja_week = new String[] {"日", "月", "火", "水", "木", "金", "土"};
	/** 英語の曜日 */
	private static final String[] en_week = new String[] {"Sun", "Mon", "Tue", "Wed", "thu", "Fri", "Sat"};

	private Calendar cal = null;
	
	public CalendarPosit() {
		cal = Calendar.getInstance();
	}
	
	/**
	 * 今月の最大日数を返す
	 * @return 最大日数
	 */
	public int getMaxDayOfMonth() {
		return cal.getMaximum(Calendar.DATE);
	}

	/**
	 * 今月の最大日数を返す
	 * @move 今月の月からずらす月数
	 * move=1  -> 1ヶ月たす
	 * mobe=-1 -> 1ヶ月引く
	 * 
	 * 
	 * 現在:10月
	 * move=  1  -> 11月の最大日数
	 * move= -1 -> 9月の最大日数
	 * @return 最大日数
	 */
	public int getMaxDayOfMonth(int move) {
		Calendar tmpCal = cal;
		tmpCal.add(Calendar.MONTH, move);
		return tmpCal.getMaximum(Calendar.DATE);
	}

	/**
	 * 対象月の月初の曜日を取得する
	 * @return Calendar.MONDAY〜SUNDAY
	 */
	public int getStartPoint(boolean isStartMon) {
		int day_week = 0;
		cal.set(Calendar.DATE, 1);
		day_week = cal.get(Calendar.DAY_OF_WEEK);
		if (isStartMon) {
			day_week = day_week == 1 ? 7 : day_week - 1;
		}
		return day_week;
	}	

	/**
	 * 現在の年の指定した月初の曜日を返却する
	 * @param month 1-12を指定する
	 * @return 対象月の、月初曜日
	 */
	public int getStartPoint(int month, boolean isStartMon) {
		int day_week = 0;
		Calendar cal = Calendar.getInstance();
		cal.set(Calendar.MONTH, month - 1);
		cal.set(Calendar.DATE, 1);
		
		day_week = cal.get(Calendar.DAY_OF_WEEK);
		return day_week;
	}

	/**
	 * カレンダークラスを返却する。
	 * @return
	 */
	public Calendar getCalendar() {
		return this.cal;
	}
	/**
	 * 開始日付に対応する曜日の値を返す。
	 * @return 曜日の文字列
	 */
	public String getDayOfWeek(boolean isStartMon) {
		return ja_week[getStartPoint(isStartMon)];
	}
	/**
	 * 引数の値が何曜日か判定して返却する
	 * @param value Calendarクラスより取得した、DAY_OF_WEEK
	 * @return 対象の曜日の値
	 */
	public int chkWeek(int value, boolean isStartMon) {
		int day_of_week = 0;
		
		switch(value) {
		case Calendar.MONDAY:
			day_of_week = Calendar.MONDAY;
			break;
		case Calendar.TUESDAY:
			day_of_week = Calendar.TUESDAY;
			break;
		case Calendar.WEDNESDAY:
			day_of_week = Calendar.TUESDAY;
			break;
		case Calendar.THURSDAY:
			day_of_week = Calendar.TUESDAY;
			break;
		case Calendar.FRIDAY:
			day_of_week = Calendar.TUESDAY;
			break;
		case Calendar.SATURDAY:
			day_of_week = Calendar.TUESDAY;
			break;
		case Calendar.SUNDAY:
			day_of_week = Calendar.TUESDAY;
			break;
		default:
			// TODO-[例外処理の実装]
			System.out.println("想定外の曜日です[day_of_week]: " + day_of_week);
			System.exit(-1);
		}

		// 日曜始まり=true, 月曜始まり=false
		if (isStartMon == false) {
			day_of_week = day_of_week < 7 ? day_of_week + 1 : 1;
		}
		return day_of_week;
	}
}

いろんなパターンに対応できる様に先にコードだけ作りました。

関連ページ一覧

<家計簿アプリ作成>

  1. Eclipse アプリ作成 Lv1〜家計簿を作る準備〜
  2. Eclipse アプリ作成 Lv2〜家計簿を作る土台作り〜
  3. Eclipse アプリ作成 Lv3〜3Dグラフ用Cube作り〜
  4. Eclipse アプリ作成 Lv4〜3Dグラフ用Cubeに高さを与える〜
  5. Eclipse アプリ作成 Lv5〜惨敗:CubeにTextureを貼る〜
  6. Eclipse アプリ作成 Lv7〜Cubeを日付順に並べる

Java Basic

  1. Java Basic Level 1 〜Hello Java〜
  2. Java Basic Level2 〜Arithmetic Calculate〜
  3. Java Basic Level3 〜About String class〜
  4. Java Basic Level 4〜Boolean〜
  5. Java Basic Level 5〜If Statement〜
  6. Java Basic Summary from Level1 to 5
  7. Java Basic Level 6 〜Traning of If statement〜
  8. Java Basic Level8 〜How to use for statement〜
  9. Java Basic Level 8.5 〜Array〜
  10. Java Basic Level 9〜Training of for statement〜
  11. Java Basic Level 10 〜While statement 〜
  12. Java Basic Swing〜オブジェクト指向〜
  13. Java Basic Swing Level 2〜オブジェクト指向2〜
  14. サンプル実装〜コンソールゲーム〜
  15. Java Basic インターフェース・抽象クラスの作り方
  16. Java Basic クラスとは〜Step2_1〜
  17. Java Basic JUnit 〜テストスイートの作り方〜

Java 3DGame LWJGL Retry Lv7 遊んでみる3〜全部テクスチャにする〜

イントロダクション

ここのところ負け越しの1引分けでしたが、ついにやっつけました。

<戦闘履歴>

  1. Eclipse アプリ作成 Lv5〜惨敗:CubeにTextureを貼る〜「惨敗」
  2. Java 3DGame LWJGL Retry Lv3 Texture〜動かして理解する3〜「負け越し」
  3. Java 3DGame LWJGL Retry Lv4 デバック〜動かして理解する4〜「黒星」
  4. Java 3DGame LWJGL Retry Lv5 遊んでみる〜動かして理解する5〜「引分け」
  5. Java 3DGame LWJGL Retry Lv6 遊んでみる2〜動かして理解する6〜「白星」

最終的に以下の様なコードができました。主にいじったのは「DummyGame」クラスです。ポイントは以下になります。

  1. メッシュを作成するときはテクスチャを使用する様に修正
  2. メッシュを作成するときの各頂点(Vertex)の定義を見直す
  3. 同様に頂点を結びつける順番(indices)を見直す

<DummyGame#init()>

    @Override
    public void init(Window window) throws Exception {
        renderer.init(window);
        // Cubeの高さ
        ArrayList<float[]> floats = new ArrayList<>();
        floats.add(new float[] {0.0001f, 0.12f, 0.3f, 0.001f, 0.1f, 0.25f, 0.1f});
        floats.add(new float[] {0.15f, 0.19f, 0.23f, 0.2f, 0.08f, 0.13f, 0.12f});
        floats.add(new float[] {0.1f, 0.2f, 0.4f, 0.001f, 0.2f, 0.05f, 0.15f});
        floats.add(new float[] {0.11f, 0.12f, 0.3f, 0.001f, 0.1f, 0.25f, 0.1f});
        floats.add(new float[] {0.12f, 0.13f, 0.14f, 0.015f, 0.16f, 0.17f, 0.18f});
        // Cubeの底面のサイズ(正方形)
        final float cubeSize = 0.1f;
        // x軸の初期値
        final float xInit = -0.5f;
        // y軸の初期値
        final float yInit = -0.8f;
        // z軸の初期値
        final float zInit = -2;
        // x軸の増減幅
        final float xWidth = 0.185f;
        // y軸の増減幅
        final float yWidth = 0.033f;
        // z軸の増減幅
        final float zWidth = 0.1f;
        ArrayList arr = new ArrayList();
        
        // 初めの1回目だけ曜日のテキストプレートを作成する
        boolean isCreateTexture = false;
        // 1ヶ月分(5週間分のマスを作る)
        for(int j = 1; j <= 5; j++) {
        	// 開始点より一列文ずらす
        	// X軸の開始点
        	float xStart = xInit - (0.1f * j);
        	// Y軸の開始点
        	float yStart = yInit + (0.06f * j) ;
        	// Z軸の開始点
        	float zStart = zInit - (0.16f * j);
            // 1週間分
        	float[] weekArr = floats.get(j-1);
        	
        	// 1回目だけテクスチャを作成する
        	isCreateTexture = j == 1 ? true: false;
            for(int i = 1; i <= 7; i++) {
            	float xAdd = xWidth * i;
            	float yAdd = yWidth * i;
            	float zAdd = zWidth * i;
            	if (isCreateTexture) {
            		arr.add(putOnTexturePlate(xStart + xAdd + 0.01f , yStart + yAdd - 0.03f, zStart - zAdd + 0.03f, i-1));
            	}
            	float val = weekArr[i - 1];
        		arr.add(createCube(val,
        				cubeSize, xStart + xAdd, yStart + yAdd, zStart - zAdd));
            }
        }
        // 配列の要素数を指定する
        System.out.println("GmaeItems: " + arr.size());
        GameItem[] items = new GameItem[arr.size()];
        // 配列の取り出し
        gameItems = arr.toArray(items);
        
        // DEBUG
        //debug();
    }

<DummyGame#putOnTexturePlate()>

    /**
     * Texture作成メソッド
     * @param xPos
     * @param yPos
     * @param zPos
     * @param num 曜日の番号0:月〜7:日
     * @return GameItem
     */
    private GameItem putOnTexturePlate(float xPos, float yPos, float zPos, int num) {
    	float size = 0.08f;
    	float[] positions = new float[] {
    			-1 * size, size, size, // V0
    			-1 * size, -1 * size, size, //V1 
    			size, -1 * size, size, // V2
    			size, size, size, // V3
    			
    			-1 * size, size, -1 * size, // V4
    			size, -1 * size, size, //V5 
    			-1 * size, -1 * size, -1 * size, // V6
    			size, -1 * size, -1 * size, // V7
    			};
    	float[] textCoords = new float[]{
                0.0f, 0.0f, 
                0.0f, 1.0f,
                1.0f, 1.0f,
                1.0f, 0.0f,
            };
    	int[] indices = new int[] {
    			0, 1, 3, 3, 1, 2
			};
    	Texture texture = null;

    	try {
        	texture = new Texture("/textures/" + weekTexture[num] +  ".png");
    	} catch(Exception e) {
    		e.printStackTrace();
    	}
        TexturedMesh mesh = new TexturedMesh(positions, textCoords, indices, texture);
        GameItem gameItem = new GameItem(mesh);
        gameItem.setPosition(xPos, yPos, zPos);
        gameItem.setRotation(20, 30, 0);
        return gameItem;
    }

<DummyGame#createCube()>

    private GameItem createCube(float height, float cubeSize, float posX, float posY, float posZ) {
        // Create the Mesh
        float[] positions = new float[]{
            // VO
            -1 * cubeSize,  height,  cubeSize,
            // V1
            -1 * cubeSize, -1 * cubeSize,  cubeSize,
            // V2
            cubeSize, -1 * cubeSize,  cubeSize,
            // V3
            cubeSize,  height,  cubeSize,
            // V4
            -1 * cubeSize,  height, -1 * cubeSize,
            // V5
            cubeSize,  height, -1 * cubeSize,
            // V6
            -1 * cubeSize, -1 * cubeSize, -1 * cubeSize,
            // V7
            cubeSize, -1 * cubeSize, -1 * cubeSize,
        };
        float[] colours = new float[]{
            0.5f, 0.0f, 0.0f,
            0.0f, 0.5f, 0.0f,
            0.0f, 0.0f, 0.5f,
            0.0f, 0.5f, 0.5f,
            0.5f, 0.0f, 0.0f,
            0.0f, 0.5f, 0.0f,
            0.0f, 0.0f, 0.5f,
            0.0f, 0.5f, 0.5f,
        };
        int[] indices = new int[]{
            // Front face
            0, 1, 3, 3, 1, 2,
            // Top Face
            4, 0, 3, 5, 4, 3,
            // Right face
            3, 2, 7, 5, 3, 7,
            // Left face
            6, 1, 0, 6, 0, 4,
            // Bottom face
            2, 1, 6, 2, 6, 7,
            // Back face
            7, 6, 4, 7, 4, 5,
        };
    	Texture texture = null;
    	try {
        	texture = new Texture("/textures/wood1.png");
    	} catch(Exception e) {
    		e.printStackTrace();
    	}
        TexturedMesh mesh = new TexturedMesh(positions, colours, indices, texture);
        GameItem gameItem = new GameItem(mesh);
        gameItem.setPosition(posX, posY, posZ);
        gameItem.setRotation(20, 30, 0);
        return gameItem;
    }

上記のポイントを抑えサイドコードを直したところうまくいきました。


関連ページ一覧

<Java Basic>

  1. Java Basic for文 〜Step1_3_1〜
  2. Java Basic Level8 〜How to use for statement〜
  3. Java Basic Level 9〜Training of for statement〜
  4. Java 3DGame LWJGL GitBook chapter7-1〜Cube作成〜「動画あり」

Java 3DGame LWJGL Retry Lv6 遊んでみる2〜動かして理解する6〜

イントロダクション

ここ数日連敗が続いていましたが、昨日引き分けにもつれ込みました。

  1. Eclipse アプリ作成 Lv5〜惨敗:CubeにTextureを貼る〜「惨敗」
  2. Java 3DGame LWJGL Retry Lv3 Texture〜動かして理解する3〜「負け越し」
  3. Java 3DGame LWJGL Retry Lv4 デバック〜動かして理解する4〜「黒星」
  4. Java 3DGame LWJGL Retry Lv5 遊んでみる〜動かして理解する5〜「引分け」
  5. Java 3DGame LWJGL Retry Lv6 遊んでみる2〜動かして理解する6〜「白星」

今日は勝つ予定です。。。

前回(「引分け」)はLWJGL GitBook Chapter7テクスチャを張り替えたり表示を変えたりして遊びました。

そして「遊んでみる方が学習効率も高い」ということを確信しました。というわけで、このまま次のチャプタをやってみようと思います。

Chapter8〜Camera〜

チャプタとしては「カメラ」と書いてありますが、ポイントとしてはテクスチャを貼り付けたオブジェクトを複数生成する場合の処理方法の理解です。

<Chapter8の実行結果>

*******************************************************************

*******************************************************************

 テクスチャありとなしのキューブを作成する

色々やったけど、こんな感じで中途半端にテクスチャが貼れています。

結論

シェーダを2種類用意していないな・・・つまり、テクスチャありの状態でCubeを作成しているので、全てのCubeにテクスチャが貼り付けられている状態です。

『本当は、「テクスチャあり」が手前の7個でそのほかは「テクスチャなし」にしようと思っております。』

<2018/11/09追記>
→この考え方が負因でした、片方はシェーダを使用する、もう片方はシェーダを使用しない。こうなるとシェーダプログラムを2つ用意してそれぞれのメッシュにもそれぞれのシェーダプログラムを適用する必要があるので、まぁ面倒だし実用的でないと判断しました。つまり全部テクスチャを使用する様に修正しました。

 

<テクスチャなし>色を指定しています。

今回は辛勝といったところでしょうか。。。

でわでわ。。。

関連ページ一覧

<今回のやっていること>

  1. Eclipse アプリ作成 Lv1〜家計簿を作る準備〜
  2. Eclipse アプリ作成 Lv2〜家計簿を作る土台作り〜
  3. Eclipse アプリ作成 Lv3〜3Dグラフ用Cube作り〜
  4. Eclipse アプリ作成 Lv4〜3Dグラフ用Cubeに高さを与える〜
  5. Eclipse アプリ作成 Lv5〜惨敗:CubeにTextureを貼る〜
  6. Eclipse アプリ作成 Lv7〜Cubeを日付順に並べる〜

<Java Basic>

  1. Java Basic for文 〜Step1_3_1〜
  2. Java Basic Level8 〜How to use for statement〜
  3. Java Basic Level 9〜Training of for statement〜
  4. Java 3DGame LWJGL GitBook chapter7-1〜Cube作成〜「動画あり」