需要のないページ

プログラミングや趣味や。

TensorFlow/models/Mask R-CNNのクラス構造と関数の引数

Pull Request #1561によるコードとコメントをまとめたもの。

Apache Licenseなので、問題はない... はず。

 

MaskRCNNBoxPredictor (BoxPredictor)
Mask R-CNN Box Predictor. See Mask R-CNN: He, K., Gkioxari, G., Dollar, P., & Girshick, R. (2017). Mask R-CNN. arXiv preprint arXiv:1703.06870.
This is used for the second stage of the Mask R-CNN detector where proposals cropped from an image are arranged along the batch dimension of the input image_features tensor. Notice that locations are *not* shared across classes, thus for each anchor, a separate prediction is made for each class. In addition to predicting boxes and classes, optionally this class allows predicting masks and/or keypoints inside detection boxes. Currently this box predictor makes per-class predictions; that is, each anchor makes a separate box prediction for each class.

  • __init__
    • self
    • is_training
      Indicates whether the BoxPredictor is in training mode.
    • num_classes
      Number of classes. Note that num_classes *does not* include the background category, so if groundtruth labels take values in {0, 1, .., K-1}, num_classes=K (and not K+1, even though the assigned classification targets can range from {0,... K}).
    • fc_hyperparams
      Slim arg_scope with hyperparameters for fully connected ops.
    • use_dropout
      Option to use dropout or not. Note that a single dropout op is applied here prior to both box and class predictions, which stands in contrast to the ConvolutionalBoxPredictor below.
    • dropout_keep_prob
      Keep probability for dropout. This is only used if use_dropout is True.
    • box_code_size
      Size of encoding for each box.
    • conv_hyperparams=None
      Slim arg_scope with hyperparameters for convolution ops.
    • predict_instance_masks=False
      Whether to predict object masks inside detection boxes.
    • mask_prediction_conv_depth=256
      コメントなし
    • predict_keypoints=False
      Whether to predict keypoints inside detection boxes.
  • num_classes (@property)
    • self
  • _predict
    Computes encoded object locations and corresponding confidences. Flattens image_features and applies fully connected ops (with no non-linearity) to predict box encodings and class predictions. In this setting, anchors are not spatially arranged in any way and are assumed to have been folded into the batch dimension. Thus we output 1 for the anchors dimension.
    • self
    • image_features
      A float tensor of shape [batch_size, height, width, channels] containing features for a batch of images.
    • num_predictions_per_location
      An integer representing the number of box predictions to be made per spatial location in the feature map. Currently, this must be set to 1, or an error will be raised.
    • Return Values
      A dictionary containing the following tensors.
      • box_encodings
        [batch_size, 1, num_classes, code_size] representing the location of the objects.
      • class_predications_with_background
        [batch_size, 1, num_classes + 1] representing the class predictions for the proposals.
      • instance_masks (When predict_mask is True)
        A float tensor of shape [batch_size, 1, num_classes, image_height, image_width]
      • keypoints (When predict_keypoints is True)
        [batch_size, 1, num_keypoints, 2]

SSD-TensorFlowをWindows10で試してみる

前書き

そろそろ物体検出のアルゴリズムを試してみないと、と思い立った。*1

使ってみるのはSSDと呼ばれるもの。ぶっちゃけ内容は理解していない。

 

SlideShareにも参考になりそうなスライドがある。

 

環境

  • Windows10 Home x64
  • Miniconda 4.3.21
  • CUDA 8.0
  • PyCharm 171.4424.42

Pythonのパッケージは下記のとおり。

 

実験用のPython仮想環境を用意

今までと同様、Minicondaを用いてpython3.5環境を用意した。

とりあえず導入したパッケージとバージョンなどは次のとおり。

numpy 1.13.0 py35_0
scipy 0.19.0 np113py35_0
scikit-learn 0.18.1 np113py35_1
matplotlib 2.0.2 np113py35_0
pandas 0.20.2 np113py35_0

ここらへんはすべて、conda install で叩き込める。

 

後になって必要になったパッケージとバージョンは次のとおり。

opencv-python 3.2.0.7 <pip>
pillow 4.1.1 py35_0
jupyter 1.0.0 py35_3

OpenCVはpip*2、それ以外はcondaでインストールできる。

 

これに加えて、TensorFlowも当然導入する。

公式サイトに従うと、導入コマンドは次のとおり(GPU版)*3


pip install --ignore-installed --upgrade https://storage.googleapis.com/tensorflow/windows/gpu/tensorflow_gpu-1.1.0-cp35-cp35m-win_amd64.whl

 

とりあえず動かそうとしてドツボにはまる

見出しから落ちは見えているが、とりあえず失敗談 (=大事な経験) をば。

 

今回動かしてみる実装は以下のとおり。

READMEによると、ダウンロード後にssd_300_vgg.ckpt.zipを解凍すれば、

notebookディレクトリのssd_notebook.ipynbで動作確認できるらしい。

 

PythonもDNNもろくにいじったことがなく、これらが何か調べる必要があった。

ssd_300_vgg.ckpt
TensorFlowで学習済みのパラメータのことらしい。
解凍すると .index と .data-00000-of-00001 が出てきた。

ssd_notebook.ipynb
Jupyter notebookで用いるファイル形式らしい。
Pythonのコードの中でMarkDownコメントを使えて便利。
...結果的にはこいつにはめられた。

READMEに従って.ckptを解凍し、PyCharmでプロジェクトファイルを開く。

[TerminalIPythonApp] WARNING | Subcommand `ipython notebook` is deprecated and will be removed in future versions.
[TerminalIPythonApp] WARNING | You likely want to use `jupyter notebook` in the future

いきなり怒られた。

ipythonから改名したんだよ!と主張しているだけなので、これは無視する。

 

In[6]で怒涛のワーニング & Tracebackが発生した。以下抜粋。

2017-06-09 19:05:48.187022: W c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\framework\op_kernel.cc:1152] Invalid argument: Unsuccessful Tenso[IPKernelApp] ERROR | Exception in message handler:
tensorflow.python.framework.errors_impl.InvalidArgumentError: Unsuccessful TensorSliceReader constructor: Failed to get matching files on ../checkpoints/ssd_300_vgg.ckpt: Not found: FindFirstFile failed for: ../checkpoints : \udc8ew\udc92\u80b3\udc82\ua0bd\udc83p\udc83X\udc82\udcaa\udc8c\udca9\udc82\x82\udca9\udc82\udce8\udc82\u0702\udcb9\udc82\udcf1\udc81B
tensorflow.python.framework.errors_impl.InvalidArgumentError: Unsuccessful TensorSliceReader constructor: Failed to get matching files on ../checkpoints/ssd_300_vgg.ckpt: Not found: FindFirstFile failed for: ../checkpoints : \udc8ew\udc92\u80b3\udc82\ua0bd\udc83p\udc83X\udc82\udcaa\udc8c\udca9\udc82\x82\udca9\udc82\udce8\udc82\u0702\udcb9\udc82\udcf1\udc81B
InvalidArgumentError (see above for traceback): Unsuccessful TensorSliceReader constructor: Failed to get matching files on ../checkpoints/ssd_300_vgg.ckpt: Not found: FindFirstFile failed for: ../checkpoints : \udc8ew\udc92\u80b3\udc82\ua0bd\udc83p\udc83X\udc82\udcaa\udc8c\udca9\udc82\x82\udca9\udc82\udce8\udc82\u0702\udcb9\udc82\udcf1\udc81B
UnicodeEncodeError: 'utf-8' codec can't encode character '\udc8e' in position 174: surrogates not allowed
UnicodeEncodeError: 'utf-8' codec can't encode character '\udc8e' in position 3281: surrogates not allowed

どうやら、一番最初のWarningに全てのエラーが引きずられているようだ。

Jupyter Notebookをブラウザで開いてみると...

さっき解凍した.index と .data-00000-of-00001がアップロードされていない!

f:id:LouiS:20170610114559p:plain

 

ここでの解決篇: .ipynbを.pyに変換する

もっとスマートな方法がある気もするが、ここでは.ipynbに別れを告げる。

.ipynbのmd形式*4で書かれている部分を消してやれば.pyと同じだ。

ただ、%matplotlib inline を消してやる必要がある。

 

実は相対パスをすべて絶対パスに置き換えればなんとでもなるっちゃなる。

しかし、新たな非常に厄介な問題に遭遇したのでここでは避ける。

 

実行結果(の代わり)

実行結果を撮って置くのを忘れ、しかしまたリポジトリを落とす気にもなれず。

ネットから持ってきた画像を貼る。(当たり前だが)同じ結果だった。

f:id:LouiS:20170625004743p:plain

 

もしかして

上記のサイトによると、もっとスマートに.ipynb.pyに変換できるようだ。


    > jupyter nbconvert --to python ssd_notebook.ipynb
    

ももうjupyterなんてシラネ。

*1:この記事は二週間前から下書きに埋もれていたので、時系列的に内容は古い

*2:野良ビルドを用いることは可能

*3:必要に応じて、CUDAなどの環境を整える必要がある。

*4:Mark Down形式

TensorFlowの学習済みモデルをいじってみる(1)

能書き

TensorFlowの学習済みモデルで遊ぼうと思ったら、いろいろとハマった。

同じようにハマることもあるだろう、記録は残しておいた方がよい。

 

環境

  • Windows 10 64bit
  • PyCharm 2017.1.3
  • Miniconda 3.6
  • Python 3.5
  • Tensorflow-gpu 1.1.0 <pip>

 

チュートリアルの実行

object_detectionのチュートリアルを試してみる。

 

私は実行ファイルを移動させてしまったので、sys.pathをいじる必要があった。

元のコードを次のように書き換える。


    # sys.path.append("..")
    LIBRARY_PATH = '[ダウンロード先のパス]/models'
    sys.path.append(LIBRARY_PATH)
    sys.path.append(LIBRARY_PATH + '/object_detection')

さらに、コードに散らばる相対パス絶対パスに置き換えていく。

これを怠ると、 次のようなメッセージを含んだエラーが吐かれる。

tensorflow.python.framework.errors_impl.NotFoundError: NewRandomAccessFile failed to Create/Open: data\mscoco_label_map.pbtxt : \udc8ew\udc92?\udc82?\udc83p\udc83X\udc82\udcaa\udc8c\udca9\udc82?\udca9\udc82\udce8\udc82?\udcb9\udc82\udcf1\udc81B
FileNotFoundError: [Errno 2] No such file or directory: 'test_images\\image1.jpg'

二回出たエラーを繋げて書いてみた。両方同時に出るわけではない。

 

パス問題を解消したが... 今度は次のエラーに泣かされた。

Traceback (most recent call last):
File "[LIBRARY_PATH]/models/ObjectDetection/objTest.py", line 18, in <module>
from utils import label_map_util
File "[LIBRARY_PATH]/models/object_detection\utils\label_map_util.py", line 22, in <module>
from object_detection.protos import string_int_label_map_pb2
ImportError: cannot import name 'string_int_label_map_pb2'

ディレクトリを見ると、.protoファイルはあっても.pyファイルがない。

 

.protoファイルから、.pyファイルを生成しなければいけないようだ。

 

このページの最新の圧縮ファイルのうち、win32.zipとついているものを落とす。

win64版が見当たらずにしばらく探したが、結局win32で妥協した。

Windows以外のOSを用いている場合、自力でビルドする必要があるらしい。

 

解凍して得られるprotoc.exeをmodelsに移し、次のコマンドをたたく。


    > protoc object_detection/protos/*.proto --python_out=.
    

そうすると、確かに.pyファイルがディレクトリに追加されていた。

 

実行結果

f:id:LouiS:20170624175046j:plain

もう一つ、海辺の画像が表示されるが、そのページは有名なので端折る。

OpenCVのクラスの前方宣言をまとめてみる

次のサイトがものすごい参考になる。

 

  cv::Mat


    namespace cv {
        class Mat;
    }

  cv::Point


    namespace cv {
        template< typename > class Point_;
        typedef Point_< int > Point;
    }

  cv::Range


    namespace cv {
        class Range;
    }

  cv::Rect


    namespace cv {
        template< typename > class Rect_;
        typedef Rect_< int > Rect;
    }

  cv::Scalar


    namespace cv {
        template< typename > class Scalar_;
        typedef Scalar_< double > Scalar;
    }

  cv::Size


    namespace cv {
        template< typename > class Size_;
        typedef Size_< int > Size;
    }

  cv::String


    namespace cv {
        class String;
    }

 

間違いのご指摘、(答えるとは限りませんが)リクエストある場合はご連絡下さい。

本稿はOpenCV3.1のソースを基に書いています。

Condaを使ってTensorFlowの野良ビルドを導入する

前書き

前回の記事の続きっちゃ続き。環境は、Windows10の64bit機だ。

ただし、この記事ではCPU版のインストールだけ実践する。

 

TensorFlowは、深層学習によく用いられるフレームワークである。

公式サイトは、次のように説明している。

TensorFlow™ is an open source software library for numerical computation using data flow graphs.

使いづらいと揶揄されるが、あくまで本質は計算ライブラリのようだ。

 

TensorFlowのインストール (公式による)

公式サイトが簡潔なインフォメーションを出している。

適当にまとめてみる。(稚拙な和訳/強引な意訳)

  • CPUのみをサポートするものと、GPUも利用するものとの二種類がある
  • GPUが利用できる環境は次の通り
    • CUDA Toolkit 8.0が入っていて、パスが通っている
    • 上記CUDAに対応するNVIDIAドライバが搭載されている
    • cuDNN v5.1が入っていて、パスが通っている
    • GPUのCompute Capabilityが3.0以上である
  • GPUが利用できる場合も、まずはCPU版を試してみることを勧める

 

  • インストールの際は、次の二通りの方法がある
    • "native" pipを利用するもの (推奨)
      • pip3 install --upgrade tensorflow
      • pip3 install --upgrade tensorflow-gpu
    • Anacondaを利用するもの (公式のサポート外)
      • pip install --ignore-installed --upgrade https://storage.googleapis.com/tensorflow/windows/cpu/tensorflow-1.1.0-cp35-cp35m-win_amd64.whl
      • pip install --ignore-installed --upgrade https://storage.googleapis.com/tensorflow/windows/gpu/tensorflow_gpu-1.1.0-cp35-cp35m-win_amd64.whl

 

このインフォは親切だが、condaを用いたインストールは出来ないのだろうか?

 

TensorFlowのインストール (condaを用いる)

予め確認しておくが、この方法は公式に保証されたものではない。

こだわりなくインストールしたいのなら、native pipを用いるのが確実だ。

 

野良ビルドリポジトリは、Anaconda Cloudで検索できる*1

適合する条件をちょいちょい入力すると、次の結果が表示された。

f:id:LouiS:20170606164033p:plain

投稿現在(2017/06)、4件のリポジトリが表示される。リンクはこちら

 

この中で最もダウンロード数の多い、一番上の候補を選んだ。

緑色のリンクをクリックすると、インストールコマンドが表示される。

conda install -c conda-forge tensorflow=1.1.0

こいつをAnacondaプロンプトに叩き込んでやればよい。

 

以下、実際のプロンプト画面。

前もって、Python3.5の仮想環境をactivateしていることに注意されたい。

f:id:LouiS:20170606165401p:plain

 

PyCharmで試してみる

公式インフォQiitaの投稿を参考に、次のテストコードを実行してみた。

 

ほぼほぼパクリだが、先頭二行のコードで警告レベルを変更した。

当初この二行を除いて実行した結果、怒涛のWarningに襲われたからである。

C:\Users\[UserName]\AppData\Local\conda\conda\envs\python35\python.exe C:/Users/[UserName]/temp/py35test/test.py
2017-06-06 17:18:42.845745: W c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\platform\cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE instructions, but these are available on your machine and could speed up CPU computations.
2017-06-06 17:18:42.863110: W c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\platform\cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE2 instructions, but these are available on your machine and could speed up CPU computations.
2017-06-06 17:18:42.863465: W c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\platform\cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE3 instructions, but these are available on your machine and could speed up CPU computations.
2017-06-06 17:18:42.863802: W c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\platform\cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE4.1 instructions, but these are available on your machine and could speed up CPU computations.
2017-06-06 17:18:42.864145: W c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\platform\cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE4.2 instructions, but these are available on your machine and could speed up CPU computations.
2017-06-06 17:18:42.864488: W c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\platform\cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use AVX instructions, but these are available on your machine and could speed up CPU computations.
2017-06-06 17:18:42.864843: W c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\platform\cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use AVX2 instructions, but these are available on your machine and could speed up CPU computations.
2017-06-06 17:18:42.865197: W c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\platform\cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use FMA instructions, but these are available on your machine and could speed up CPU computations.
b'Hello, TensorFlow!'
32
Process finished with exit code 0

適切に実行されているようであるが、警告がなんにせようざったい。

 

適宜検索検討して、これらの警告をにぎりつぶすことに決めた。

  1. これらの警告は、CPUの拡張命令をフルに活かせていないことを指摘するもので、自前でビルドしなおすことで解消される。

  2.  ビルドしなおすと確かに速くなるが、その効果は限定的で、それだったらGPUを使える環境を整える方が圧倒的に理にかなっている。

  3. 頑張ってCPUの実行速度を最適化しても、GPUの実行速度には影響しない。(GPUの方が圧倒的に貢献度が高い状況だと、CPUに合わせてチューニングしても意味が薄い)

 

警告レベルを変えれば、(当たり前だが)表面上はすっきり実行される。

C:\Users\[UserName]\AppData\Local\conda\conda\envs\python35\python.exe C:/Users/[UserName]/temp/py35test/test.py
b'Hello, TensorFlow!'
32

Process finished with exit code 0

 

せっかくなので

せっかくなのでKerasも叩き込んだ。

conda install -c conda-forge keras=2.0.2

f:id:LouiS:20170607162451p:plain

まだ試していないが。

 

後書き

上記の例では、TensowFlowの機能の1%も使えていないだろう。

GitHubからTensorFlowを活用したリポジトリを落として、解析しなければ。

なお、ボトムアップ的(?)な学習には、以下の公式ページが参考になりそうだ。

あと、GPU試す。これ大事。

*1:サインインしなくても普通に使える

Python食わず嫌いな人間が環境を作ってみた [Miniconda + PyCharm]

前書き

C++畑で育った人間として、Pythonはとても気持ち悪く思える。

動的型付けなんて黒魔術みたいだ/バイナリファイルを作りたい/etc.

しかし、『使えない』のと『使わない』のとは全く違う。

最低限は試してみようと、環境を構築してみることにした。

なお、使用OSはWindows10の64bit機。

 

導入の方針

次のサイトが非常に参考になる。

qiita.com

他にもいろいろなサイトを巡ってみて、次のような方針を決めた。

  • ディストリビューションとしてMinicondaを使用
  • 基本的に仮想環境上でPythonの実行を行う
    • 諸般の事情により、2.7と3.5、3.6を同居させたい
    • 環境それぞれに必要なパッケージを導入するつもり
  • pipやwheelにはできる限り頼らない
  • IDEとしてはJetBrains社のPyCharmを用いる

こんな感じ。さっそく始める。

 

Minicondaのインストー

公式サイトからMinicondaインストーラを入手できる。

最新バージョンを落とせば古いPythonの環境も作れる。

特に理由がなければ、一番新しいバージョンをダウンロードするのがよい。

インストールも、嫌らしいデフォルト設定などないのでポチポチでOK。

 

インストールすると、スタートメニューにプロンプトが追加されている筈だ。

f:id:LouiS:20170604192206p:plain

素晴らしいMiniconda。非常にシンプルな構成である。

 

プロンプトを開き、現在インストールされているパッケージを見てみる。

conda list -n root

f:id:LouiS:20170604203237p:plain

-n rootは不要だが、ルート設定であるということを明示するために入れてみた。

この結果を見ても、NumPyなどがプリインストールされていないことがわかる。

 

仮想環境の構築

さて、仮想環境の構築とカスタマイズをしてみよう。

仮想環境を作るためには、プロンプトに次のように打ち込めばよい。

conda create -n [環境名] python=[バージョン]

 

実際の画面は次のようである。

f:id:LouiS:20170604205542p:plain

『次のパッケージを入れるけど、いい?』と聞いてくれたり、

具体的な使い方を指南してくれるあたり、非常に親切である。(若干感動した)

ただ、案外時間がかかる印象はあった。

 

Pythonインタプリタ本体は、赤線部のディレクトリに入っている。

後でPyCharmのセッティングをするときに使うので、確認しておこう。

 

パッケージのインストー

仮想環境に必要なパッケージをインストールしていこう。

ここでは、プログラマに(おそらく)必須であろう、NumPySciPyを入れてみる。

 

他のページを見てみると、-nオプションで環境指定してインストー*1している。

しかしここでは、環境をactivateしてからインストールする方法を取りたい。

なぜか?―私がしばしばオプションを付け忘れるからである!(仮想環境作った意味)

 

具体的には、次のように命令を打ち込む。

activate [環境名]

conda install numpy scipy

パッケージは同時に複数インストールでき、バージョンも指定できる。

実際の画面は次のようである。

f:id:LouiS:20170604215052p:plain

プロンプトの左側に、activateな環境が書かれている。つくづく親切である。

 

Anacondaプロンプトを使った操作は、基本の基本は以上でおしまい。

ググれば他にもたくさん命令が出てくるので、見てみるとよいだろう。

なお、公式のチートシートが公開されている。

 

PyCharmを使ってみる

Intellij IDEAを使ってからJetBrainsに惚れているので、PyCharmを採用した。

インストーラ公式サイトからダウンロードできる。

 

インストールにあたり、いじったオプションは一か所だけ。

f:id:LouiS:20170604231403p:plain

今までVSCodeで見ていたpyファイルを、PyCharmに結び付けることにした。

 

インストール後、とりあえず起動し、Look and Feelを好みに合わせる。

ここら辺はこだわりがないのならデフォルトで一切問題がない。

 

プロジェクトをつくる際、インタプリタを指定する必要がある。

f:id:LouiS:20170604231938p:plain

Interpreter右端にある歯車のボタン→Add Localで、環境を指定する。

私の環境の場合、次を指定した。

C:\Users\[ユーザ名]\AppData\Local\conda\conda\envs\python35\python.exe

 

目当てのインタプリタが見つからない場合、次のようにすればわかる。

f:id:LouiS:20170604234739p:plain

さりげなくAnacondaプロンプト再登場である。

なお、おそらくそのディレクトリにはpythonw.exeも存在する。

直下のページが検討しているとおり、あまり気にしなくてよいようだ。

blog.shibayan.jp

 

プロジェクトを作れたら、次のコードを打ち込んでみる。

f:id:LouiS:20170604235907p:plain

実行すると、リストが出力されるはずだ。

 

なお、File→Settings→Project: [name]→Project Interpreterを見ると、

いま使われているインタプリタと、導入できるパッケージが見られる。

f:id:LouiS:20170605002657p:plain

また、同じ画面からインタプリタの変更もできる。

試しにPython2系に変更してみると、エラーが出てくるだろう。

 

Anacondaだけで対応できないとき

Anacondaは便利だが、入手できないパッケージもしばしばあるらしい。

以下のページに、その解決法が書いてあった。

ど素人なので今見ても全然わからないが、そのうちお世話になりそう。

 

後書き

冒頭で述べたようにPythonの気味の悪さのようなものを感じている。

また、以前にpipでハマったこともあり、苦手意識はかなりあった。

しかし、Anacondaを使いこなせれば、ストレスも減るのかと思う。

 

実は、PythonはTensorFlowを利用するために導入してみた。

*1:conda install -n [環境名] [パッケージ名]

定数M_PIが使えない

能書き

かなり間があいたので、小ネタ。

毎度ながら、知ってる人にとっては当たり前の内容を有難そうに書く。

 

遭遇したエラー

例えば、次のようなコードを書いたとする。

まあ、半径から円の面積を計算するだけの、よく見るコードだ。

 

ただし、円周率を自前で定義しているあたり、このコードはモダンでない。

_USE_MATH_DEFINESフラグを立ててcmathをインクルードしたほうがよい。

上記のコードのdefine文を単に置き換えると次のようになる。

しかし、このコードをコンパイルすると... 次のようなエラーが出る。

error C2065: 'M_PI': 定義されていない識別子です。
error C2065: 'M_PI': undeclared identifier

 

原因と対策

実はこれ、ヘッダファイルを読み込む順序が問題なのである。

エラーが出たコードでは、フラグを立てる前にiostreamを読み込んでいた。

 

iostreamヘッダのインクルードを追ってみると:

  • iostreamは、istreamをインクルードしている。
  • istreamは、ostreamをインクルードしている。
  • ostreamは、iosをインクルードしている。
  • iosは、xlocnumをインクルードしている。
  • xlocnumは、cmathをインクルードしている。

巡り巡って、iostreamをインクルードすると、cmathが読み込まれる。

もちろんその時点では_USE_MATH_DEFINESフラグは立っていない。

 

解決策は、次のとおり。

マクロの順序を変更しただけ。

ヘッダファイルに関するフラグは、include文より前に定義しよう。

 

雑記:前もって定義されている数学定数

#define _USE_MATH_DEFINES

フラグを立ててcmathをインクルードすると、次の数学定数を利用できる。

f:id:LouiS:20170506160658j:plain

ネイピア数含め対数周りの定数と、円周率に関する定数。

一番下の定数を、M_1_SQRT2と改名すべきと思うのは私だけだろうか?

 

雑記:ぼやき

iostreamに、cstdlibやcmathがインクルードされてるっていったい何なんだ。

いわゆる神モジュールになっている気が...

 

 

Qtでソケット通信を試してみた(UDP)

能書き

トランスポート層プロトコルは主に二種類ある。

  • 確実だけどめんどくさいTCP
  • 不確実だけど楽チンなUDP

今回はQtのQUdpSocketを用いたUDPのソケット通信を試してみた。

ネットワーク通信については詳述できないしないが、次のページが参考になる。

f:id:LouiS:20170324171137g:plain

 

また、QtのGUIの使い方は、以下のページで紹介したとおり。環境も同様。


ただし、モジュールの『Network』を有効にすること。

f:id:LouiS:20170324175701p:plain

 

サンプルコードの挙動

f:id:LouiS:20170324203640p:plain

Senderのエディットに文字列を入れてボタンを押すと、Recieverに反映される。

ただし、それぞれ別のアプリケーションとして起動する。それだけ。

 

このコードを実行するとき、このような警告が出ることがある。

両方のチェックボックスにチェックを入れて、アクセスを許可すること。

f:id:LouiS:20170324213759p:plain

 

サンプルコード―送信

送信用のウィジェットとして、次のようなものを用意する。

f:id:LouiS:20170324182843p:plain

clicked()シグナルと、ダイアログのsendDatagram()スロットを連結する。

 

まずヘッダファイル。

先のスロットと並んで、ソケットとポート番号を宣言している。

ポート番号は、ウェルノウン*1でなければ、適当に選んで大丈夫(だと思う)。 

ポート番号に関する解説は、このページが詳しい。

 

次に、ソースファイル。

23行目のwriteDatagram()第二引数には、通信先のIPアドレスを指定する。

  • QHostAddress::LocalHost
     自分自身と通信する、ループバックアドレス。実験段階で特に有用だろう。
  • QHostAddress::Broadcast
     同一ネットワーク上の全てのノードに送信するアドレス。ただし、ブロックするルータもある。
  • QHostAddress::QHostAddress( "xxx.xxx.xxx.xxx" )
     コンストラクタを用いて、IPv4アドレスを指定する方法。

 

18-21行目は、送信用のバイト列をうまく扱うための方便。

とりあえずは盲目に真似して、C++のストリームの恩恵に預かってよいだろう。

 

このように、送信側はただ『データを成形して』『送信する』だけで実装できる。

 

サンプルコード―受信側

受信側のウィジェットは単純で、ラベルが一つだけあるダイアログだ。

コードは次のとおり。

ヘッダファイルは送信側と酷似している。

ソースファイルに関しても、結局は『受信して』『データを解析して』いるだけ。

21行目から24行目は、このままでいろいろなニーズに使いまわせる。

 

受信側特有の処理は二つある。第一は、cppファイル9行目のbind()だ。

どのデータを受け取るか選ぶために、前もってポート番号を知らせる必要がある。

 

第二の特有の処理は、readyRead()シグナルと処理スロットを接続することだ。

サンプルコード10,11行目の処理にあたる。

readyRead()シグナルは、送信されたデータを読む準備が出来ると発呼される。 

 

まとめ

  • QUdpSocketクラスは、低信頼/高速の通信プロトコルを提供する。
  • 送信側/受信側が、共通のポート番号を知っておく必要がある。
  • 送受信にはQByteArrayクラスのバイト列を用いる。
    QDataStreamクラスを利用すると便利。
  • 受信側は、前もってbind処理を行っておくこと。
  • データが受信されると、readyRead()シグナルが発呼される。

案外単純である。

 

ストリームの取扱いに関しても、そのうち書く...かもしれない。

書かないかもしれない。 

 

ただし

ただし、このコードでは、短時間に連続して送られるデータには対応できない。

処理に手間取っているうちに送信されたデータは無視される。

全てのデータを扱う必要があるのなら、

hasPendingDatagrams()を用いたループ内に処理命令を書くこと。

リファレンスのサンプルコード片が参考になる。

 

これについてはそのうち書く。

*1:0~1023 予約されているポート

マルチスレッドQtアプリケーション(まとめ)

能書き

Qtでマルチスレッドを実現する方法にはいくつかある。

それらの方法や参考となるページをまとめる。

 

『マルチスレッドとは何か?』

これについては、検索すればいくらでも情報がヒットするので、特筆しない。

個人的には、『処理の流れが複数ある状態』と説明している。

 

継承を用いて実現する方法

以下のページに詳細を示した。

この方法は、おそらく最も素直な実装スタイルだと思うし、

私が参照したオライリーの入門書でもこれが紹介されていた。

 

しかし、いくつかの問題点を含むため、次の方法が現在では主流のようだ。

 

委譲を用いて実装する方法

以下のページに詳細を示した。

 

参考となるページ

QThread Class | Qt Core 5.8 | DetailsQObject Class | Qt Core 5.8 | moveToThread()

まずは公式リファレンス×2。

それぞれのページで、Qtのスレッドの扱いについて知見を深められる。

また、継承による方法、委譲による方法の両方のコードが紹介されている。

Qtでスレッドを使う前に知っておこう - Qiita

この中で唯一日本語のページ。

なぜ継承による実装が避けられるのか、その問題点を列挙している。

また、connect関数の第5引数に関しても詳説があり、非常に参考になる。

How To Really, Truly Use QThreads; The Full Explanation | Maya's Programming & Electronics Blog

委譲による方法を提言し、Qt公式リファレンスに強い影響を与えたエントリー。

従来の方法を批判する要点は次のとおり(稚拙な和訳/強引な意訳)。

  • 継承による従来の方法は、必要以上に複雑で不適当。
  • QThreadは、『スレッド』そのものではない。スレッド周辺を覆うラッパークラスだ。したがって、サブクラスがスレッドのように扱われるのは不適当。

具体的に実装する際の主な注意点は次のとおり(稚拙な和訳/強引な意訳)。

  • 別スレッドで動かすQObjectのコンストラクタでヒープ領域を確保しないこと。QObjectをインスタンス化したときは、まだ旧スレッド上にある。
  • deleteLater()スロットを適切に用いれば、クラッシュを回避できる。

マルチスレッドQtアプリケーション(2)

能書き

前回の記事の続きとして見てもよいだろうし、そうでなくてもよいだろう。

 

マルチスレッドをQtで再現する方法はいくつかあるが、

その中で、QThreadにタスクを委譲するものを紹介する。

 

簡単な例

まず、特に簡単なコードを示す。

異なるスレッド間で直接関数を呼び出すことは危険だが、ここでは無視する。

その他もいろいろいい加減なコードであるが、許してもらいたい。

 

実行結果は次のとおり。

Main: QThread(0x24230a65230)
Func: QThread(0x24230a68820)

マルチスレッドが実現されていることがわかる。

 

必要な手順

委譲を用いてマルチスレッドを実現するために必要な手順は以下のとおり。

  1. 別スレッドで実行したい処理を担うクラスを、QObjectを継承して作る。
  2. そのインスタンス(subObject)をヒープ領域に生成する。
     SubObject *subObject = new SubObject();
  3. QThreadのインスタンス(subThread)をヒープ領域に生成する。
     QThread *subThread = new QThread();
  4. subObjectの所有権をsubThreadに移す。
     subObject->moveToThread( subThread );
  5. subThreadの処理を開始する。
     subThread->start();

上記の処理は、source.cppの17-19行目に実装されている。

 

4番目の手順、5番目の手順を抜かしても、ビルドエラーは生じない。

予期通りの動作をしないときは、このようなミスをしている恐れがある。

 

異なるスレッド間で直接関数を呼び出してはいけない理由

例えば、先のコードを一部改造して、次のようなコードを書いたとする。

subThreadのstart()をコメントアウトし、シグナル/スロットを追加している。

 

実行結果は次のとおりである。

Main: QThread(0x1b9868d5300)
Func: QThread(0x1b9868d89b0)
emit complete

結果を見てわかるように、スロットが呼び出されていない!

 

これはなぜか。

実はスロットは、シグナルが発呼された直後に呼び出されるのではない。

スロットが所属するスレッドのキューに組み込まれるだけだ。

スレッドが開始されなければ、もちろんキューは処理されない。

それために、先の実行結果が出たのである。

 

直接関数を呼び出してしまっては、スレッドが開始していようがいまいが、

関係なしに即時実行してしまうこととなる。

/* コードブロック */