みの屋.

プログラムを書いてる時に躓いたことやコードの進捗などをメモでまとめていく予定です.(ついでに日常のつぶやきもちらほらと……)

Javaでフォルダ以下のファイルを全部取ってみる

実は,この頃学校の講義や実験などでなにかとファイルにまとめられた大量の細かいデータを扱うことが増えてきました.

例えば,
f:id:Minoya:20171209184850p:plain:w200

という構造を持つファイルに対して.csvのファイルだけを全て検索,処理したい.
ということが増えました.

因みに,今は主にJavaPythonを使っているのですが,Pythonなら一瞬で全て検索できます.

import glob                                                                     
                                                                                
for name in glob.iglob('**/*.csv', recursive=True):                             
    print(name)

結果(Folder階層で検索した結果)↓

folder_A/1340_19832.csv
folder_A/214_20496.csv
folder_A/2310_13554.csv
folder_A/538_20593.csv
folder_A/945_20591.csv
folder_B/11_20062.csv
folder_B/9_20331.csv
folder_C/486_19918.csv
folder_C/494_19912.csv
folder_C/folderC_1/142_15207.csv
folder_C/folderC_1/21_15157.csv
folder_C/folderC_1/86_15241.csv

………たった3行で完璧に検索できました.
これもういっそ全部pythonでいいんじゃないか

…という考えは捨てて,もしかしたらpythonでなくjavaでやらないといけない場合が出るかもしれない…!!
ということで,javaでも同じことを実装するときにどこまで短く書けるのだろう,と思いjavaでも同じコードを書いてみました.

今回プログラムを組む際に主に用いたのがFileクラスです.
File クラスとはローカルディスクにあるファイルをJavaで扱えるようにしてくれるクラスのことです.

アルゴリズム

今回のプログラムのアルゴリズムは以下のようになります.

  • Fileクラスに指定Pathを渡してlistFile()を用いてファイル一覧を取得する
  • for文でリスト内を一つ一つフォルダかファイルかを判別し,フォルダだった場合はそのフォルダのパスを引数として再帰を行う
  • ファイルだった場合はさらにendsWith(指定したい拡張子)で分け,条件に合うならば出力

因みにファイルの拡張子関係なく指定ディレクトリ下全てのファイルを取得したい場合には上の3番目の処理を抜けばokです.
このアルゴリズムで出来上がったプログラムが以下になります.

import java.io.File;                                                            
//import java.io.FileNotFoundException;                                         
                                                                         
public class file_control{                                                      
    public static void main(String[] args){                                     
        String dir_path = "/Users/user_name/dir/path/";  //検索開始したいフォルダのPath(今回の場合なら`~Folder/`まで書く)
        String extension = ".csv";   //検索したいファイルの拡張子                      
        file_search(dir_path, extension);                                                  
    }                                                                           
    public static void file_search(String path, String extension){                                
        File dir = new File(path);                                              
        File files[] = dir.listFiles();                                         
        for(int i=0; i<files.length; i++){                                      
            String file_name = files[i].getName();                              
            if(files[i].isDirectory()){  //ディレクトリなら再帰を行う                                                                        
                file_search(path+"/"+file_name, extension);                                
            }else{  
                if(file_name.endsWith(extension)){  //file_nameの最後尾(拡張子)が指定のものならば出力                                 
                    System.out.println(path+"/"+file_name);
                }                                  
            }                                                                   
        }                                                                       
    }                                                                           
}                                                                               

一応24行に抑えましたが今の私にはこれが精一杯の短縮です……もっと知識があればもっと短くなりそう…………
……そして肝心の実行結果は,

folder_A/1340_19832.csv
folder_A/214_20496.csv
folder_A/2310_13554.csv
folder_A/538_20593.csv
folder_A/945_20591.csv
folder_B/11_20062.csv
folder_B/9_20331.csv
folder_C/486_19918.csv
folder_C/494_19912.csv
folder_C/folderC_1/142_15207.csv
folder_C/folderC_1/21_15157.csv
folder_C/folderC_1/86_15241.csv

よかった.ちゃんと正しく出力できました.

………もっと色んなコード書いてプログラム書く力つけていきたいなぁ

【Java】OpenCVで画像のアルファブレンディングしてみるお話

とある授業の課題で,2つの画像をアルファブレンディングすると言うものが出たのですが,そのうちの一つでめちゃくちゃ悩んだので解決法を.

今回作成に悩んだ画像はこちら.

f:id:Minoya:20171208160016p:plain:w300
ケーキのアルファブレンディング

一見簡単そう(OpenCVに一発でできる関数ありそう)とか思ってたのですが,そんな関数は探しても見つからず………
それなら誰かがネットに解決法を書いているだろうと探すもやっぱりどこにも落ちておらず………

結局自分でいろんなサイトさんを組み合わせて試行錯誤しながら完成までに数日かかってしまいました………

本当は隣の机で同じ課題のコードをPythonOpenCVで完成させていたのを横目に作業していたものの,やっぱり環境構築で意地はってJavaでやると決めたからには今回もがっつり意地はってしまいました………Pythonで書くと今回の私のコードより10行近く短縮されるらしいので「絶対Javaじゃないと嫌!」「Javaでないと困る!」という方でなければPythonの方も書きやすくてオススメです.

そして,私の検索スキルが弱いせいかもしれませんが,思ったより全くプログラムがヒットしなかったため, 今回やっと完成させたプログラムを忘れないように早速こっちにメモメモ.

アルゴリズム

今回のプログラムでは

  • 合成する画像を縦1ピクセルごとに分割する
  • 画像を合成する際にalpha値の比を右から左へグラデーションするように少しずつ変更(1ピクセルごとに1/widthだけalpha値を足していく)しながらfor文で処理を行う
  • 1ピクセルごとに処理したMatを再度連結して一つの画像にする

この3つの処理ができれば右から左へのアルファブレンディングが完成するはずなので,これを目標にプログラムしていきます

まずは,早速1番目と2番目の項目の実装です.

       int wid_main = img.cols();
        double wid = 0;

        for(int width=0; width<wid_main; width++){   //画像の横幅分for文を回す(img:画像1, img:画像2)
            Mat des = new Mat();
            Core.addWeighted(img.col(width), wid, img2.col(width), 1-(wid), 0.0, des);
            wid += 1.0/img.cols();
        }

ここはあっさり完成しました.
ここに処理した画像を一つの画像に連結し直すプログラムを書き加えれば完成です.

…………実は今回はここでがっつりつまづきました.
なんと,JavaOpenCVでは画像を一発で連結できる関数が無かったです

そこで,新規Matを用意し処理した縦1ピクセルのMatを順番に貼り付けてみたもののうまく行かず,また元の画像に処理済みのMatを順番に上書きしてみるもののコードが汚くなったばかりか画像も綺麗に仕上がらず.

結局OpenCVで画像を連結する際によく使われているhconcatという関数を見つけるまでに結構な時間がかかってしまいました.
しかし,やっと見つけたこのhconcat なぜか引数が(List, Mat)
てっきりこの関数は連結させたい画像2枚を引数に取ると思っていたのでまた混乱しました.

落ち着いてhconcatについてよくよく調べてみると,こちらのサイトさんに詳しい記述がありました. iwaki2009.blogspot.jp

以前は、2枚の画像を接続するのに使用したが、配列やVectorに格納されたMatを格納された枚異数だけ結合する機能もあることがわかった。 2枚の場合、配列の変形版で実現されている。

なるほど.hconcatを使う際には,Mat型を連結する順に格納したListが必要なようです.
やっと引数の意味がわかったところでMatをListに格納するための処理をソースコードに書き加えていきます.

       Mat[] array = new Mat[img.cols()];
        int wid_main = img.cols();
        double wid = 0;

        for(int width=0; width<wid_main; width++){
            Mat des = new Mat();
            Core.addWeighted(img.col(width), wid, img2.col(width), 1-(wid), 0.0, des);
            array[width] = des;
            wid += 1.0/img.cols();
        }

…………はい.実は先ほどListに格納しなければならない,と言ったものの,上記では配列にMatを格納しています.
実は使用したいList型のArrays.asList()は中身の追加ができないため,一度配列に格納してから最後に一括でListに起こしています.

ここまでできたところで,やっと配列をList型に変換し,hconcatを使用していきます.

       List<Mat> dest = Arrays.asList(array);
        Mat dst = new Mat();
        Core.hconcat(dest, dst);

これで,dstにList内のMatが全て連結された画像が格納されました.
あとは画像を出力して完成です!!
色々混乱することが多かったですが,やっぱりできた時の達成感は良いですね!

今回のプログラムの完全版は以下に示します.

import java.util.Arrays;
import java.util.List;

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

public class alpha_blending_gradation {
    public static void main(String[] args){
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        Mat img = Imgcodecs.imread("/Users/ユーザ名/dir/path/cake_Apple.jpg");
        Mat img2 = Imgcodecs.imread("/Users/ユーザ名/dir/path/cake_Marron.jpg");

        Mat[] array = new Mat[img.cols()];
        int wid_main = img.cols();
        double wid = 0;

        for(int width=0; width<wid_main; width++){
            Mat des = new Mat();
            Core.addWeighted(img.col(width), wid, img2.col(width), 1-(wid), 0.0, des);
            array[width] = des;
            wid += 1.0/img.cols();
        }

        List<Mat> dest = Arrays.asList(array);
        Mat dst = new Mat();
        Core.hconcat(dest, dst);
        Imgcodecs.imwrite("/Users/ユーザ名/dir/path/cake_alpha_blending_gradation3.png", dst);
    }
}

【Mac】javaでOpenCVの環境設定に苦労したメモ2

前回はJavaOpenCVをインストールしたものの謎のエラーで詰まってしまったのですが,どうにかこうにかOpenCVが使える環境を手に入れました.

結論から言うと……

ターミナルやIntelliJ で実行出来ずに結局Eclipseを使うことになりました.

…………はい.正直大人しく最初からEclipseを使えば行けるのではないかと薄々感じてはいたのですが……完全に私の意地でターミナルのみで完結させようとしてました.
ついでに言うならjupyter notebookを使ってレポート作成した方が楽なので,本当はできるだけターミナルで動かしたかったと言うのが本音だったり………

というわけでせっかく唯一実行出来たEclipseでの設定方法を忘れないようにメモしておこうかと.

EclipseOpenCVの設定

実行環境

Eclipse Neonのダウンロードまではこのページを参考にしました.

qiita.com

無事Eclipseがダウンロードできたら,早速設定をしていきます.
まずEclipseを起動し,プロジェクトを選択した状態でメニューバーの
"ファイル"→"プロパティー" を選択

f:id:Minoya:20171116133821p:plain

するとウィンドウが開くので,
"Javaのビルド・パス" → タブの"ライブラリ"を選択 → 右側の"外部JARの追加"
で追加するJARファイルを選びます.
今回の場合だとOpencv3.3.1_1を追加するので名前は"opencv-3.3.1"とでもしておけばおk.f:id:Minoya:20171116134528p:plain

次にネイティブライブラリ(.dylibとかいうやつ)も指定しないといけないので,さっき追加した"opencv-3.3.1"の"ネイティブ・ライブラリ・ロケーション"を選択し,"編集"で"ネイティブライブラリのあるフォルダのパス"を指定します.
(ここでネイティブライブラリ自体を選択する必要はないです.あくまでもネイティブライブラリのあるフォルダを指定すること)

f:id:Minoya:20171116143142p:plain

ここまで設定できたら後は「適応」を押して完了!!!

f:id:Minoya:20171116144858p:plain

あんなに実行できなくて悩んでいたエラーも出ることなく以外にもあっさり実行できました.
ちょっと悔しい…………

【Mac】javaでOpenCVの環境設定に苦労したメモ

一旦デスクトップマスコットのお話は置いておいて, 今回はここしばらく頭を悩ませていたJavaOpenCVをインストールするためのメモ書きを.

実はOpenCV自体は約2年前からちょこちょこjavaで入れようとしてたものの毎回失敗して後回しにしてたんですが,ある授業で画像処理の課題が出たので改めて挑戦してみることに.

前置きとして,今回やっとOpenCVjavaコンパイルは通ったものの,現時点でまだ実行時にエラーが出てます.

今回のインストール環境(Home brew がインストールされていることが前提)

今まではOpenCVを'.tar'ファイル等で落として環境構築しようとしてたのだけど,brewでインストールしているサイトが多かったため,今回はbrewでインストールすることに.

OpenCVのインストール

ではでは早速brewでインストール………と,その前にまずはしばらくサボっていたbrewのupdateとupgradeを.

因みに3ヶ月?くらいサボっていたため更新に15分以上かかりました.こまめにbrew updateしないとやっぱりいざという時に結構時間食いますね.

そしてやっと本題のOpenCVのインストール!
本当は参考にしたサイト様のリンクを貼りたいのですが,なぜか数日前に削除されてしまっていたので自分で新しくメモ書きします.

  1. まずはantをインストール.brew install antこれがないと目当ての.jarファイルが生成されないらしい…

  2. 次にbrew edit opencvを実行し,-DBUILD_opencv_java=OFFであればONに書き換える.(2017.11月現在 標準でOFFになっていました)
    因みにこの部分をONにしないと--with-javaオプションが働かないです.自分は最初これを知らずにjarファイルが出来なくてがっつり躓きました…

  3. brew install --build-from-source opencvを実行してOpenCVをインストール.
    これは思ったよりは時間かかるので,できれば時間と充電の余裕のある時にした方が良いです.

  4. 3で,エラーを吐かずにインストールが完了していれば,
    /usr/local/Cellar/opencv/3.3.1_1/share/OpenCV/javaopencv_java310.jarができているはず.
    もしjavaというフォルダがなければjarファイルのインストールに失敗しています.2の変更がちゃんと更新されているか確認して更新されていた場合は一度ターミナル自体を再起動した後に再インストールしてみてください.

ここまででjarファイルが作成されていればOpenCVのインストールは成功なので,次は以下のサンプルコードが実際に実行できるかを試していきます.

Sampleコードのコンパイル

今回使用する動作確認用のOpenCVのサンプルコード(hogehoge.java)は以下.

import org.opencv.core.Core;                                                                                            
import org.opencv.core.CvType;                                                                                          
import org.opencv.core.Mat;                                                                                             
                                                                                                                        
public class hogehoge {                                                                                                 
    public static void main(String[] args) {                                                                            
        System.out.println("check1");                                                                                   
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);                                                                   
        System.out.println("check2");                                                                                   
        Mat m = Mat.eye(3, 3, CvType.CV_8UC1);                                                                          
        System.out.println("check3");                                                                                   
        System.out.println("m = " + m.dump());                                                                          
    }                                                                                                                   
}

まずはこのコードをどこかのディレクトリに作成.作成する場所はどこでも大丈夫なので適当に選んじゃっておk
javaコードが用意できたら次はコンパイルしていく.

次にこのコードをコンパイルするためにさっきのopencv_java310.jarが必要なので,

javac  -classpath /usr/local/Cellar/opencv/3.3.1_1/share/OpenCV/java

でクラスパスを指定してコンパイルしてあげるか,あらかじめ

/Libraty/Java/Extensions

の中にjarファイルを入れてあげればクラスパスが通る.
因みに2つともクラスパスを通しているんだけど,-classpathの方はコンパイル&実行するたびにちゃんと指定してあげないとうまく動かないので注意が必要かも.
まとめると,

  • classpath:このコマンドをつけた時だけそのクラスパスを通す.他のファイルには一切影響しない.
  • Extensionsの中に入れる:全てのjavaファイルのコンパイル時に参照される.他のファイルに影響があると困るときは避けた方が良い.

という感じ.

ここまででちゃんとクラスパスが通っていればコンパイルは通るはず…!!
もしExtensionsの中にjarファイルを入れてもこんな感じのエラーが出たらいっそOpenCVアンインストールしてもう一度インストールした方が良いかも…
(自分はこのエラーが出て1年以上悩んだ結果再インストールしたので……)

hogehoge.java:1: エラー: シンボルを見つけられません
import org.opencv.core.Core;
                      ^
  シンボル:   クラス Core
  場所: パッケージ org.opencv.core
hogehoge.java:7: エラー: シンボルを見つけられません
    System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
                       ^
  シンボル:   変数 Core
  場所: クラス hogehoge
エラー2個

……org.opencv.core.Matとかはちゃんと存在していることになっているのになぜかorg.opencv.core.Coreだけが存在しないエラー………インストールのときに何か失敗してたのかな………

とりあえず,今回はこのエラーにめちゃくちゃ悩まされてたのがやっと解決して実行段階へ移れたので次へ進みます.

コードの実行

やっとコンパイルに成功して実行へと!!!!
次のような出力があれば成功なのでようやっと最終段階!!!!!!

mat = [  1,   0,   0;
   0,   1,   0;
   0,   0,   1]

自分はjarファイルをExtensionの中に入れているためコンパイル&実行時に-classpathをつけてないですが,コンパイル-classpathを使ってたら実行時にも指定してあげないと動かないです.

>java hogehoge

check1
check2
Exception in thread "main" java.lang.UnsatisfiedLinkError: org.opencv.core.Mat.n_eye(III)J
    at org.opencv.core.Mat.n_eye(Native Method)
    at org.opencv.core.Mat.eye(Mat.java:492)
    at hogehoge.main(hogehoge.java:10)

……………ん?Matに代入しているところでエラー吐いてる…?
LinkError?途中まで実行できてはいるけどnativeliblaryとか何かしらの参照がうまくいってない…?

…………という感じで今絶賛エラーと格闘しています.
Docker使えよと言われればそれまでなんですが,せっかくコンパイルも通ってここまで来たのでDockerに頼るのは何か悔しい……←
次回はエラーが解決してからの更新となりますが,もしその前にこのエラーの原因を知っている方がいればコメント等で教えていただけると幸いです………………

ちょっと自分専用のデスクトップキャラクター作ってみたお話(2)

今回も前々から別ブログで書き溜めていた"自分専用のデスクトップキャラを作るお話"の続きになります.

自分が色々忘れてしまう前にざくざく書いておかねば……

前回までのお話

  • javaで何か目に見える形で作品残すために自分専用のデスクトップキャラクター作ってみよう.
  • せっかく完成できたんだからもっと自分専用の機能とかつけてオリジナル感出していきたいね!
  • そうだ.自分からもメッセージを送れるようにまずは新規ウィンドウ増やそう!!!


…ということで,今回は新しく文字の入力ができるウィンドウを作っていきたいと思います.

突然ですが,前回書いた(ほぼ参考サイト様の)ウィンドウ用のソースコードがこちら.
(参考サイト様は前回のブログに掲載)

import java.awt.*;                                                              
import java.awt.event.*;                                                        
import java.awt.geom.*;                                                         
import java.awt.image.*;                                                        
import javax.swing.*;                                                           
import javax.swing.event.*;                                                     
import java.io.*;                                                               
import com.sun.awt.AWTUtilities;                                                
                                                                                
class MakeWindow extends JWindow{                                               
                                                                                
    JLabel label = null;                                                        
    Canvas canvas = null;                                                       
    Container contentPane = null;                                               
                                                                                
    MakeWindow(){                                                               
    }                                                                           
                                                                                
    //---------------通常のwindow作成----------------------                     
    public JFrame makeWindow(int width, int height){                            
        JFrame mainFrame = new JFrame("サンプル");                              
        mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
        mainFrame.setSize(width,height);                                      
        mainFrame.setLocationRelativeTo(null);                                
                                                                              
        //JFrameよりContentPaneを取得                                         
        Container contentPane = mainFrame.getContentPane();                   
                                                                              
        //ラベルのインスタンスを生成                                          
        label = new JLabel("これはラベルです");                               
                                                                              
        //ラベルをContentPaneに配置                                           
        contentPane.add(label,BorderLayout.CENTER);                           
                                                                              
        mainFrame.setVisible(true);                                           
        return mainFrame;                                                     
    }                                                                         
                                                                              
    //----------------画像windowの作成----------------------                  
    public JFrame makeImageWindow(String imagePath){                          
        JFrame frame = new JFrame();                                          
        frame.setUndecorated(true);                                           
        //各種リスナー追加                                                    
        addListener(frame);                                                   
                                                                              
        frame = viewImage(frame,imagePath);                                   
                                                                              
        return frame;                                                         
    }                                                                         
                                                                              
    //----------------window表示メイン----------------------                  
    public JFrame viewImage(JFrame frame,String imagePath){                   
        ImageIcon icon = new ImageIcon(imagePath);                            
        frame.setSize(icon.getIconWidth(),icon.getIconHeight());              
        setImagePosition(frame,icon.getIconWidth(),icon.getIconHeight());   

        Shape shape = getImageShape(icon);                                    
        frame.setShape(shape);                                                
                                                                              
        frame.setBackground(new Color(0,0,0,0));                              
        Image image = icon.getImage();                                        
                                                                              
        contentPane = frame.getContentPane();                                 
                                                                              
        //canvasがない時は作成                                                
        if(canvas == null){                                                   
            canvas = new Canvas(image);                                       
            contentPane.add(canvas);                                          
            frame.setVisible(true);                                           
                                                                              
        }else{                                                                
            //canvasがある時は再描写                                          
            canvas.image = image;                                             
            canvas.repaint();                                                 
        }                                                                     
        //setAlwaysOnTop(true);                                               
        return frame;                                                         
    }                                  

                                                                              
    public JFrame makeMessageWindow(String imagePath){                        
        JFrame frame = new JFrame("new");                                     
        frame.setUndecorated(true);                                           
        addListener(frame);                                                   
                                                                              
        ImageIcon icon = new ImageIcon(imagePath);                            
        frame.setSize(icon.getIconWidth(),icon.getIconHeight());              
        setImagePosition(frame,icon.getIconWidth(),icon.getIconHeight() + 30);                                                                             
                                                                              
        Shape shape = getImageShape(icon);                                    
        frame.setShape(shape);                                                
        //Image image = icon.getImage();                                      
        //Canvas canvas = new Canvas(image);                                  
                                                                              
        JPanel p = new JPanel();                                              
        ////JPanel p1 = new JPanel();                                         
        //JLabel label = new JLabel("うにゃー!!");                          
        //p.add(label);                                                       
        //p.setBackground(Color.WHITE);                                       
                                                                              
        String aisa;                                                          
        aisa = timeevent.time();                                              
                                                                              
        label = new JLabel(aisa);                                             
        ////JTextField text = new JTextField(8);                              
        label.setHorizontalAlignment(JLabel.CENTER);                          
        ////text.setHorizontalAlignment(JLabel.CENTER);                       
                                                                              
        p.add(label);                                                         
        ////p1.add(text);                                                     
        Container contentPane = frame.getContentPane();                       
        contentPane.add(label,BorderLayout.CENTER);                           
        ////contentPane.add(text,BorderLayout.CENTER);                        
        frame.setVisible(true);                                               
        return frame;                                                         
    }        

    //----------------------------各種処理系------------------------------    
    //------ドラッグ可能にして,ダブルクリックで終了するようにする--------    
    class DragWindowListener extends MouseAdapter{                            
        private MouseEvent start;                                             
        private Point loc;                                                    
        private Window window;                                                
        public DragWindowListener(Window w){                                  
            super();                                                          
            window = w;                                                       
        }                                                                     
        @Override                                                             
        public void mousePressed(MouseEvent me){                              
            if(me.getClickCount() >= 2){                                      
                System.exit(0);                                               
            }                                                                 
            start = me;                                                       
        }                                                                     
        @Override                                                             
        public void mouseDragged(MouseEvent me){                              
            loc = window.getLocation(loc);                                    
            int x = loc.x - start.getX() + me.getX();                         
            int y = loc.y - start.getY() + me.getY();                         
            window.setLocation(x,y);                                          
        }                                                                     
    }                                                                         
                                                                              
    //-------------------ラベルの再描写-----------------------                
    public void redrawLabel(String message){                                  
        label.setText(message);                                               
        repaintWindowAncestor(label);                                         
    }                                    

    //再描写                                                                  
    public void repaintWindowAncestor(JComponent c){                          
        JRootPane root = c.getRootPane();                                     
        if(root == null){                                                     
            return;                                                           
        }                                                                     
        Rectangle r = SwingUtilities.convertRectangle(c,c.getBounds(),root);  
        root.repaint(100,r.x,r.y,r.width,r.height);                           
    }                                                                         
                                                                              
    //各種リスナー追加                                                        
    private void addListener(JFrame frame){                                   
        DragWindowListener dwl = new DragWindowListener(frame);               
        frame.addMouseListener(dwl);                                          
        frame.addMouseMotionListener(dwl);                                    
    }                                                                         
                                                                              
    //画面端に配置                                                            
    private void setImagePosition(JFrame frame, int width, int height){       
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();   
        frame.setBounds(screenSize.width - width,screenSize.height - height,width,height);                                                                 
    }                         
                                                                              
    //アルファ値が0である箇所以外を全てshapeに追加してそれを返す             
    public Shape getImageShape(ImageIcon icon){                               
        GeneralPath shape = new GeneralPath();                                
        final BufferedImage bi = new BufferedImage(icon.getIconWidth(),icon.getIconHeight(),BufferedImage.TYPE_INT_ARGB);                                  
        icon.paintIcon(null,bi.createGraphics(),0,0);                         
        ColorModel cm = bi.getColorModel();                                   
        for(int y=0; y<bi.getHeight(); y++){                                  
            for(int x=0; x<bi.getWidth(); x++){                               
                //完全に透過していなければshapeに追加                         
                if(cm.getAlpha(bi.getRGB(x,y)) > 0){                          
                    int start = x;                                            
                    int width = 0;                                            
                    while((x<bi.getWidth()) && (cm.getAlpha(bi.getRGB(x++,y)) > 0)){                                                                       
                        width++;                                              
                    }           
                    shape.append(new Rectangle(start,y,width,1),true);        
                }                                                             
            }                                                                 
        }                                                                     
        return shape;                                                         
    }                                                                         
               
    //Canvasクラスを改造する                                                  
    private class Canvas extends JPanel{                                      
        private static final long serialVersionUID = 1L;                      
        private Image image = null;                                           
        public Canvas(Image image){                                           
            super();                                                          
            this.image = image;                                               
        }                                                                     
                                                                              
        public void paintComponent(Graphics g){                               
            if(image == null) return;                                         
            g.drawImage(image,0,0,this);                                      
        }                                                                     
    }                                                                         
                                                                              
                                                                              
}                                                                             
                                                                                         

……実を言うとまだ完璧に理解できた,というわけではない為,自分で少々無駄なところを勝手に付け足してしまっているかもしれません.
(でも動くから多分大丈夫…!!ソースを最適化するのはとりあえずプログラムが動いた後で…!!!)

では次に,ここに今回のメインである文字入力専用のウィンドウを作成するコードを書いていきます.
上のソースに書き加えたのが以下になります.

    //chatWindow作成                                                          
    JTextField text;                                                          
                                                                              
    public JFrame makeChatWindow(String imagePath){                           
        JFrame frame = new JFrame("chat");                                    
        frame.setUndecorated(true);                                           
        //frame.setVisible(true);                                             
        addListener(frame);                                                   
                                                                              
        ImageIcon icon = new ImageIcon(imagePath);                            
        frame.setSize(icon.getIconWidth(),icon.getIconHeight());              
        setImagePosition2(frame,icon.getIconWidth(),icon.getIconHeight());    
                                                                              
        Shape shape = getImageShape(icon);                                    
        frame.setShape(shape);                                                
                                                                              
        JPanel p = new JPanel();                                              
        //p.setBackground(Color.WHITE);                                       
                                                                              
        text = new JTextField(9);                                             
                                                                              
        text.setHorizontalAlignment(JLabel.CENTER);                           
                                                                              
        p.add(text);                                                          
                                                                              
        Container contentPane = frame.getContentPane();                       
        contentPane.add(text,BorderLayout.CENTER);                            
        frame.setVisible(true);                                               
        text.addKeyListener(new KeyInput());                                  
        return frame;                                                         
    }                               

    //Enterキーが押された時の処理                                             
    class KeyInput extends KeyAdapter {                                       
        public void keyPressed(KeyEvent e){                                   
            if(e.getKeyCode() == KeyEvent.VK_ENTER) {                         
                String txt = text.getText();                                  
                                                                              
                //------------類語変換-------------                           
                txt = vocabulary.chenge(txt);                                 
                                                                              
                //------------返答分岐-------------                           
                String responce;                                              
                responce = answer.ans(txt);                                   
                redrawLabel(responce);                                        
                System.out.println(responce);                                 
                                                                              
                //------------終了コマンド---------                           
                if(txt.equals("n")){                                          
                    System.exit(0);                                           
                }                                                             
            }                                                                 
        }                                                                     
    }       


    //chatの画面配置                                                          
    private void setImagePosition2(JFrame frame, int width, int height){      
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();   
        frame.setBounds(screenSize.width - width, screenSize.height - height \
- 400,width,height);                                                          
    }  

ちょっとコードがごちゃごちゃしているので順を追って説明していくと,

まず初めに,文字を入力する為にJTextFieldを用意.それをメッセージウィンドウと同様にして作ったウィンドウにp.add()して貼り付けています. これでとりあえず文字を入力する形は完成!

次に入力した文字をエンターキーで受け取ることができるようにaddKeyListener(new KeyInput());を使ってエンターキーが押された時のイベントを作っています.
また,KeyInputクラスではエンターキーが押された時点でのTextFieldにある文字を取得し,各処理を行ってキャラクターの返事を再描写しています.
因みに,返事は別コードのanswer.javaで処理されていますが,「こんにちは」と挨拶を入れてあげると時間帯にあった挨拶を返してくれるようになっています!
とりあえずanswer.javaのコード説明は次回以降に回します.

そして,今回このコードを書くにあたってはこちらのサイト様を参考にさせていただきました. https://teratail.com/questions/8253

最後に,チャット用ウィンドウの表示場所ですが,他の二つのウィンドウと同じに設定してしまうとキャラクターのセリフウィンドウと丸かぶりしてしまう為わざと初期表示位置を他のウィンドウと分けています.

……とまぁいろいろごちゃごちゃと勢いだけで書き込んでいったのですが,今回追加実装できた子はこうなりました!!! f:id:Minoya:20171009194814p:plain

ちゃんと挨拶できてる…!!!!!!!!

やっぱり画面に居るだけじゃなくて言葉に反応してくれるようになるとますます愛着が湧いてきますね…!!!!
(ちょっと次はSiriみたいにアプリ起動してくれる機能つけてみる予定…)

そして実は,ここまで作った段階でMakeWindow.java 以外にも

があるのですが,次回ではその部分の説明ができたら良いなと思ってます!!

ちょっと自分専用のデスクトップキャラクター作ってみたお話(1)

今回は前のブログで結構前から書きかけだった自分専用のデスクトップキャラクターを作ってみたお話.

javaの勉強を始めてなんだかんだ毎回簡単なプログラムばっかり書いて遊んでたんですが,最近ターミナル上で動かしてばっかりで目に見える作品ができてないことに気づきました.
…ということで唐突ですがたまには目に見える形でプログラム書いてみたいなーと思ってちょっと自分専用のデスクトップキャラクターとか作ってみることに.

まずネットで検索をかけてみると……

なんとまさにやりたいことがそのまま書かれているサイトを発見.

milk0824.hatenadiary.jp

………あれ?これやりたいこと丸ごと書いてあるじゃん…………

このコードをお借りしてちょっと画像とか表示文字とか変えたら終わりじゃないか…!!!

なんて思ってたんですが,javaGUIを使うのが初心者な私がコードの意味を知らずにコピーしただけのプログラムでは早々に完成するわけもなく.

案の定キャラクターとメッセージのウィンドウがバラバラの場所に表示されてしまったりキャラクターの首がウィンドウから見切れてる…なんて事態になったりして完成するのに丸々2ヶ月もかかりました.

そしてなんだかんだで形になったのがこれ. f:id:Minoya:20171007235227p:plain

雑だけどやっと形にはなった……っ!!

使用したキャラクターの画像は学科の巨大液タブ使って描きました(学科の貸し出し設備って強い)

あとメッセージを表示する際にData型を使って朝昼晩の時間ごとに挨拶の言葉が変わるようにちょっとだけプログラムに追加することにも成功したのでひとまず最初にやりたかったことはこれで完成させることができました.

できました………が,よくよく考えるとこれだと時間ごとに挨拶を変える機能を追加しただけでこのキャラクターほとんどただの置物化しちゃってないか…?

ってことでせっかくここまで作ることができたし,ここからは他にも色々この子に機能を追加していって自分オリジナルのデスクトップキャラクターに成長させていきたくなったので,自分のjava技術の成長とともに機能の追加で一緒にこの子も成長していくことになりそうです…!!

次回もまだまだ書き溜めたお話でこのキャラクターと簡単な挨拶ができるように新しいウィンドウを作ったお話になります!

ここから.

ちょっと某発表会を見ていてプログラム系のブログ面白そう……ってなったので作ってみました.

 

主にプログラムを書いた時に自分が詰まったところや進捗等のメモがわりに使っていければ良いなって思ってます笑