본문 바로가기
Nodejs

Nodejs python-shell image 이미지 파일 Json으로 주고 받기

by ahsung 2020. 1. 16.

 

Nodejs에서는 python과의 연동을 하기 위해서 python-shell이라는 모듈을 사용 할 수 있습니다.

 

nodejs를 통해 받은 이미지를 python에서 가공하여 다시 반환하는 코드를 작성하겠습니다.

 

 

처음 pyshell 모듈을 사용한 연동을 구상할 때

binary mode로 bytes 정보를 넘겨서 처리하는 것을  가장 먼저 떠올리고 여러번 시도하며,

api문서도 다시보고, 여기저기 글들을 찾아보았지만..

다음과 같은 이유들로 실패하였다.

 

요약: JSON을 사용해서 node <-> python 통신을 하자!

*  기본적으로 python-shell 모듈은 python을 직접구동하여, 표준입출력(stdIO)와 argv를 통해 정보를 주고받는다.

 

*  binary모드로 보내면 물론 이진코드로 번역해서 전송하는듯 하나...

*  결국 표준입출력 형태이기 때문에 파이썬으로 넘어갈 때는 문자열 자체로 인식되어 버리는 문제가 생긴다.

 

*  예시로, "hello" 라는 문자열을 binary로 보내게되면, 예를들어  \xf2\xff\x??~~~ 형태의 byte 코드로 전송될 것이다.

*  (위의 바이트코드는 정확한 hello의 코드가 아닌 그냥 가정이다.)

 

*  하지만 표준입력으로 들어갈때는 결국 "\xf2\xff\x??~~~~" 이걸 묶은 자체의 문자열이 되어 버린다.

*  예를 들어   숫자 1의 이진코드는 x01  이다, 하지만  문자열 "1"의 이진코드는 아스키 코드인 49 = x31 이다.

 

*  즉 차라리 그냥 hello라고 보냈으면 hello로 그대로 받아들여서 문제가 없었지만,

*  문자열이 아닌 다른 형태로 보낸 형식이 문자열 그 자체로 다시 받아들여지면서 값이 변화되어 버렸다..

 

*  python api도 이런저런 찾아보았고 물론 표준입력을 byte코드로 받는 sys.stdin.buffer.read() 함수등도

*  사용해 보았지만, 이 방법도 이미 문자열화 되어버린 코드를 다시 문자열의 byte코드로 읽을 뿐이었다..

(문자열을 byte 처럼 파싱이 아니다..)

 

*  (어차피 표준 입력을 하려고 하는 순간... string으로 변할거라면 굳이 왜 binary 모드를 넣었는지는 모르겠다..)

 

*  binary모드는 그나마 binary형태로라도 보내주니 문자열을 파싱하면 불가능한것은 아니나.. 

* 다행이도 nodejs와 python은 이미 훌륭한 JSON이라는  공통의 파싱도구를 가지고 있다.!!

 

 

 

javascript

 

var Pythonshell = require("python-shell");
var fs = require("fs");

// nodejs에서 이미지파일 받기
var data = fs.readFileSync("../../detImage/i2.jpeg");

pyshell = new Pythonshell.PythonShell("./scripts.py");

//json으로 묶어준다.
dat = {
  binary: data
};

//json으로 submit
pyshell.send(JSON.stringify(dat), { mode: "json" });

//python에서 가공후
//string 형태의 json으로 반환.
pyshell.on("message", results => {
  //다시 json으로 변환후 획득
 var imgdata = JSON.parse(results).img;
 console.log(imgdata);
  
//imgdata는 이진정보를 리스트로 담고 있기 때문에
// Buffer 타입으로 변환시켜준다.
imbuffer = new Buffer.from(imgdata);

// local write
// 이미지를 로컬에 저장하여 확인해본다.
fs.writeFileSync("temp.jpeg", imbuffer);
console.log("complete");
});

//error 처리
pyshell.end(err => {
  if (err) {
    console.log(err);
  }
});

 

python code

다행인 것은 nodejs에서는 Byte코드를 나타내는 객체 Buffer가 array(list)와 호환되는 형태라는 점이다.

 

위의 코드와 같이 json 객체로 묶어준후,  JSON.stringify()를 통해 json을 문자열 형태로 만들어 보내준다.

 

json은 여러 언어들에서 쓰이기위해 보통의 통신을 문자열 자체로 주고 받고

 

각 언어가 파싱하여 정보를 캐내는 방식을 취하고 있다.

 

 

import sys
import numpy as np
import json
import cv2

# json으로 받기.
inputs = sys.stdin.read()
# 문자열로 받은 형태를 json형태로 반환해준다.(dict)
dat = json.loads(inputs)

# 그 중에서 array로 담겨져있던 binary코드를 가져온다.
binary_arry = dat['binary']['data']

#opencv는 바이너리코드를 인코딩하여
#python에서 컨트롤 가능한 비트맵으로 만들어 줄 수 있다.
#각 np의 원소는 uint8이어야 한다. 1byte = 8bits
binary_np = np.array(binary_arry, dtype=np.uint8)

# data cv2 np convert
img_np = cv2.imdecode(binary_np, cv2.IMREAD_ANYCOLOR)

# 좌측 끝상단에 검은색 네모를 칠한다.
img_np[0:50, 0:50] = 0

# convert bytes
# 다시 byte형태를 담은 list로 바꾸어준다.
_, imen = cv2.imencode('.jpeg', img_np)
imenb = bytes(imen)
imnb = list(imenb)

#보낼때 역시 json으로 보내준다.
result = json.dumps({'img': imnb})
print(result)

 

 

  

 

python에서는  json을 다시 파싱하여 dict형태로 변환후

원하는 byte 정보가 담긴 list를 획득하여 

 

이미지를 opencv 라이브러리를 통해 원하는데로 변형후

다시 byte 정보를 담은 list를 json으로 묶어 전송하였다.

 

1bytes는 8비트 즉 0~255까지이며, int로 치환한 List로 전송하였고

python3는 다행히도 list와 bytes가 쉽게 호환이 가능하여 변환이 매우 간단하였다.

물론 시도해보지 않았지만, c에서도리스트문자열을 char 배열의 한바이트씩 읽을 수 있게 알고리즘을 구현한다면,

충분히 응용 가능할 것 같다

 

1. dumps -> 문자열형태로 전송

 

2. 다시 nodejs에서는 parse() 

 

3. Buffer타입으로 변환후 local에 write 해보면,

 

원본이미지가 opencv를 통한 변화가 생긴 모습을 확인 할 수 있다.

변환시켜 받아온 byte list 형태이다.

 

원본 이미지
위 예제 코드결과

 

python detectron2 반환 결과.

https://asung123456.tistory.com/16

 

Nodejs python-shell binary 연동, 바이트 파일 주고받기. example opencv image 변경

다른 포스팅에서는 opencv형태로 image를 전송하기위해 json을 이용하여 bytes 배열을 보내는 방법을 사용하였다. 표준입출력의 특성상 어쨌든 보내면 문자열로 변하는 문제가 있어 결국 파싱의 문제가 생겨 우회..

asung123456.tistory.com

나중에 알게 된 사실이지만, 굳이 이미지 파일을 보낸다면, Json으로 묶는 것보다 

base64로 인코딩하는게 더 깔끔하다는 걸 알게됬다..

 

base64는 byte stream을 문자열로 저장하는 인코딩 방식이다.

byte stream을 DB에 저장할때도 많이 사용하는 방식으로 알고 있다.

댓글