msgpack + zeromqでなんでも送る
メリット
圧縮してtcp通信をするシンプルな発想、手順もすくない
NDIやspoutでメタ情報などが送りづらいとき
フレームにあわせていろいろ情報をつっこめる
画像をほぼそのままおくるのは確かに非効率ではあるが、特定の環境化でラフに行うという代替的発想
JPEGをそのまま送れる(NDIのような圧縮プロセスがない)
C++、pythonで問題なく実装できる
Unity C#もたぶんいけそう
UEでも試せるかも
デメリット
oFの構築はだるい…😡
帯域幅はそこそこあるはず
tdは受けやすいのか?
ScriptDATでうけるしかなさそうなのであまり良い策ではない
送りはpythonそのままなのでOK
シナリオ
例えば python -> oF でフレーム画像とフレームにひもづくメタデータ
元画像と解析結果情報
画像
PIL(py), turbojpeg(C++)でJPEG圧縮 -> msgpackで圧縮 -> バイト列として送信
逆手順で解凍
構造体
そのままオブジェクトとして格納
C++では型に厳格なので、Pythonなどでふわっとした構造体を送ると謎のエラー(bad castの例外)に苦しむので注意
よくあるのはpythonで整数値が浮動小数でなどで型定義がされており、C++側で例外がでちゃうなど
python
code:cmd
pip install msgpack zmq
code:send.py
import zmq
import msgpack
# ZeroMQの設定
ZMQ_IP = "127.0.0.1" # ZeroMQ受信側のIPアドレス
ZMQ_PORT = 5555 # ZeroMQ受信側のポート
context = zmq.Context()
socket = context.socket(zmq.PUB)
socket.bind(f"tcp://{ZMQ_IP}:{ZMQ_PORT}")
while True:
# ... 色々な処理
# フレーム内のオブジェクト情報を収集
frame_data = []
for box in results0.boxes: # 例えばオブジェクト検出のバウンディングボックスがあるとする x1, y1, x2, y2 = box.xyxy0.tolist() confidence = box.conf0.item() class_id = int(box.cls0.item()) tracking_id = box.id0.item() if box.id is not None else -1 # トラッキングID tracking_id = int(tracking_id) # 浮動小数と解釈されないよう明示的に整数値に
frame_data.append({ "label": label, "confidence": confidence, "x1": x1, "y1": y1, "x2": x2, "y2": y2, "tracking_id": tracking_id})
# フレームデータをMessagePackでシリアライズしてZeroMQで送信
packed_data = msgpack.packb(frame_data)
socket.send(packed_data)
socket.close()
context.term()
oF
include, libファイルは古いので必要に応じて本家から持ってくる
boostがoFの内部にあるものだとファイルが足りないので、まるごと差し替える…
これはだるい…🤬
of_root/libs/boostの中身を差し替えてoFをリビルド
include, libファイルは古いので必要に応じて本家から持ってくる
code:ofApp.h
struct BoxData {
std::string label;
double confidence = 0.0;
double x1 = 0.0;
double y1 = 0.0;
double x2 = 0.0;
double y2 = 0.0;
int tracking_id = -1;
MSGPACK_DEFINE_MAP(confidence, x1, y1, x2, y2, tracking_id)
};
class ofApp : public ofBaseApp{
public:
void setup();
void update();
void draw();
void exit();
void keyPressed(int key);
private:
ofxZmqSubscriber subscriber;
ofxMessagePack::Unpacker unpacker;
};
code:ofApp.cpp
void ofApp::setup() {
ofSetFrameRate(60);
ofSetVerticalSync(false);
subscriber.connect("tcp://127.0.0.1:5555");
}
void ofApp::update() {
while (subscriber.hasWaitingMessage()) {
ofBuffer data;
subscriber.getNextMessage(data);
msgpack::object_handle oh = msgpack::unpack(data.getData(), data.size());
msgpack::object obj = oh.get();
std::vector<msgpack::object> elements;
obj.convert(elements);
std::vector<BoxData> boxes;
for (auto& element : elements) {
BoxData boxData;
ofLogNotice() << "element: " << element;
element.convert(boxData);
}
}
}
void ofApp::draw() {
}
void ofApp::exit() {
}
void ofApp::keyPressed(int key) {
}
JPEGの送受信
code:python
from PIL import Image
# フレームをJPEG形式に変換
pil_image = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
buffer = io.BytesIO()
pil_image.save(buffer, format="JPEG")
jpeg_frame = buffer.getvalue()
# フレームデータとJPEG画像をMessagePackでシリアライズしてZeroMQで送信
packed_data = msgpack.packb({"frame_data": frame_data, "jpeg_frame": jpeg_frame})
socket.send(packed_data)
code:C++
ofxZmqSubscriber subscriber;
ofxMessagePack::Unpacker unpacker;
ofxTurboJpeg turboJpeg;
// ofxZmqを用いた受信
while (subscriber.hasWaitingMessage()) {
ofBuffer data;
subscriber.getNextMessage(data);
// ofxMessagePackを用いたデータのアンパック
unpacker.setBuffer(data);
FrameData frameData;
unpacker >> frameData;
// ofxTurboJpegを用いたJpegのofIamgeへの読み出し
ofImage image;
ofBuffer rawJpegBuffer((const char*)frameData.jpeg_frame.data(), frameData.jpeg_frame.size());
turboJpeg.load(image, rawJpegBuffer);
// ...
}