OpenCV4 のC++開発環境を整備する(自分用メモ)

■前提:
・対象:Linux または WSL(Windows Subsystem for Linux).
・言語:C++でOpenCVを使う.(Python からは anaconda 経由で使うなどを想定)

■今回のファイル:
まとめてhttps://github.com/dif-engine/memo/tree/master/opencvに置いてあります.

■OpenCV4のビルドとインストール
注意:以下のスクリプトを実行すると~/opencv がいじられます.適宜修正して使ってください.

cd;
sudo apt update
sudo apt-get install cmake libeigen3-dev libgtk-3-dev qt5-default freeglut3-dev libvtk6-qt-dev libtbb-dev ffmpeg libdc1394-22-dev libavcodec-dev libavformat-dev libswscale-dev libjpeg-dev libpng++-dev libtiff5-dev libopenexr-dev libwebp-dev libhdf5-dev libopenblas-dev liblapacke-dev
mkdir -p opencv && cd opencv
wget https://github.com/opencv/opencv/archive/4.2.0.zip
unzip 4.2.0.zip
mkdir opencv-4.2.0/build && cd opencv-4.2.0/build
cmake -G "Unix Makefiles" --build . -D BUILD_CUDA_STUBS=OFF -D BUILD_DOCS=OFF -D BUILD_EXAMPLES=OFF -D BUILD_JASPER=OFF -D BUILD_JPEG=OFF -D BUILD_OPENEXR=OFF -D BUILD_PACKAGE=ON -D BUILD_PERF_TESTS=OFF -D BUILD_PNG=OFF -D BUILD_SHARED_LIBS=ON -D BUILD_TBB=OFF -D BUILD_TESTS=OFF -D BUILD_TIFF=OFF -D BUILD_WITH_DEBUG_INFO=ON -D BUILD_ZLIB=OFF -D BUILD_WEBP=OFF -D BUILD_opencv_apps=ON -D BUILD_opencv_calib3d=ON -D BUILD_opencv_core=ON -D BUILD_opencv_cudaarithm=OFF -D BUILD_opencv_cudabgsegm=OFF -D BUILD_opencv_cudacodec=OFF -D BUILD_opencv_cudafeatures2d=OFF -D BUILD_opencv_cudafilters=OFF -D BUILD_opencv_cudaimgproc=OFF -D BUILD_opencv_cudalegacy=OFF -D BUILD_opencv_cudaobjdetect=OFF -D BUILD_opencv_cudaoptflow=OFF -D BUILD_opencv_cudastereo=OFF -D BUILD_opencv_cudawarping=OFF -D BUILD_opencv_cudev=OFF -D BUILD_opencv_features2d=ON -D BUILD_opencv_flann=ON -D BUILD_opencv_highgui=ON -D BUILD_opencv_imgcodecs=ON -D BUILD_opencv_imgproc=ON -D BUILD_opencv_java=OFF -D BUILD_opencv_ml=ON -D BUILD_opencv_objdetect=ON -D BUILD_opencv_photo=ON -D BUILD_opencv_python2=OFF -D BUILD_opencv_python3=OFF -D BUILD_opencv_shape=ON -D BUILD_opencv_stitching=ON -D BUILD_opencv_superres=ON -D BUILD_opencv_ts=ON -D BUILD_opencv_video=ON -D BUILD_opencv_videoio=ON -D BUILD_opencv_videostab=ON -D BUILD_opencv_viz=OFF -D BUILD_opencv_world=OFF -D CMAKE_BUILD_TYPE=RELEASE -D WITH_1394=ON -D WITH_CUBLAS=OFF -D WITH_CUDA=OFF -D WITH_CUFFT=OFF -D WITH_EIGEN=ON -D WITH_FFMPEG=ON -D WITH_GDAL=OFF -D ITH_GPHOTO2=OFF -D WITH_GIGEAPI=ON -D WITH_GSTREAMER=OFF -D WITH_GTK=ON -D WITH_INTELPERC=OFF -D WITH_IPP=ON -D WITH_IPP_A=OFF -D WITH_JASPER=OFF -D WITH_JPEG=ON -D WITH_LIBV4L=ON -D WITH_OPENCL=ON -D WITH_OPENCLAMDBLAS=OFF -D WITH_OPENCLAMDFFT=OFF -D WITH_OPENCL_SVM=OFF -D WITH_OPENEXR=ON -D WITH_OPENGL=ON -D WITH_OPENMP=OFF -D WITH_OPENNI=OFF -D WITH_PNG=ON -D WITH_PTHREADS_PF=OFF -D WITH_PVAPI=OFF -D WITH_QT=OFF -D WITH_TBB=ON -D WITH_TIFF=ON -D WITH_UNICAP=OFF -D WITH_V4L=OFF -D WITH_VTK=OFF -D WITH_WEBP=ON -D WITH_XIMEA=OFF -D WITH_XINE=OFF -D WITH_LAPACKE=OFF -D WITH_MATLAB=OFF ..
make -j8
sudo make install

■opencv のライブラリパスを通す
vim ~/.bashrc して次のような行を加える:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib

:wqして保存終了したら source ~/.bashrc しておく.

■C++サンプル
以下のサンプルは以前の記事のサンプルをOpenCV4に合わせてフィックスしたものです.

#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>

using namespace cv;
using namespace std;

#define image_path_string "madoka.png"
#define W 500
#define H 350

/*
CV_AA ==> LINE_AA
cvNamedWindow ==> namedWindow
cvWaitKey ==> waitKey
format ==> Formatter::format ; コードを参照
*/



#define IMAGESHOW(X) do{  \
  namedWindow(#X);      \
  imshow(#X, X);          \
  }while(0)

int main()
{
  //画像の読み込み
  Mat img1 = imread( image_path_string, IMREAD_COLOR);


  //画像をグレースケールで読み込む
  Mat img2 = imread( image_path_string, IMREAD_GRAYSCALE);


  //カラー画像の作成
  Mat img3 = Mat::zeros(Size(W, H), CV_8UC3);


  //応用:img2と同じサイズでグレー画像を作成
  Mat img4 = Mat::zeros(Size(img2.cols, img2.rows), CV_8UC1);


  //グレースケール画像をカラー画像に変換する
  Mat img2_color;
  cvtColor(img2, img2_color, COLOR_GRAY2BGR);


  //比較のために両方に「赤い対角線」を入れてみる
  const int W2 = img2.cols;
  const int H2 = img2.rows;
  line(img2, Point(0,0), Point(W2,H2)
    , Scalar(0,0,255), 3, LINE_AA);
  line(img2_color, Point(0,0), Point(W2,H2)
    , Scalar(0,0,255), 3, LINE_AA);
  //Scalar(b,g,r,alpha=0)


  //コピーコンストラクタ
  Mat img5(img2);
  //これは「浅い」コピーになる。
  //したがって、img5 に対する操作は img2 に反映される。
  line(img5, Point(0,H2/2), Point(W2,H2/2)
    , Scalar(0,255,0), 3, LINE_AA);


  Mat img6 = img2.clone();
  //これは「深い」コピーになる。
  //したがって、img6 に対する操作は img2 には全く反映されない。


  //画素に対する直接操作(グレースケール画像の場合)
  //市松模様にしてみる
  for(int y = 0; y < img6.rows; ++y)
  {
    for(int x = 0; x < img6.cols; ++x)
    {
      img6.at<unsigned char>(y,x)
        = 255 * ((x/10+y/10) % 2);
      // (y,x) という順であることに注意する。
    }
  }


  //画素に対する直接操作(カラー画像の場合)
  //一列毎に色が変わる市松模様にしてみる
  Mat img7 = img3.clone();
  for(int y = 0; y < img7.rows; ++y)
  {
    for(int x = 0; x < img7.cols; ++x)
    {
      img7.at<Vec3b>(y,x)[0] = ((y/10)%3 == 0)? 255 * ((x/10+y/10) % 2) : 0;//B
      img7.at<Vec3b>(y,x)[1] = ((y/10)%3 == 1)? 255 * ((x/10+y/10) % 2) : 0;//G
      img7.at<Vec3b>(y,x)[2] = ((y/10)%3 == 2)? 255 * ((x/10+y/10) % 2) : 0;//R
      // (y,x) という順であることに注意する。
    }
  }


  //画像の「原点」は左上である;
  //つまりx軸は左から右へ、y軸は上から下に走っている。
  circle(img6, Point(0,0), 100
    , Scalar(0,250,250), 5, LINE_AA);
  circle(img7, Point(0,0), 100
    , Scalar(0,250,250), 5, LINE_AA);


  //画像の保存:拡張子に応じた形式で保存される。
  imwrite("img7.png", img7);
  imwrite("img7.jpg", img7);
  imwrite("img7.bmp", img7);

  //文字列への書き出し
  //(検証しやすくするため小さめの画像でやる)
  Mat img8 = Mat::zeros(Size(20, 15), CV_8UC1);
  for(int y = 0; y < img8.rows; ++y)
  {
    for(int x = 0; x < img8.cols; ++x)
    {
      img8.at<unsigned char>(y,x) = 255 * ((x+y) % 2);
    }
  }

  //C言語のデータとして使える形式で書き出し
  ostringstream C_out;
  C_out << Formatter::get(Formatter::FMT_C)->format(img8);
  cout << "C:" << C_out.str() << endl;

  //CSV形式で書き出し
  ostringstream CSV_out;
  CSV_out << Formatter::get(Formatter::FMT_CSV)->format(img8);
  cout << "CSV:" << CSV_out.str() << endl;

  //Pythonのデータとして使える形式で書き出し
  ostringstream Python_out;
  Python_out << Formatter::get(Formatter::FMT_PYTHON)->format(img8);
  cout << "Python:" << Python_out.str() << endl;

  IMAGESHOW(img1);
  IMAGESHOW(img2);
  IMAGESHOW(img2_color);
  IMAGESHOW(img3);
  IMAGESHOW(img4);
  IMAGESHOW(img5);
  IMAGESHOW(img6);
  IMAGESHOW(img7);

  cout << "hit any key on some image window" << endl;
  waitKey(0);

  return 0;
}

これに対するMakefileは次の通りです:

help:
	@grep -E '^[0-9a-zA-Z_-]+[[:blank:]]*:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[1;32m%-30s\033[0m %s\n", $$1, $$2}'

.PHONY: help add


OPENCVHEADER = /usr/local/include/opencv4
 
LIB_LOADPATH = -L/usr/local/lib
 
LINK_OPENCV_LIBS = \
-lopencv_core \
-lopencv_imgcodecs \
-lopencv_highgui \
-lopencv_imgproc

 
LDFLAGS = -lm -lstdc++ $(LIB_LOADPATH) $(LINK_OPENCV_LIBS)
 

add : ## add some files
	git add Makefile basic1.cc

sample: sample.cc ## build sample.cc 
	$(CXX) $< -o $@ -I$(OPENCVHEADER) $(LIB_LOADPATH) $(LINK_OPENCV_LIBS)

make sample して ./sampleすればテストできます.
WSLでは画像が表示されないかもしれません.これはX環境の問題なのでこの記事で書いたような方法で整備をしておく必要があります.

Makefile の先頭にある help ターゲットはMakefile自己文書化の仕組みです.以前にこの記事で解説しました.

ubuntu でカーソルが太い十字架になって何もできなくなった

■原因: (ghci を起動したと思い込んで) import hoge.hs してしまった.

■対処法:killall -KILL import

(python, OpenCV)imshow がエラーになる問題の workaround

OpenCVをPython から使おうとしているが imshow という関数がエラーになる.
色々な fix 法が検索でヒットするが自分の環境で試してもうまくいかなかった.
そこで以下のような workaround を使うようにしている.

import cv2
import numpy as np
import matplotlib.pyplot as plt

img = np.zeros((size, size, 3), np.uint8)

#cv2.imshow('image',img)

fig, ax = plt.subplots()
ax.imshow(img)
plt.show()

makeの特定のターゲットで実行されるコマンドを「その場で」つまりMakefileを直接いじらずに修正したい

■tl;dr
$ make hoge -n | vipe | bash
一時バッファでvimが開くのでそれを編集してから :wq すると編集されたコマンドが実行される.

■状況
$ make hoge で実行されるコマンドが
some-long-command1 arg1 arg2 arg3
のようになっており,臨時に arg3 だけ変更したいがMakefileをいじるまでもない.
どうするか?

■素朴な対処法
$ make hoge -n でコマンドの内容を書き出し,コピーして$ 以下に貼り付けてからその場でコマンドを編集して実行する.

■素朴な対処法の不満点
タッチパッドしか使えない場合などにはこのやり方はかなり不便.また,hoge ターゲットが複数のコマンド(複数行)からなっている場合,作業そのものが面倒.

■うまく行かない方法
(1) $ make hoge -n | xclip (2) マウスの中ボタンを押す.
ポイント1:マウスの中ボタンを押すとコマンドが実行されてしまう.
ポイント2:タッチパッドに中ボタンがそもそもないんですけど!

■解決策: vipe を使う.
(1) $ make hoge -n | vipe | bash
一時バッファでvimが開くのでそれを編集する.
(2):wq でvimを終了すると編集されたコマンドが実行される.

■vipe はどう入れる?
$ sudo apt install moreutils

■謝辞
vipe というコマンドにはググっても自力でたどり着くことができなかった.
Twitter上で教えてくださった @Mi_Sawa 氏に感謝する.

Ubuntuのランチャーが消えた(自分用メモ)

ある朝気がかりな夢から覚めると,Ubuntuデスクトップを起動したときにランチャーが一切表示されない状態になっていた.右クリックメニューでターミナルは起動できたので $ firefox とか打ちながら情報を探した.結論から言うと次のようにしてうまく行った:

$ sudo rm -rf ~/.cache/compizconfig-1
$ sudo rm -rf ~/.compiz
$ sudo rm -rf ~/.Xauthority
$ sudo rm -rf ~/.config/autostart
$ sudo apt-get install --reinstall ubuntu-desktop unity compizconfig-settings-manager upstart
$ sudo dconf reset -f /org/compiz/
$ setsid unity

参考にしたページ
https://toxweblog.toxbe.com/2017/10/01/ubuntu-no-icon-menu/

(自分用メモ)「ずれてく」プリンタへの対処

説明が難しいのですが,いまUbuntuデスクトップから使えるプリンタでは,複数ページ印刷するとページが変わるごとに印刷位置が右に五ミリぐらいずつずれていきます.一ページずつ印刷すればこの問題は起きません.そこで,たとえばfoo.pdf の pp.5-27をコマンドラインから印刷するときには

$ for i in {5..27}
> do
> lp -P $i foo.pdf
> done

とすればよいのですが,いちいち手で打つのが大変なので ~/.bashrc につぎのような関数を書いておくことにしました:

mylp(){
if test -f $1
then
for i in `seq $2 $3`
do
lp -P $i $1
done
fi
}

こうしておいてコマンドラインから

$ mylp foo.pdf 5 27

とすればよいわけです.根本的な問題の解決にはなっていませんが,これでよしとします.

(自分用メモ)tldr というコマンド

これは何ですか?
tldr コマンド名 とやるとコマンドの使用例を教えてくれるやつ.

情報源
https://github.com/raylee/tldr

インストール
$ mkdir -p ~/bin
$ curl -o ~/bin/tldr https://raw.githubusercontent.com/raylee/tldr/master/tldr
$ chmod +x ~/bin/tldr

最後に ~/bin にパスを通す.(例えば ~/.bashrc に
export PATH=$PATH:~/bin
という一行を加えて保存し,source ~/.bashrc する.)

使用例
オプションを忘れてしまっても使用例で教えてくれて助かる例:

$ tldr tar
tar

Archiving utility.
Often combined with a compression method, such as gzip or bzip.

- Create an archive from files:
tar cf target.tar file1 file2 file3

- Create a gzipped archive:
tar czf target.tar.gz file1 file2 file3

- Extract an archive in a target folder:
tar xf source.tar -C folder

- Extract a gzipped archive in the current directory:
tar xzf source.tar.gz

- Extract a bzipped archive in the current directory:
tar xjf source.tar.bz2

- Create a compressed archive, using archive suffix to determine the compression program:
tar caf target.tar.xz file1 file2 file3

- List the contents of a tar file:
tar tvf source.tar

- Extract files matching a pattern:
tar xf source.tar --wildcards "*.html"

(自分用メモ)ファイルサイズの計測と記録の自動化

記事執筆のモチベを管理する方法の一つが計測だ.計測と言っても,要するにモチベが上がれば良いのだから実のところ測るべき数字の「質」について悩む必要はない.行数でも良いがtexの場合ならば文字数が良いかもしれない.以前は wc -m した結果を自分でエクセルに貼り付けていたがこれは自動化できてしかるべきなのでCSVファイルを用意し,Makefile から自動で更新できるように方法を考えてみた.

count : yourfile.tex  ## count characters in the files.
	$(eval DATE=$(shell date "+%Y/%m/%d,"))
	$(eval INFO=$(shell wc -m $<))
	$(eval TMPNAM=$(shell mktemp))
	@echo $(DATE)$(INFO) > $(TMPNAM)
	@sed -i -e 's/$<//' $(TMPNAM)
	cat $(TMPNAM) >> count.csv
	@$(RM) $(TMPNAM)
	@tail count.csv

これで
2019/01/13,3000
のような行が毎回 Make count するたびに count.csv に追加されるようになる.

(自分用メモ)WSLが立ち上がらないとき:LxssManager

WSLを立ち上げようとしてもこんな風になってうまくいかないことがある:
WSLbozo

こんなときには「サービス」で検索し
サービスを探す
LxssManager をスタートさせれば良い.

texdoc(自分用メモ)

texlive を入れると自動的に texdoc というコマンドもついてくるが,ドキュメントを(カスタムインストールの設定などで)省いてしまった場合,
$ sudo apt install texlive-*-doc --fix-missing
のようにして補うと良い.

texdoc amsfont

のようにして調べ物をすると自動的に evince が呼ばれてPDFが表示される.