Julia でカメラから取得した画像を描画できましたね．

# 本日は

Julia上でカメラ画像を扱うというモチベーションで昔PyCall経由で呼び出した方法を書きましたが，OpenCVなどを明示的に呼び出さずにJulia単体でも行けることに気づいたので書いておきます．

# VideoIO.jl

VideoIO.jl がありまして https://github.com/JuliaIO/VideoIO.jl これを使うと良いことがわかりました．

## install

```
julia> using Pkg
julia> Pkg.add("VideoIO")
```

はい．これでOK

USBカメラをつけておけばすぐに使えます．

## Quick Start

動作確認をしてみましょう．VideoIO.jl が提供しているAPIとして Makie.jl を用いた撮像結果を描画するデモがあるのでそれを使ってみましょう．

```julia
julia> using VideoIO
julia> using Makie
julia> viewcam()
```

そうするとMakieのsceneオブジェクト上にOpenCVの `cv2.imshow` で出力するような結果が出ます．

## 動作確認できたハード

コードを見るとWindows Mac Linux で使えるようにはなっているようです．バージョンが1.1のJulia上でですが

- Jetson nano (USBカメラ接続で)
- Raspberry Pi 3　（ラズパイ公式カメラで）
- Mac Book Air (2018)

では動作確認できてます．

- ただし，ラズパイの場合はOpenCVなどでも必要な呪文として実行前に `$ sudo modprobe bcm2835-v4l2` が必要です． (`/etc/modules` に `bcm2835-v4l2` を書いておけば呪文唱えなくても多分大丈夫)
  - あくまでも `f=opencamera()` をして `read(f)` の結果しか確かめて居ません．描画用のソフトはJulia on Raspberry Pi では充実してないのでJulia以外の方法を検討する方が今のところ近道です．
- iMac 2019 だとなぜか認識しない．OpenCVやffmpeg からカメラは見えてるのに．MacBookだとできるのに．なぜかほんとうにわからない．ナゾイ.iMacでできた方いらっしゃいましたら教えてください．

# Usage

`viewcam()` だとナンもできないので `opencamera()` を使いましょう.

```julia
julia> using VideoIO
julia> f = opencamera() # Pythonであれば import cv2; cv2.VideoCapture()相当
julia> img=read(f) # RGB を要素にもつArrayが得られる
julia> close(f) #  カメラを閉じる
```

デフォルトのカメラデバイスが何かは `VideoIO.DEFAULT_CAMERA_DEVICE` で見ることができます．
`VideoIO.CAMERA_DEVICES` で候補を調べることができます．Macだと例えば下記のような出力になりますね．

```julia
julia> VideoIO.DEFAULT_CAMERA_DEVICE
Base.RefValue{String}("FaceTime HD Camera (Built-in)")
julia> VideoIO.CAMERA_DEVICES
3-element Array{String,1}:
 "FaceTime HD Camera (Built-in)"
 "Capture screen 0"
 "Capture screen 1"
```

上で出てきたどれかの文字列を `opencamera` 関数の引数に渡すことで対応するデバイスの情報を捉えることができます．
Capture screen は実際にはカメラデバイスではなくスクリーンショットを取得する物と対応しているようです．

`close` でカメラを閉じます．カメラを開いてる間にエラー・例外が発生してしまうとカメラの撮像をJuliaが止めてしまいますが，再びオープンさせる時に失敗します．ですのでいったん exit してJuliaのREPLを再起動するか例外をキャッチして`close` させるようなコードを書いておく必要がありますね．



# サンプルコード

```julia
# Usage:
# REPL 開いて上記のコードを読み込んで main_makie() を実行させる
# または main_plots() を実行させる．

import VideoIO
import Plots
import AbstractPlotting


import Makie
using Images, CoordinateTransformations, OffsetArrays

function main_makie()
    f = VideoIO.opencamera()
    img = read(f)
    tfm = recenter(RotMatrix(pi/2), Images.center(img))
    img=parent(warp(img,tfm))
    scene = Makie.Scene(resolution = size(img))
    makieimg = Makie.image!(
        scene,
        img, 
        show_axis = false,
        scale_plot = true
    )[end]
    display(scene)

    while !eof(f)
        # OffsetArrays -> Array
        img=parent(warp(read(f),tfm))
        @info size(img)
        makieimg[1] = img
        sleep(1/f.framerate)
    end
end


function main_plots()
    f = VideoIO.opencamera()
    img = read(f)
    p=Plots.plot(img) |> display
    for i in 1:100
        @info i
        read!(f,img)
        Plots.plot(img, title="$i") |> display
    end
end


```

Makieが使えない場合でも Plots の方で画像を plot させればいけるんじゃないかなっと思って作りました．（動きます）．
ですがMakieの方が応用を考えるとパフォーマンスが出そうな感触です．

多分上記のコードよりは VideoIO.jl の中に定義されている `viewcam()` のソースコードを読んでコードを抽出した方が綺麗なコードになって居ます．Sceneオブジェクトを９０度回転するか img オブジェクトを rot させるかの違いですが，こうせざるを得ないのはMakieの画像描画仕様がアレだからです．

# まとめ

- VideoIO.jl でカメラ画像を読み込めた．
- iMacで動かないけれど手持ちのデバイスでは動作させられた．
- ラズパイとかJetson-nanoで動いたのでJuliaでの画像認識デモの可能性が出てきましたね．