画像RSS

ラベル Java の投稿を表示しています。 すべての投稿を表示
ラベル Java の投稿を表示しています。 すべての投稿を表示

2016年9月4日日曜日

画像を出力するJava Servlet

 ウェブサーバに動的に画像を出力させたい場合がある。 リアルタイムで値が変動するデータのグラフを作りたい場合やアクセスカウンターを作りたい場合などがそれである。 グラフはJavaScriptのCanvas 2Dでも実現できなくもないだろうが、JavaScriptはクライアントに依存するため、それだけに頼るのは避けるべきである。SVGというXMLを利用した方法も使えるだろうが、SVGでは不都合な場合もあるだろう。SVGはブラウザによっては対応していないこともあるかもしれない。

 そのため、Java Servletを利用して画像を出力する方法を考えてみた。思いの外簡単であり、誰でも思いつくようなことだろうが一応書いておく。

 画像を出力するにあたってまず必要なことは、HttpServletインスタンスのdoGetメソッド等の引数である HttpServletResponseインスタンスのメソッドsetContentTypeを実行することである。 そのメソッドの引数に"image/png"などの画像の形式 (MIME型) を表す文字列を与えればいい。

 Javaで画像を扱うのに簡単な方法はBufferedImageを使うことである。 コンストラクタで一から生成してもいいし、ImageIOreadメソッドで既存のファイルを鋳型として生成してもいい。BufferedImagegetGraphicsメソッドの返り値のGraphicsインスタンスや、 createGraphicsメソッドの返り値のGraphics2Dインスタンスを利用すれば、画像の変換等を行うことができる。

 画像の処理を行った後、最後にImageIOwriteメソッドを実行する。第1の引数に処理を行ったBufferedImageのインスタンスを指定する。 第2の引数は"PNG"や"JPEG"等の画像の形式を表す文字列とする。 HttpServletResponseインスタンスのメソッドgetOutputStreamの返り値を第3の引数とする。 こうして、画像が出力される。

public class ImageDrawer extends HttpServlet {
 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  response.setContentType("image/png");
  
  BufferedImage img=new BufferedImage(200,200,BufferedImage.TYPE_INT_ARGB);
  Graphics g=img.getGraphics();

  g.setColor(Color.BLACK);
  g.fillRect(0,0,200,200);
  g.setColor(Color.WHITE);
  g.setFont(new Font("梅明朝",Font.BOLD,30));
  g.drawString("0123456789",0,30);
  g.drawLine(0,0,200,200);
  
  OutputStream os=response.getOutputStream();
  ImageIO.write(img,"PNG",os);
 }
}
実効結果

 上記のコードを実行すれば、このような画像が出力されるはずである。

Javaでスクリーンキャプチャ

Javaでスクリーンショットを撮る

 ディスプレイに表示されている画面を画像データ化したいとき、つまりはスクリーンショットを撮りたいときがある。WindowsではPrintScreenキーを押すとクリップボードにスクリーンショットを保存できる。画像編集ソフトウェアのGIMPには高度なスクリーンキャプチャの機能がある。 そして、Javaにもスクリーンショットのための機能がある。

 Javaには標準でjava.awt.Robotというクラスが存在する。このクラスは仕様書によると「Java プラットフォーム実装テストを自動化する」ために存在するらしく、自動的にキーボードのキーを押したりカーソルを移動させたりする機能がある。 当記事で紹介するのはこのクラスのcreateScreenCaptureメソッドである。

 このcreateScreenCaptureメソッドがスクリーンキャプチャの働きをするメソッドである。 引数はjava.awt.Rectangleのインスタンスであり、これで撮影する範囲をXY座標、幅、高さで指定する。 返り値はjava.awt.image.BufferedImageのインスタンスであり、このインスタンスは画像データを示す。こうして得られた画像をそのままファイルとして保存したり何かしらに表示したりすることができる。

連続スクリーンキャプチャ

 このスクリーンキャプチャの機能を利用するために私が実際に作成したプログラムの一部を次に示す。このプログラムは指定した回数countだけ撮影するというもので、指定したミリ秒delayの分だけ撮影毎に間隔をあける。そして、撮影したスクリーンショットのハードコピーをディレクトリdirに適当な名前をつけて保存する。 Robotにはスレッドスリープの機能をするdelayメソッドもあるためそれも利用した。 画像の保存ではjavax.imageio.ImageIOクラスを用いた。

//Robotを取得
Robot robot=null;
try{
 robot=new Robot();
}catch(AWTException e){
 e.printStackTrace();
}

for(int i=0;i<count;i++){
 /*遅延*/if(i!=0)robot.delay(delay);
 //ハードコピーを取得
 BufferedImage image=robot.createScreenCapture(rectangle);
 try {
  //画像を出力 (形式はPNG)
  ImageIO.write(image,"PNG",new File(dir,"screenshot"+(i+1)+".png"));
 }catch(IOException e){
  e.printStackTrace();
 }
}

 なお、RobotにはgetPixelColorというメソッドもある。このメソッドは指定した座標のピクセルの色を取得するというものである。 このように、Robotには少し変わった機能があり、工夫次第で面白い使い方もできるかもしれない。

JavaでWebカメラを制御する便利なAPI

 JavaでWebカメラを制御するためのAPIが存在し、「Webcam Capture in Java」で配布されている。 私がこのAPIの存在を知ったのは「Webcam Capture を使って Java から Webカメラ を簡単制御(キャプチャ・動体検知)する」という記事のおかげだが、使ってみると本当に簡単で便利だった。

 このAPIはJava6以降で動作し、当然ながら使用するにはWebカメラが必要である。ネットワークカメラも使えるらしい。「Webcam Capture in Java」によると、このAPIには次のような特徴があるという (下記の部分は前述のページを私が要約したものであり、参考にする際は自己責任でお願いします) 。

  • シンプルでスレッドセーフでノンブロッキングなAPI。
  • 他にソフトウェアを追加する必要なし。
  • マルチプラットフォーム。WindowsやLinux、Mac OSなどで動作する。また、様々なアーキテクチャをサポート。32ビット、64ビット、ARMで動作する。
  • 画像を備え付けのWebカメラから取得できる。USBで接続されたWebカメラも使える。ネットワークカメラも使用可能。
  • JMFやOpenCVなどの複数のキャプチャ用のフレームワークをサポート。
  • 一度コードを書いた後に別のドライバを使っても、そのドライバはWebcamDriverクラスによってラップされるため、コードを書き換える必要はない。
  • カメラ映像を表示するSwingコンポーネントが用意されている。複数のカメラから特定のカメラの映像を選んで表示できる。
なお、上記のサイトは全部英語で書かれている。正確な情報が欲しい方は頑張って和訳していただきたい。

 このAPIの使い方は「Webcam Capture を使って Java から Webカメラ を簡単制御(キャプチャ・動体検知)する」を読めばだいたい分かるはずである。 ここではその補足をする。詳細はAPI付属の仕様書を読んで把握してほしい。

 リアルタイムのカメラの映像を表示させたいときにはcom.github.sarxos.webcam.WebcamPanelクラスまたはcom.github.sarxos.webcam.WebcamViewerクラスのインスタンスを用いればいい。 WebcamPanelはjavax.swing.JPanelのサブクラスであり、WebcamViewerはjavax.swing.JFrameのサブクラスである。使い方はスーパークラスに準じる。 WebcamViewerではカメラの切り替えが可能である。 WebcamPanelは指定したWebカメラだけを対象にする。必要に応じて使い分けるといいだろう。

 また、Webカメラが取得できる画像の大きさはデバイスに依存するようである。com.github.sarxos.webcam.WebcamクラスのgetDeviceメソッドで com.github.sarxos.webcam.WebcamDeviceインターフェースというデバイスを抽象化したインターフェースが取得できる。 そのインターフェースのgetResolutionsメソッドで利用できる画像の大きさのリストを取得できる。 それから適当なものを選んで、WebcamクラスのsetViewSizeメソッドで取得したい画像のサイズを指定すればいい。 getResolutionsメソッドの返り値にないものを指定するとエラーが発生するため注意が必要である。