Python 画像加工結果→Javaで受信→WebPage表示

画像URL → Python 処理に渡す。。。
Python PILLOW で画像加工
→ 結果をPython 実行の標準出力で出力
→ この Python 処理を Java のプロセス起動で実行して
 結果標準出力をストリームで受け取る。
→ Webページで表示
という流れのシナリオで一切画像ファイルでサーバ上のファイルシステム(ディスク)上には
置かせないというサンプルです。

Python 処理を Java のプロセス起動で実行するのは、以下を使用します。
https://github.com/yipuran/yipuran-core/wiki/Script_exec

Wicket を使用します。
Wicket WebPage で持つ変数として以下を用意します。

public String imageName = "";
public String imageWidth = "";
public String imageHeight = "";
public Integer reWidth;
public Integer reHeight;
static String TargetImage = "targetImage";
protected Key resultkey = null;
protected String imagetype = "JPEG";

ドラッグエリアは、初期画像:img タグとして以下 ResourceReferenceを定義します。

ResourceReference initImageRefer = new PackageResourceReference(SamplePage.class, "noimage.jpg");
final Image image = new Image("target", initImageRefer);
image.setOutputMarkupId(true);

AjaxFileDropBehavior をドラッグ&ドロップで実行する処理として、img タグにビヘビアとして追加します。
アップロード実行されて読み込んだ結果の処理なので結構やるべきことがいっぱいあります。

image.add(new AjaxFileDropBehavior(){
   // 画像リファレンスKey
   private Key key = initImageRefer.getKey();
   @Override
   protected void onFileUpload(AjaxRequestTarget target, List<FileUpload> files){
      Optional.ofNullable(files).ifPresent(flist->{
         if (flist.size() > 0){
            FileUpload fu = flist.get(0);
            imageName = fu.getClientFileName();
            if (!imageavailable(fu)){
               target.appendJavaScript("alert('Not Image file');");
               return;
            }
            // upload File read ⇒ byte[] ⇒ ByteArrayResource  ⇒ 画像リファレンス差し替え
            try(InputStream in = fu.getInputStream();
                ByteArrayOutputStream out = new ByteArrayOutputStream()){

               in.transferTo(out);
               // 画像表示サイズ調整 → width:420以下に抑制
               Map<String, Integer> smap = getScale(fu);
               Map<String, Integer> rmap = resize(smap, 420);
               imageWidth = smap.get("width").toString();
               imageHeight = smap.get("height").toString();
               reWidth = rmap.get("width");
               reHeight = rmap.get("height");
               // 画像リファレンス差し替え
               getApplication().getSharedResources().remove(key);
               getApplication().getSharedResources()
               .add(TargetImage, new ByteArrayResource("image/jpeg", out.toByteArray()));
               ResourceReference reference = new SharedResourceReference(TargetImage);
               key = reference.getKey();
               image.setImageResourceReference(reference);
               target.add(image);
               target.add(info);
               String jscript
 = "$('#target').css('width','"+rmap.get("width")+"px'),$('#target').css('height','"+rmap.get("height")+"px');";
               target.appendJavaScript(jscript);
               target.appendJavaScript("$('#convert').prop('disabled',false);");
            }catch(Exception ex){
               logger.error(ex.getMessage(), ex);
            }
         }
      });
   }
   protected boolean imageavailable(FileUpload fu){
      String c = fu.getContentType();
      if (c.equals("image/jpeg")){
         imagetype = "JPEG";
         return true;
      }
      if (c.equals("image/png")){
         imagetype = "PNG";
         return true;
      }
      if (c.equals("image/gif")){
         imagetype = "GIF";
         return true;
      }
      return false;
   }
   protected Map<String, Integer> getScale(FileUpload f) throws Exception{
      Map<String, Integer> r = new HashMap<>();
      try(InputStream in = f.getInputStream()){
         BufferedImage bimg = ImageIO.read(in);
         r.put("width", bimg.getWidth());
         r.put("height", bimg.getHeight());
         return r;
      }
   }
   protected Map<String, Integer> resize(Map<String, Integer> m, int widthmax){
      int width = m.get("width");
      if (width > widthmax){
         Map<String, Integer> r = new HashMap<>();
         r.put("width", widthmax);
         BigDecimal k = new BigDecimal(widthmax).divide(new BigDecimal(width), 8, RoundingMode.HALF_UP);
         r.put("height", new BigDecimal(m.get("height")).multiply(k).setScale(0, RoundingMode.HALF_UP).intValue());
         return r;
      }
      return m;
   }
});
queue(image);

表示する画像は、ResourceReference で宣言、
setOutputMarkupId(true) でPythonで画像加工後に、表示更新できるようにしておきます。

final Image result = new Image("result", new PackageResourceReference(Sample.class, ""));
result.setOutputMarkupId(true);
queue(result);

Python に渡す URL です。→ 今回のサンプルは自分のサイトです。。

String url = "http://xxxxx/example/wicket/resource/org.apache.wicket.Application/" + TargetImage;

ボタンによる Python 実行→ 結果受け取り画面表示処理

queue(new Button("convert").add(AjaxEventBehavior.onEvent("click", t->{
   int sts = ScriptExecutor.runStream(()->"python " + pyscript + "/image/img_invert.py "
   , ()->Arrays.asList(url, "\n", imagetype, "\n\n")
   , inst->{
      try(ByteArrayOutputStream out = new ByteArrayOutputStream()){
         inst.transferTo(out);
         out.flush();
         if (resultkey != null) getApplication().getSharedResources().remove(resultkey);
         getApplication().getSharedResources()
            .add("resultgaus", new ByteArrayResource("image/jpeg", out.toByteArray()));
         ResourceReference reference = new SharedResourceReference("resultgaus");
         resultkey = reference.getKey();
         result.setImageResourceReference(reference);
         t.add(result);
         String jscript = "$('#result').css('width','"+reWidth+"px'),$('#target').css('height','"+reHeight+"px');";
         t.appendJavaScript(jscript);
      }catch(Exception ex){
         throw new RuntimeException(ex);
      }
   }, (e, x)->{
      logger.error(e);
      logger.error(x.getMessage(), x);
   });
})));

JavaScript として用意しておくソース、特定部分だけのドラッグにします。
ページ全体ではドラッグとドロップ操作不可にする。
しかし、AjaxFileDropBehavior 適用部分は、ドラッグ&ドロップ可能である。

window.addEventListener('dragover', function(ev){
  ev.preventDefault();
}, false);
window.addEventListener('drop', function(ev){
  ev.preventDefault();
  ev.stopPropagation();
}, false);

URL と画像タイプを標準入力で受け取ってURLが指す画像を取得して
ネガポジ変換して標準出力する Python

# -*- coding: UTF-8 -*-
from PIL import Image, ImageOps
import io
import sys
import requests

def imageinvert(url, type):
    image = requests.get(url).content
    img = Image.open(io.BytesIO(image))
    res = ImageOps.invert(img)
    #---- バイトで標準出力 ----
    po = io.BytesIO();
    res.save(po, format=type)
    sys.stdout.buffer.write(po.getvalue())
#######################
if __name__ == '__main__':
    inlist = []
    try:
        while True:
            inp = input('')
            if inp=='':break
            inlist.append(inp)
    except EOFError:
        pass
    # [0]=URL,  [1]=image type 'JPEG' or 'PNG'
    imageinvert(inlist[0], inlist[1])

Wicket ページHTML

<div class="drop-content">
   <img wicket:id="target" id="target" alt="drop target image">
</div>
<div wicket:id="info">
   <div wicket:id="imageName"></div>
   <div>width : <span wicket:id="width"></span>  height : <span wicket:id="height"></span></div>
</div>
<div>
   <button wicket:id="convert" id="convert" type="button" disabled="disabled">Pillow invert実行</button>
</div>

<div class="drop-content">
   <img wicket:id="result" id="result">
</div>

初期表示
f:id:posturan:20190501143415j:plain
画像をドラッグ&ドロップしてボタンを押すと、、
f:id:posturan:20190501143525j:plain