usagi / virtual-keyboard-prototype-1

仮想キーボード試作1型
3 stars 0 forks source link

重い処理をPC側で行うモードに対応する #65

Closed usagi closed 10 years ago

usagi commented 10 years ago

58 から #61 や #64 の高速化を施しては居るが、Raspberry Piにおけるmainモードの実行速度が 14.752[sec/frame] = 0.067 [FPS] では100倍高速化できたとしても 6.7 [FPS]に留まり、現実的にmainモードを現状でRaspberry Piに実装する事は無謀と思える。

そこで、現在のmain/recieverのモードに加え、重い処理をmain側ではなくreciever側で行う新たな動作モードを追加する。

usagi commented 10 years ago

modeを次のように追加します。

usagi commented 10 years ago

モード名、 main- と reciever+ にしましょう。内部的なシンボルとしては main_m1 と reciever_p1 にしましょう。

usagi commented 10 years ago

まて、UDP/IPでそのままじゃ1枚900KBもの画像2枚をそのまま送受信するなんて無理。

usagi commented 10 years ago

UDP/IPv4のパケット限界値は64KBなので、通信状況が理論値並に良好と仮定しても900KBの送信は15パケット必要。2枚分では29パケット必要。

送受信専用の型を用意してデータにシーケンシャルにカウントアップする番号を付与して飛んだら通信エラーによるフレームドロップとするか、再送要求をして補完する必要がある。再送要求まで実装するくらいならTCP/IPv4にした方が楽。

或いはUDP/IPv6にすればジャンボグラムとやら最大4GBまでのパケットを扱えるらしいが、調査しないとわかりません。

usagi commented 10 years ago

http://takuponv6.cocolog-nifty.com/blog/2008/10/ipv6-5rfc2471-1.html

今回は特殊化されたネットワークを使うわけではないのでUDP/IPv6のジャンボグラムは忘れましょう。一般的なネットワークは巨大なMTUなんてサポートしてない。

usagi commented 10 years ago

レイテンシーの都合からTCP/IPは使いたくない。

それと、無圧縮のBGRデータストリームとか、2枚転送するだけで14.0625Mbit必要。30FPSならば当然30倍の帯域が必要となるので、421.875Mbps必要。これはギガビットイーサのでなければ確保できない帯域。ギガビットイーサの一般的な実効帯域は650Mbps程度です。

画像の圧縮コストが遅延になるとはいえ、画像を圧縮しなければ一般的なネットワークでこの帯域確保は無理でしょう。

usagi commented 10 years ago

top.1.mp4の圧縮結果

-rw-r--r-- 1 usagi usagi 901K 2014-01-29T11:40:51 test.bmp
-rw-r--r-- 1 usagi usagi 471K 2014-01-29T11:39:33 test.png
-rw-r--r-- 1 usagi usagi 8.2K 2014-01-29T11:53:04 test.0.jpg
-rw-r--r-- 1 usagi usagi  17K 2014-01-29T11:52:44 test.10.jpg
-rw-r--r-- 1 usagi usagi  27K 2014-01-29T11:52:30 test.20.jpg
-rw-r--r-- 1 usagi usagi  35K 2014-01-29T11:52:04 test.30.jpg
-rw-r--r-- 1 usagi usagi  42K 2014-01-29T11:51:49 test.40.jpg
-rw-r--r-- 1 usagi usagi  48K 2014-01-29T11:51:33 test.50.jpg
-rw-r--r-- 1 usagi usagi  56K 2014-01-29T11:49:05 test.60.jpg
-rw-r--r-- 1 usagi usagi  67K 2014-01-29T11:48:48 test.70.jpg
-rw-r--r-- 1 usagi usagi  85K 2014-01-29T11:48:27 test.80.jpg
-rw-r--r-- 1 usagi usagi 124K 2014-01-29T11:40:23 test.90.jpg
-rw-r--r-- 1 usagi usagi 401K 2014-01-29T11:39:58 test.100.jpg
usagi commented 10 years ago
[KB/frame] [KB/2-frames] [packets] [KB/sec] [MB/sec] [Mbps] Ether-class
test.bmp 901 1,802 28.16 54,060 52.79 422.34375 1000Base
test.png 471 942 14.72 28,260 27.60 220.78125 1000Base
test.100.jpg 401 802 12.53 24,060 23.50 187.96875 1000Base
test.90.jpg 124 248 3.88 7,440 7.27 58.125 100base
test.80.jpg 85 170 2.66 5,100 4.98 39.84375 100base
test.70.jpg 67 134 2.09 4,020 3.93 31.40625 100base
test.60.jpg 56 112 1.75 3,360 3.28 26.25 100base
test.50.jpg 48 96 1.50 2,880 2.81 22.5 100base
test.40.jpg 42 84 1.31 2,520 2.46 19.6875 100base
test.30.jpg 35 70 1.09 2,100 2.05 16.40625 100base
test.20.jpg 27 54 0.84 1,620 1.58 12.65625 100base
test.10.jpg 17 34 0.53 1,020 1.00 7.96875 100base
test.0.jpg 8 16 0.26 492 0.48 3.84375 10Base
usagi commented 10 years ago
usagi commented 10 years ago

暫定仕様:

sequence_idは30FPSで進行するとして256個を消費するには8.533秒を要する。受信側でこの時間より古いデータは破棄するようにすれば事実上問題は起こらないと考えられる。

usagi commented 10 years ago
struct frame_packet_t
{
  uint16_t data_size;
  uint8_t capture_id;
  uint8_t sequence_id;
  uint8_t data[65535-(16+8+8)/8];
};

こんなの用意しといて送信時も受診時も使ったらいい。

usagi commented 10 years ago

OpenCV内蔵のJPEGエンコーダー、デコーダーが遅すぎなければこれを使いましょう。新たにImageMagickを依存ライブラリーに追加する状況でもありませんし。

usagi commented 10 years ago

これ以上は時間無い。仕事終わったら再開する。たぶん実装する納期が今日中なら間に合うが問題はそれを @arisin が使う事、またテスト次第ではおそらくUDP/IPのパケットロスが問題になりパケットサイズの制限が必要になる。何れ続きはあとだ。

arisin commented 10 years ago

すみません。よろしくお願いします。

usagi commented 10 years ago

http://melpon.org/wandbox/permlink/3CrKngJwJo8iTWdb

現実としてloclahost的にはUDP/IPv4で32753*sizeof(uint16_t)=65506bytesのパケットを扱えるようだから、パケットの最大サイズは65506byte。

これにcapture_idは必須だから1byte減って、sequence_idも欲しいから1byte減って、data_sizeで2bytes減って、合計で4bytes減るから、65502bytesまでの画像データ本体部分まで送信可能、という事になる。

usagi commented 10 years ago

network-common.hxx

#pragma once

#include <vector>
#include <array>

#include <opencv2/core/core.hpp>

namespace arisin
{
  namespace etupirka
  {
    struct frame_packet_t final
    {
      using sequence_id_t = uint8_t;

      uint16_t real_data_size;
      uint8_t capture_id;
      sequence_id_t sequence_id;

      static constexpr size_t info_size = sizeof(real_data_size) - sizeof(capture_id) - sizeof(sequence_id);
      static constexpr size_t data_size = 65506 - info_size;
      static constexpr size_t this_size = info_size + data_size;

      uint8_t data[data_size];

      inline void set_data(const cv::Mat& m, const int jpeg_quality = 60)
      {
        std::vector<uint8_t> buffer;

        cv::imencode(".jpg", m, buffer, { CV_IMWRITE_JPEG_QUALITY, jpeg_quality });

        if(buffer.size() > data_size)
          throw std::runtime_error("encoded frame size is over.");

        std::copy(std::begin(buffer), std::end(buffer), &data[0]);
        real_data_size = buffer.size();
      }

      inline uint8_t* data_begin(){ return &data[0]; }
      inline uint8_t* data_end(){ return data_begin() + size_t(data_size); }

      using mutate_array_t = std::array<uint8_t, this_size>;

      inline const mutate_array_t& mutate_to_array() const
      { return *reinterpret_cast<const mutate_array_t*>(this); }
    };
  }
}
usagi commented 10 years ago

動作確認一切して無いけど実装完了しました。

arisin commented 10 years ago

ありがとうございます。

usagi commented 10 years ago

とりあえずClose。バグがあったら別途バグごとにIssueで。