# worker.py
import sys
import json
import struct
import traceback
import numpy as np
def read_exact(stream, size: int) -> bytes:
"""
指定byte数を必ず読む。
足りない場合はEOF扱い。
"""
data = bytearray()
while len(data) < size:
chunk = stream.read(size - len(data))
if not chunk:
raise EOFError("stdin closed while reading data")
data.extend(chunk)
return bytes(data)
def read_packet():
"""
C#から送られてくる1パケットを読む。
形式:
4byte: JSONヘッダ長 little-endian uint32
Nbyte: JSONヘッダ
Mbyte: 画像データ
"""
# 4byteのヘッダ長を読む
header_len_bytes = read_exact(sys.stdin.buffer, 4)
header_len = struct.unpack("<I", header_len_bytes)[0]
# JSONヘッダを読む
header_json = read_exact(sys.stdin.buffer, header_len).decode("utf-8")
header = json.loads(header_json)
# 画像本体を読む
byte_length = int(header["byte_length"])
image_bytes = read_exact(sys.stdin.buffer, byte_length)
return header, image_bytes
def write_packet(header: dict, image_bytes: bytes):
"""
C#へ1パケット返す。
"""
header_json = json.dumps(header, ensure_ascii=False).encode("utf-8")
header_len_bytes = struct.pack("<I", len(header_json))
sys.stdout.buffer.write(header_len_bytes)
sys.stdout.buffer.write(header_json)
sys.stdout.buffer.write(image_bytes)
sys.stdout.buffer.flush()
def process_image_dummy(header: dict, image_bytes: bytes):
"""
ダミー処理。
受け取ったMono8画像をnumpy配列にして、
今回は何もせずそのまま返す。
"""
width = int(header["width"])
height = int(header["height"])
pixel_type = header["pixel_type"]
if pixel_type != "mono8":
raise ValueError(f"Unsupported pixel_type: {pixel_type}")
expected_len = width * height
if len(image_bytes) != expected_len:
raise ValueError(
f"Invalid image size. expected={expected_len}, actual={len(image_bytes)}"
)
# byte列 → numpy画像
img = np.frombuffer(image_bytes, dtype=np.uint8).reshape((height, width))
# 例:
# processed = cv2.GaussianBlur(img, (3, 3), 0)
#
# 今回はダミーなのでそのまま返す
processed = img
# numpy画像 → byte列
result_bytes = processed.tobytes()
result_header = {
"frame_id": header.get("frame_id"),
"ok": True,
"width": width,
"height": height,
"pixel_type": "mono8",
"byte_length": len(result_bytes),
"message": "dummy passthrough"
}
return result_header, result_bytes
def main():
# 重要:
# stdoutはC#へのバイナリ応答専用。
# ログは必ずstderrへ出す。
print("Python worker started", file=sys.stderr, flush=True)
while True:
try:
header, image_bytes = read_packet()
result_header, result_bytes = process_image_dummy(header, image_bytes)
write_packet(result_header, result_bytes)
except EOFError:
print("stdin closed. worker exiting.", file=sys.stderr, flush=True)
break
except Exception as e:
# エラー時もC#が読めるように、エラーパケットを返す
print("Error occurred:", file=sys.stderr, flush=True)
print(traceback.format_exc(), file=sys.stderr, flush=True)
error_header = {
"frame_id": None,
"ok": False,
"width": 0,
"height": 0,
"pixel_type": "mono8",
"byte_length": 0,
"message": str(e)
}
write_packet(error_header, b"")
if __name__ == "__main__":
main()
# ------
# worker.py
import sys
import json
import struct
import traceback
import numpy as np
def read_exact(stream, size: int) -> bytes:
data = bytearray()
while len(data) < size:
chunk = stream.read(size - len(data))
if not chunk:
raise EOFError("stdin closed while reading data")
data.extend(chunk)
return bytes(data)
def read_packet():
header_len_bytes = read_exact(sys.stdin.buffer, 4)
header_len = struct.unpack("<I", header_len_bytes)[0]
header_json = read_exact(sys.stdin.buffer, header_len).decode("utf-8")
header = json.loads(header_json)
byte_length = int(header["byte_length"])
image_bytes = read_exact(sys.stdin.buffer, byte_length)
return header, image_bytes
def write_packet(header: dict, image_bytes: bytes):
header_json = json.dumps(header, ensure_ascii=False).encode("utf-8")
header_len_bytes = struct.pack("<I", len(header_json))
sys.stdout.buffer.write(header_len_bytes)
sys.stdout.buffer.write(header_json)
sys.stdout.buffer.write(image_bytes)
sys.stdout.buffer.flush()
def process_image_dummy(header: dict, image_bytes: bytes):
width = int(header["width"])
height = int(header["height"])
pixel_type = header["pixel_type"]
if pixel_type != "mono8":
raise ValueError(f"Unsupported pixel_type: {pixel_type}")
expected_len = width * height
if len(image_bytes) != expected_len:
raise ValueError(
f"Invalid image size. expected={expected_len}, actual={len(image_bytes)}"
)
img = np.frombuffer(image_bytes, dtype=np.uint8).reshape((height, width))
# ==============================
# ここに本来のPython画像処理を書く
# 今回はダミーなのでそのまま返す
# ==============================
processed = img
result_bytes = processed.astype(np.uint8).tobytes()
result_header = {
"frame_id": header.get("frame_id"),
"ok": True,
"width": width,
"height": height,
"pixel_type": "mono8",
"byte_length": len(result_bytes),
"message": "dummy passthrough"
}
return result_header, result_bytes
def main():
print("Python worker started", file=sys.stderr, flush=True)
while True:
try:
header, image_bytes = read_packet()
result_header, result_bytes = process_image_dummy(header, image_bytes)
write_packet(result_header, result_bytes)
except EOFError:
print("stdin closed. worker exiting.", file=sys.stderr, flush=True)
break
except Exception as e:
print("Error occurred:", file=sys.stderr, flush=True)
print(traceback.format_exc(), file=sys.stderr, flush=True)
error_header = {
"frame_id": None,
"ok": False,
"width": 0,
"height": 0,
"pixel_type": "mono8",
"byte_length": 0,
"message": str(e)
}
write_packet(error_header, b"")
if __name__ == "__main__":
main()