Java Imageクラス使い方
Javaによる画像処理プログラミングの基本は、Imageクラスの利用方法を理解することでしょう。
このサンプルではソースコード内に記述したフォルダ内の、jpegファイルをすべて読み出し、
サイズを変えたものをJpeg形式で指定されたフォルダ内に書き出します。書き出す際には
指定されたJpeg品質で行います。サイズの指定はソースコード内で行っていますが、横方向のドット数で指定するようになっています。
縦方向は元ファイルの縦横比率を保持するように自動的に調整されます。したがってこのソフトで作成されたサイズ変更後のファイルは
すべて横幅のドット数が同じものになります。
このソフトのもとになったのは、自炊で作成された書籍のスキャンファイルをタブレットなどで読む際にサイズを合わせるために作った
ソフトです。そのソフトでは先鋭化やコントラスト調整も行われるようになっています。
Imageはイメージデータのコンテナとしての基本ですが、画像処理を行うためには一つ一つのピクセルデータを
取り出して処理し、書き戻すといった作業が必要となります。
PixelGrabber pg = new PixelGrabber(image,0,0,w,h,pixels,0,w);
のような形で、PixelGrabberクラスインスタンスを用いて1次元配列に各ピクセルのデータを取り込みます。
配列データとして得られれば、あとは先鋭化なりぼかしなり回転、変形などは、配列データに対して行うことができます。
サンプルはJpgCnvCUI.javaがメインとなるクラスです。この中でImgPrcクラスインスタンスを作成して、その機能を使って
サイズの拡縮やBufferedImageインスタンス化などを行っています。また今回のサンプルでは使っていませんが、ImgPrcクラス
には、先鋭化、ぼかし、コントラスト強調、明度調節、上下左右反転などの機能が組み込まれています。GUI版の一括イメージサイズ
変換ソフトを作る際に使用しています。これは別のサンプルとして後日掲載します。
まずはメインクラスのJpgCnvCUI.javaです。元ファイルの入っているフォルダと変換後のファイルの格納フォルダを
自分の環境に合わせて変更してコンパイルしてください。
String inputFolderName = "d:\\work\\jpgtest\\";
String outputFolderName = "d:\\work\\";
package JpgCnvCUI; import java.awt.image.BufferedImage; import java.io.File; import javax.imageio.ImageIO; import java.io.*; import java.util.Iterator; import javax.imageio.IIOImage; import javax.imageio.ImageWriteParam; import javax.imageio.ImageWriter; import javax.imageio.stream.ImageOutputStream; public class JpgCnvCUI{ int targetWidth = 640; //画像横幅がこのサイズになるよう拡縮する String ext = ".jpg"; String inputFolderName = "d:\\work\\jpgtest\\"; String outputFolderName = "d:\\work\\"; float qt=80f; //Jpegの品質 File [] fileNameArray; // 処理対象ファイルの配列 int imageWidth; //現在の画像幅 int imageHeight; ImgPrc imgprc; public int searchFile(String dirname){ fileNameArray=null; try{ File file = new File(dirname); fileNameArray = file.listFiles(getFileFilter(ext)); } catch (Exception ex) { System.out.println(ex); } if(fileNameArray==null)return 0; return fileNameArray.length; } public static FilenameFilter getFileFilter(String fileExtension) { final String _extension = fileExtension; return new FilenameFilter() { public boolean accept(File file, String name) { boolean ret = name.endsWith(_extension); return ret; } }; } public void convert1(){ if(searchFile(inputFolderName)==0){ System.out.println("No matched file in "+inputFolderName); return; } String delim="\\"; String delimOut="\\"; String swk=inputFolderName; double mag; int slen=swk.length(); if(swk.charAt(slen-1) == '\\'){ delim=""; } swk=outputFolderName; slen=swk.length(); if(swk.charAt(slen-1) == '\\'){ delimOut=""; } for (int i = 0; i < fileNameArray.length; i++) { System.out.println("Source file-> "+inputFolderName+delim+fileNameArray[i].getName()); try{ BufferedImage image = ImageIO.read( new File(inputFolderName+delim+fileNameArray[i].getName()) ); imgprc= new ImgPrc(image); BufferedImage resizedBufImage = null; mag = ((double)targetWidth / imgprc.imageWidth); int targetHeight = (int)(imgprc.getImageHeight() * mag); imgprc.scaleImage(targetWidth, targetHeight); //サイズ拡縮 resizedBufImage = imgprc.getBufferedImage(); //BufferedImage取得 writeImage( resizedBufImage, outputFolderName+delim+fileNameArray[i].getName(), qt/100f ); System.out.println("Output folder-> "+outputFolderName); System.out.println(); }catch(Exception e){ System.out.println(e); } } } public void writeImage(BufferedImage image, String filename, float quality) { Iterator writers = ImageIO.getImageWritersByFormatName("jpeg"); if (writers.hasNext()) { ImageWriter writer = (ImageWriter)writers.next(); try { ImageOutputStream stream = ImageIO.createImageOutputStream(new File(filename)); writer.setOutput(stream); ImageWriteParam param = writer.getDefaultWriteParam(); if (param.canWriteCompressed()) { param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); param.setCompressionQuality(quality); } else { System.out.println("Compression is not supported."); } writer.write(null, new IIOImage(image, null, null), param); stream.close(); writer.dispose(); } catch (IOException ex) { ex.printStackTrace(); } } } public static void main(String args[]) { JpgCnvCUI jcnv = new JpgCnvCUI(); jcnv.convert1(); } } //End of class JpgCnvCUI
以下は、Image処理の機能を組み込んだクラスImgPrc.javaです。
package JpgCnvCUI; import java.awt.Toolkit; import java.awt.*; import java.awt.image.*; public class ImgPrc { public Image image; private int[] pixArray; int imageWidth; // = getImageWidth(); int imageHeight; // = getImageHeight(); public ImgPrc(){ } public ImgPrc(Image newimage) { //newimageをthis.imageにセット。imageWidth,hをセット。pixArrayにimageデータをセット。 this.image = newimage; this.imageWidth = getImageWidth(); this.imageHeight = getImageHeight(); this.pixArray = new int[imageWidth*imageHeight]; PixelGrabber pg = new PixelGrabber(image,0,0,imageWidth,imageHeight,pixArray,0,imageWidth); try{ //Set data into pixelArray[] pg.grabPixels(); } catch(InterruptedException ex){ System.out.println(ex); } } public void setNewImage(Image newimage){ this.image = newimage; imageWidth = getImageWidth(); imageHeight = getImageHeight(); pixArray = new int[imageWidth*imageHeight]; } public Image getCurrentImage(){ return this.image; } public BufferedImage getBufferedImage () { BufferedImage convertedImage = new BufferedImage( getImageWidth(), getImageHeight(), BufferedImage.TYPE_INT_RGB); convertedImage.getGraphics().drawImage(this.image, 0, 0, null); return convertedImage; } //配列から新たなイメージをthis.imageにセット //@SuppressWarnings("unchecked") public void setPixelsFromArray(int[] pixels,int w,int h){ MemoryImageSource m = new MemoryImageSource(w,h,pixels,0,w); image=Toolkit.getDefaultToolkit().createImage(m); } //Get image as an Array public int[] getPixArray(){ int[] pixels; if (image == null) return null; int w = getImageWidth(); int h = getImageHeight(); pixels = new int[w*h]; PixelGrabber pg = new PixelGrabber(image,0,0,w,h,pixels,0,w); try{ pg.grabPixels(); } catch(InterruptedException ex){ System.out.println(ex); } return pixels; } public int getImageWidth(){ if (this.image != null){ return this.image.getWidth(null); } else { return -1; } } public int getImageHeight(){ if (this.image != null){ return this.image.getHeight(null); } else { return -1; } } void contrastChange(int thres, double dParam, int shift) { //コントラスト強調化。thresは0から255の範囲で、明暗を変化させる境目。 //dParamは25から100程度。3乗根曲線の3次の係数。 //shiftは全体の明度をプラスあるいはマイナスにシフト。 // int[] pixels = getPixArray(); //現在のイメージを配列で取得 int p,red,green,blue; int[] adjRGB = new int[3], valRGB = new int [3]; for (int i = 0; i < imageWidth * imageHeight; i++){ //各色の切り出し p = pixArray[i]; red = (0xff & (p >> 16)); green = (0xff & (p >> 8)); blue = (0xff & p); valRGB[0] = red; valRGB[1] = green; valRGB[2] = blue; for (int j = 0; j < 3;j++){ adjRGB[j]= (int)( dParam * 25.0 * Math.cbrt((double)(valRGB[j]-thres))); } red += adjRGB[0]+shift; green += adjRGB[1]+shift; blue += adjRGB[2]+shift; if (red > 255) red = 255; if (red < 0 ) red = 0; if (green > 255) green = 255; if (green < 0 ) green = 0; if (blue > 255) blue = 255; if (blue < 0 ) blue = 0; pixArray[i] = (0xff000000 | red << 16 | green << 8 | blue); } try { setPixelsFromArray(pixArray,imageWidth,imageHeight); //新たなイメージをセット } catch (Exception ex) { } } //(x,y)座標のRGBいずれかの強度情報を返す //引数rgbは、red=2,green=1,blue=0 private int getRGBpix(int[] pix,int x,int y,int w,int h,int rgb){ if (x < 0) x = 0; if (x > w-1) x = w-1; if (y < 0) y = 0; if (y > h-1) y = h-1; int p = pix[y * w + x]; return 0xff & (p >> (8 * rgb)); } public void scaleImage(int dw, int dh) { //this.image のサイズを(dw,dh)に変更する BufferedImage resizeImage= new BufferedImage( dw, dh, BufferedImage.TYPE_3BYTE_BGR ); resizeImage.getGraphics().drawImage( this.image.getScaledInstance( dw, dh, Image.SCALE_AREA_AVERAGING ), 0, 0, dw, dh, null ); resizeImage.getGraphics().drawImage( image.getScaledInstance( dw, dh, Image.SCALE_AREA_AVERAGING ), 0, 0, dw, dh, null ); this.image=resizeImage; } public void softenImage(int op) { int[] pixels = getPixArray(); //現在のイメージを配列で取得 if (pixels == null) return; int w = getImageWidth(); int h = getImageHeight(); int i,j,k; int[] param = new int[9]; int m=0; for (i = 0;i < 9;i++){ param[i] = 2; m+=param[i]; } m-=param[4]; param[4] = op; //中心の重み m += op; //分母 int[] valRGB = new int[3]; int[] pixels2 = new int[w * h]; for (i = 0;i < h;i++){ System.out.println(i); for (j = 0;j < w;j++){ for (k = 0;k < 3;k++){ valRGB[k] = ( (getRGBpix(pixels,j-1,i-1,w, h, k) * param[0]) + (getRGBpix(pixels,j ,i-1,w, h, k) * param[1]) + (getRGBpix(pixels,j+1,i-1,w, h, k) * param[2]) + (getRGBpix(pixels,j-1,i ,w, h, k) * param[3]) + (getRGBpix(pixels,j ,i ,w, h, k) * param[4]) + (getRGBpix(pixels,j+1,i ,w, h, k) * param[5]) + (getRGBpix(pixels,j-1,i+1,w, h, k) * param[6]) + (getRGBpix(pixels,j ,i+1,w, h, k) * param[7]) + (getRGBpix(pixels,j+1,i+1,w, h, k) * param[8])) / m; if (valRGB[k] < 0) valRGB[k] = 0; if (valRGB[k] > 255) valRGB[k] = 255; } pixels2[i * w + j] = (0xff000000 | valRGB[2] << 16 | valRGB[1] << 8 | valRGB[0]); } } try { //pixels2[]をimageにセット setPixelsFromArray(pixels2,w,h); } catch (Exception ex) { ex.printStackTrace(); } } public void brightImage( double gm) { int[] pixels = getPixArray(); //現在のイメージを配列で取得 if (pixels == null) return; int w = getImageWidth(); int h = getImageHeight(); int p,red,green,blue; double gamma = gm; //2.2;//ガンマ値 for (int i = 0; i < w*h; i++){ //各色の切り出し p = pixels[i]; red = (0xff & (p >> 16)); green = (0xff & (p >> 8)); blue = (0xff & p); //ガンマ補正 red = (int)(255.0 * Math.pow(((double)red / 255.0),(1.0 / gamma))); green = (int)(255.0 * Math.pow(((double)green / 255.0),(1.0 / gamma))); blue = (int)(255.0 * Math.pow(((double)blue / 255.0),(1.0 / gamma))); if (red > 255) red = 255; if (red < 0 ) red = 0; if (green > 255) green = 255; if (green < 0 ) green = 0; if (blue > 255) blue = 255; if (blue < 0 ) blue = 0; pixels[i] = (0xff000000 | red << 16 | green << 8 | blue); } try { setPixelsFromArray(pixels,w,h); //新たなイメージをセット } catch (Exception ex) { } } public void sharpImage() { //先鋭化 int[] pixels = getPixArray(); int w,h; int i,j,k; pixels = getPixArray(); if (pixels == null) return; w = getImageWidth(); h = getImageHeight(); int[] rgb = new int[3]; int[] pixels2 = new int[w * h]; for (i = 0;i < h;i++){ for (j = 0;j < w;j++){ for (k = 0;k < 3;k++){ //原画像からラプラシアンを引く rgb[k] = getRGBpix(pixels, j, i, w, h, k) * 5 - (getRGBpix(pixels, j ,i-1, w, h, k) + getRGBpix(pixels,j-1,i , w, h, k) + getRGBpix(pixels,j+1,i , w, h, k) + getRGBpix(pixels,j ,i+1, w, h, k)); if (rgb[k] < 0) rgb[k] = 0; if (rgb[k] > 255) rgb[k] = 255; } pixels2[i * w + j] = (0xff000000 | rgb[2] << 16 | rgb[1] << 8 | rgb[0]); } } setPixelsFromArray(pixels2,w,h); } private int getRGBpixInt(int x, int y, int w, int h, int colorval){ //指定座標のr,g,bいずれかのピクセルデータを取得 //colorval: 0=blue, 1=green, 2=red int[] pixels = getPixArray(); //現在のイメージを配列で取得 if (x < 0) x = 0; if (x > w-1) x = w-1; if (y < 0) y = 0; if (y > h-1) y = h-1; int p = pixels[y * w + x]; return 0xff & (p >> (8 * colorval)); } //左右90°回転 public void turnImage(int direction){ //direction: 1=右90度 2=左90度 回転 if (this.image == null) return; int[] pix1 = this.getPixArray(); int w1 = this.getImageWidth(); int h1 = this.getImageHeight(); int i,j,p1,p2; int w2 = h1; int h2 = w1; int[] pix2 = new int[w2 * h2]; if (direction == 1){ //右90度回転 for (i = 0; i < h1; i++){ for (j = 0; j < w1; j++){ p1 = i * w1 + j; p2 = (h1 - (i + 1)) + (j * h1); pix2[p2] = pix1[p1]; } } } else if (direction == 2){ //左90度回転 for (i = 0; i < h1; i++){ for (j = 0; j < w1; j++){ p1 = i * w1 + j; p2 = (h1 * w1 - (h1 - i)) - (j * h1); pix2[p2] = pix1[p1]; } } } else { return; } this.setPixelsFromArray(pix2,w2,h2); } //上下または左右反転 public void reverseImage(int dir){ //dir: 1=right-left 2=top-bottom if (this.image == null) return; int[] pix1 = this.getPixArray(); int w1 = this.getImageWidth(); int h1 = this.getImageHeight(); int i,j,p1,p2; int[] pix2 = new int[w1 * h1]; if (dir == 1){ //左右反転 for (i = 0; i < h1; i++){ for (j = 0; j < w1; j++){ p1 = i * w1 + j; p2 = i * w1 + (w1 - j - 1); pix2[p2] = pix1[p1]; } } } else if (dir == 2){ //上下反転 for (i = 0; i < h1; i++){ for (j = 0; j < w1; j++){ p1 = i * w1 + j; p2 = (h1 - i - 1) * w1 + j; pix2[p2] = pix1[p1]; } } } else { return; } this.setPixelsFromArray(pix2,w1,h1); } }
Lenovo ノートPC ThinkPad
Java全般。
パーフェクトJava (PERFECT SERIES) (PERFECT SERIES 2)
開発技法などが出ています。
スッキリわかるJava入門 実践編
Swingの高度な使い方について参考になるかもしれません。
バイトコードやjvmについて、かなり深いレベルまでの知識を得たいときに。