ともさんのHP >プロブラミング >自作JavaLibrary >画像を切り取って保存

画像を切り取って保存するJavaFXアプリ

読み込んだ画像から必要な部分を切り取ってファイルに出力するアプリの紹介です。
JavaFXで作りました。
ソースコードを公開するのでご自由に改変してお使いください。

広告


ファイルの構成

PhotoCut.javaとContorolPane.javaの二つのファイルからなっています。
PhotoCut.javaはメインのステージ、ContorolPane.javaはボタンやテキストフィールドを配置するパネルです。

使いかた

まず切り取りたい画像を読み込みます。
切り取って保存したい画像のサイズをキー入力します。保存したい範囲がちょうど入るようにスケールを修正します。
画像上の四角い範囲が切り取り範囲となります。
切り取りたい位置でマウスを左クリックするとファイルが保存されます。
ファイル名はtest.pngで、実行フォルダに保存されます。

アプリの動作説明

PhotoCut.classでアプリを起動します。
ContorolPaneオブジェクトのファイル読み込みボタン楽天 を押すとファイルチューザーが表示されます。ここで切り取る画像ファイルを選択します。
画像ファイルが読み込まれると画像上でマウスが動くイベントイベントが発生し、赤い切り取り範囲を示す領域を描画します。
ContorolPaneに置かれた選択範囲の数値を変更すると、赤い四角の大きさが変更されるようになっています。

スケールの数値を変えると画像を読み込んでいるイメージビューオブジェクトのスケールが変更されるようになっています。
画像の上でマウスがクリックされると画像の指定範囲が新しいイメージとして作成されファイル上に保存されます。
マウス位置のピクセルの色が表示されますが、大きな画像を使って画像をスクロールさせると、位置がちょっとずれてしまいます。
テキストフィールドに数値以外を入れるとエラーが出ます。

ソースコード

ソースコードを下に示します。
デフォルトの読み込みフォルダや保存フォルダー保存ファイル名はお好みで変更すると良いでしょう。
JavaFX楽天 で動くので Java 楽天 のバージョンは8以上を使います。

package tomojavalib.syasin;

import java.awt.image.BufferedImage;
import java.io.File;

import javax.imageio.ImageIO;

import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;


/**写真を指定の範囲でカットしたり色構成を出力したりする*/
public class PhotoCut extends Application {
//画像表示
ImageView imgView = null;
//このステージ
Stage stage = null;
//ボーダーpane
BorderPane pane = null;
//コントロールパネル
public ContorolPane clp = null;
//写真を置くPane
public ScrollPane sp =null;
//切り取り範囲
Rectangle rect = new Rectangle( 50 , 50 );
//イメージサイズ
double imgw = 0;
double imgh = 0;

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

 @Override
 public void start(Stage stage) throws Exception {
  this.stage = stage;
  stage.setTitle("写真加工");
  pane = new BorderPane();
  Scene scene = new Scene(pane, 800, 600);
  clp = new ContorolPane( this );
  pane.setRight( clp );
  sp = new ScrollPane();
  pane.setCenter( sp );
  stage.setScene(scene);
  stage.show();
 }

/**
 * 画像を表示させる
 * */
public void showGazou(){
BufferedImage imgsw = null;
try{
 imgsw = javax.imageio.ImageIO.read( clp.file );
}catch(Exception e){}
if(imgsw!=null){
 Image image = SwingFXUtils.toFXImage( imgsw  , null);
 imgView = new ImageView( image );
 sp.setContent( imgView );

EventHandler<MouseEvent> mouseClick = ( event ) -> this.mouseClick( event );
 this.imgView.addEventHandler( MouseEvent.MOUSE_CLICKED , mouseClick );
 EventHandler<MouseEvent> move = ( event ) -> this.mouseMove( event );
 this.sp.addEventHandler( MouseEvent.MOUSE_MOVED , move );

        rect = new Rectangle( 50 , 50 );
        rect.setFill(null);
        rect.setStroke(Color.RED);
        pane.getChildren().add(rect);

Image img = this.imgView.getImage();
//イメージサイズ
 imgw = img.getWidth();
 imgh = img.getHeight();

}
}

/**マウスクリック時の処理*/
public void mouseClick( MouseEvent e ) {
//System.out.println("クリック " + e.getX() +" " + e.getY() );
this.saveCutFile( e.getX() , e.getY() );
}

/**指定範囲の画像を切り抜いて保存する*/
private void saveCutFile( double x , double y ) {
Image outimg = this.makeImage( x , y );

//File f = new File( "D:/work/digi_came-kuro/tmp/test.png");
File f = new File( "./test.png");
try {
ImageIO.write( SwingFXUtils.fromFXImage(outimg, null) , "png", f);
}catch(Exception e) {}

if(cutview!=null) { clp.getChildren().remove(cutview); }
cutview = new ImageView( outimg );
cutview.setPreserveRatio(true);;
cutview.setFitWidth(clp.getWidth());

clp.getChildren().add(cutview);
//範囲内の数値を出力
//this.makeSuuti(x,y);
}

ImageView cutview = null;


private void makeSuuti( double x , double y ) {
//画像中心点x,y
//画像幅、高さ
double w =Double.parseDouble( clp.t[0].getText() );
double h =Double.parseDouble( clp.t[1].getText() );
//スケール
double scale = Double.parseDouble( this.clp.t[2].getText() );
//画像開始位置
double sx = x- w/2./scale;
double sy = y- h/2./scale;
//画像
Image img = this.imgView.getImage();

//image作成
WritableImage outimg = new WritableImage( (int)w , (int)h );
for( int ix =0 ; ix<w;ix++ ) {
 for( int iy =0 ; iy<h;iy++ ) {
 int ixx =(int)(ix/scale+sx);
 int iyy =(int)(iy/scale+sy);
  if( ixx>-1 ) {if( iyy>-1 ) {if( ixx<imgw ) {if( iyy<imgh ) {
  String s = ixx +"," +iyy +","+ img.getPixelReader().getArgb(ixx, iyy) ;
  int a = img.getPixelReader().getArgb(ixx, iyy) ;
  int b = (a>>>8)<<8;
  b = a-b;
  int g =(a>>>16)<<16;
  g = (a-g)>>>8;
   int r =(a>>>24)<<24;
  r = (a-r)>>>16;
   System.out.println(s+" "+r+" "+g+" "+b);
  }}}}
}}

}

public Image makeImage( double x , double y ) {
//画像中心点x,y
//画像幅、高さ
double w =Double.parseDouble( clp.t[0].getText() );
double h =Double.parseDouble( clp.t[1].getText() );
//スケール
double scale = Double.parseDouble( this.clp.t[2].getText() );
//画像開始位置
double sx = x- w/2./scale;
double sy = y- h/2./scale;
//画像
Image img = this.imgView.getImage();

//image作成
WritableImage outimg = new WritableImage( (int)w , (int)h );
for( int ix =0 ; ix<w;ix++ ) {
 for( int iy =0 ; iy<h;iy++ ) {
 int ixx =(int)(ix/scale+sx);
 int iyy =(int)(iy/scale+sy);
  if( ixx>-1 ) {if( iyy>-1 ) {if( ixx<imgw ) {if( iyy<imgh ) {
   outimg.getPixelWriter().setArgb( ix , iy ,   img.getPixelReader().getArgb(ixx, iyy)   );
  }}}}
}}
return outimg ;
}

/**マウスが動いた時の処理*/
public void mouseMove( MouseEvent e ) {
double w =Double.parseDouble( clp.t[0].getText() );
double h =Double.parseDouble( clp.t[1].getText() );
rect.setTranslateY(e.getY()-h/2.);
rect.setTranslateX(e.getX()-w/2.);
rect.setWidth( w );
rect.setHeight( h );
this.getColor( e.getX() , e.getY() );
e.consume();
}

/**指定位置の色値を返す*/
private String getColor( double x , double y ) {
String s = "";
//スケール
double scale = Double.parseDouble( this.clp.t[2].getText() );
//スクロール分の補正
double spx = (imgView.getImage().getWidth() - sp.getWidth() )*sp.getHvalue();
double spy = ( imgView.getImage().getHeight() - sp.getHeight() )*sp.getVvalue();
 int xx =(int)((x+spx)/scale);
 int yy =(int)((y+spy)/scale);
  int argb = imgView.getImage().getPixelReader().getArgb( (int)xx , (int)yy ) ;
  int b = (argb>>>8)<<8;
  b = argb-b;
  int g =(argb>>>16)<<16;
  g = (argb-g)>>>8;
   int r =(argb>>>24)<<24;
  r = (argb-r)>>>16;
   int a =(argb>>>32)<<32;
 a = (argb-r)>>>24;
  s = "■"+xx +" "+ yy +" "+ r +" "+ g +" "+ b;
  Color c = new Color( r/255.,g/255.,b/255. ,a/255.);
  clp.l3.setTextFill( c );
  clp.l3.setFont(new Font(20));
 // System.out.println(s+" "+"");
  clp.l3.setText( s );
return s;
}
}

package tomojavalib.syasin;

import java.io.File;
import java.math.BigDecimal;
import java.util.Vector;

import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.Vbox;
import javafx.scene.paint.Color;
import javafx.stage.FileChooser;
import javafx.stage.FileChooser.ExtensionFilter;
import tomojavalib.p2cad.teigistage.TTextField;

/**写真の選択、名前変更を行う
 * */
public class ContorolPane extends FlowPane{
PhotoCut app = null;
public TextField[] t = null;
public File file = null;
 public Label l3 =null;

public ContorolPane( PhotoCut app ){
 this.app = app;
 this.setBackground(new Background(new BackgroundFill( Color.ANTIQUEWHITE , CornerRadii.EMPTY , Insets.EMPTY )));
  Button b = new Button( "ファイル読み込み" );
 t = new TextField[3];
 t[0] = new TextField("320");
 t[1] = new TextField("240");
 Label l1 = new Label( "選択範囲" );
 l1.setPrefSize(180, 20);
 t[0].setPrefSize(100, 20);
 t[1].setPrefSize(100, 20);
 this.getChildren().add(b);
this.getChildren().add( l1 );
 this.getChildren().add( t[0] );
 this.getChildren().add( t[1] );
  Label l2 = new Label( "スケール" );
 l2.setPrefSize(100, 20);
  t[2] = new TextField("1");
 t[2].setPrefSize(100, 20);
 this.getChildren().add( l2 );
 this.getChildren().add(t[2]);
 l3 = new Label( " " );
 l3.setPrefSize(180, 20);
  this.getChildren().add( l3 );

 this.setPrefWidth(200);

  //イベント設定
 EventHandler<MouseEvent> mouseClick = ( event ) -> this.mouseClick( event );
 b.addEventHandler( MouseEvent.MOUSE_CLICKED , mouseClick );
//マウスのホイールが動いたとき
 EventHandler<ScrollEvent> mouseScroll = ( event ) -> this.mouseScroll( event );
 t[2].addEventHandler( ScrollEvent.SCROLL, mouseScroll );
}

private File getNewFile(){
 FileChooser fc = new FileChooser();
 //タイトル設定
 fc.setTitle("ファイルリスト読み込み");
 //読み込めるファイルの拡張子を限定
 fc.getExtensionFilters().addAll( new ExtensionFilter("画像ファイル", "*.jpg","*.png", "*.JPG","*.PNG") );
 //初期ディレクトリー
 //fc.setInitialDirectory( new File("D:/work/digi_came-kuro/") );
  fc.setInitialDirectory( new File("./") );
 File file = fc.showOpenDialog( app.stage );
 return file;
}

/**
 * フォルダを選んで中の画像ファイルリストを取り出す
 * */
private void mouseClick( MouseEvent e ){
 //フォルダを読み込む
 file = this.getNewFile();
  //nullなら帰る
 if(file==null){ return; }

 //画像ファイル以外は外す処理
 Vector v = new Vector();
 boolean flug = false;
 flug = false;
 if( file.getName().indexOf(".jpg")>-1 ){ flug = true; }
 if( file.getName().indexOf(".png")>-1 ){ flug = true; }
 if( file.getName().indexOf(".JPG")>-1 ){ flug = true; }
 if( file.getName().indexOf(".PNG")>-1 ){ flug = true; }

//appに画像を表示
app.showGazou();

return;
}


/**マウススクロール時の設定*/
private void mouseScroll( ScrollEvent e ){
TextField t = (TextField)e.getSource();
double d=0.;
 String s = t.getText();
 d = Double.parseDouble( s );

 if( e.getDeltaY()>0 ){ d=d+0.01; }else{ d=d-0.01; }
  if( d<=0 ){ d = 0.01; }
 BigDecimal bd = new BigDecimal( d );
 bd = bd.setScale(5, BigDecimal.ROUND_HALF_UP);
 t.setText( bd.doubleValue() + "" );
 app.imgView.setScaleX(d);
 app.imgView.setScaleY(d);
 app.imgView.setTranslateX( -app.imgw*(1-d)/2. );
 app.imgView.setTranslateY( -app.imgh*(1-d)/2. );
 e.consume();
}


}

最終更新日: 2019-08-07 09:25:59

ともさんのHP >プロブラミング >自作JavaLibrary >画像を切り取って保存

広告
新着ページ

女王アリ探し  
Obj形式の3Dデータ  
レーザーカッターで布を裁断  
アカウキクサ  
エアコンを使わなくても涼しい私の家  
昔の玩具の作りかた  
洋裁に使えるCAD  
渋柿で柿渋作り  
新旧文化式とドレメ式原型製図を比較  
水棚の作り方  
DXFファイルの違い  

他のサイト

3D-CAD
洋裁CAD

いいねなど

 RSS 

Author: Tomoyuki Ito

このサイトの文章・写真の無断転載を禁じます