M5StickV MicroPython 画像処理<imageクラス>

実機にて調べながら書いています．
間違いなど，どしどしご指摘ください．

# ●フレームバッファに書く
　開発時にMaixPy IDEの右上のフレームバッファ表示を利用することが多い．
デバッグ時にこのフレームバッファに書きたいことがある．
特にUNIT VはLCDが無いため,うまく使いたくなる．
フレームバッファに書きこんだら`senosr.snapshot()`を呼び出すのがポイント．これを呼ぶことでMaixPy IDEの画面に反映される．`snapshot()`しているので,実機のフレームバッファは書き換えられていると思われる．

`Image sensor.get_fb()`

返り値はImageオブジェクト．このオブジェクトに書き込めば,Maix Py IDEのフレームバッファ表示部（右上）に表示される．
　sensor.snapshot()でも渡されるのはフレームバッファなので,実質同じ．ループ内で使う場合,次のような感じでOK．

```python
fbuffer = sensor.get_fb();    #
img = sensor.snapshot();
img.draw_rectangle(50,50,100,100);
fbuffer = img;    #
```

IDEの表示を綺麗にしたい場合（フレームレートは落ちる）
`sensor.set_jb_quality(95)`

<br>

# ●画像オブジェクト生成
`image.Image()`　：画像オブジェクトを生成できる．
240×135の画像が生成される．
`image.Image(filename)` ：ファイル名で指定した画像のオブジェクトを生成する．ビットマップやJPEGファイルに対応する．
　bmpの場合16ビットbmpに対応する．24ビットだとエラー（OSError: [Errno 5] EIO）になる．
　openMVのドキュメントには引数としてcopy_to_fbの記述があるが,対応していないようで,引数として与えても無視される．

```
import image

img = image.Image()
img2 = image.Image("/flash/mai.bmp")    #内蔵フラッシュの場合
img3 = image.Image("/sd/kuraki.jpg")    #SDカードのルート・ディレクトリの場合
```


<br>

# ●画像の切り出し&コピー
`Image.copy()`でディープ・コピーできる．
`Image.crop()`は使えないため，copy()にタプルを与え，切り出す．
　8の倍数以外でも切り出せる．
　x, yの位置は省略できず，必ず4lengthのタプルを与える．
　引数は前から x位置, y位置, 幅，高さ
`Image.copy()`の引数はタプルで与えるため2重かっこにしてある

```python
img = sensor.snapshot()
img2 = img.copy((50,50,10,32))
```

<br>

# ●画像を保存

```python
img = sensor.snapshot()
img.save("/flash/1.bmp")    #内蔵フラッシュに保存
img.save("/sd/2.bmp")    #SDカードに保存
```

<br>

# ●画像による位置推定
　画像を元にカメラ（デバイス）の位置変化を推定できます．
　急激に画像が変化すると，追従できません．
　フレーム・レートとの調整が必要です．
`image.find_displacement(image)`メソッドでdisplacementオブジェクトを取得し，この取得したオブジェクトから，画像の変化量を取得します．引数に与えるのは差分を取る元画像．
displacementオブジェクトにはゲッター・メソッドが用意されており，X方向変位量＋Y方向変位量（logpolar=False），もしくは，角度変位量＋大きさ変位量（logpolar=True），加えて品質？を取得できるが，5つのプロパティ全てリストからもアクセスできる．つまり`displacementObject.x_translation()` と　`displacementObject[0]` は等価．

フルステートメント
　`image.find_displacement(template[, roi[, template_roi[, logpolar=False]]])`

 - *template* 比較画像
 - *roi* 画像の処理範囲
 - *template_roi* 元画像の処理範囲
 - *logpolar* Falseなら平行移動を検知，Trueなら回転と拡大/縮小を検知．返すオブジェクトは同じ`displacementObject`．

OpenMVのドキュメントにはセンサ・サイズの設定として`sensor.B64x64`を使えと書いてある．

```python
import sensor, image, time, lcd

lcd.init(freq=15000000)
sensor.reset()                      

sensor.set_pixformat(sensor.GRAYSCALE)
sensor.set_framesize(sensor.QQVGA)
sensor.set_windowing((64,64))
sensor.skip_frames(time = 2000)
clock = time.clock()

x=0.0
y=0.0
oldImage = sensor.snapshot()
while(True):
    clock.tick()                    # Update the FPS clock.
    img = sensor.snapshot()         # Take a picture and return the image.
    lcd.display(img)
    result = img.find_displacement(oldImage)    #displacementオブジェクトを取得
    x += result.x_translation()    #x方法の移動量（画像変化量）
    y += result.y_translation()
    x = round(x,3)    #4捨5入
    y = round(y,3)
    oldImage = img.copy()
    print("x: ", x, "y: ", y)
    #print(result)
    print(clock.fps()) 
```

<br>

# 探す系

## ●2枚の画像の変化を調べる

メソッド・シグネチャ
`displacement image.find_displacement(image)`
`float displacement.x_translation()`

```python
import sensor, image, time, lcd

sensor.reset()                      
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QQVGA)
sensor.skip_frames(time = 1000)
clock = time.clock()
img2 = sensor.snapshot()

while(True):               
    img1 = sensor.snapshot()        
    dis = img1.find_displacement(img2)
    print("displacement:")
    print(dis.x_translation())
    img2 = img1.copy()
```

<br>

## ●画像の中から線を探す

　線が見つからなければ，空のリストを返す．
線が見つかれば，`line`クラスのオブジェクトのリストを返す．つまり複数の線を見つけられる．
lineオブジェクトはx1, y1, x2, y2, 線の長さ，ハフ変換後の線の長さ，ハフ変換後の角度，ハフ変換後のP値を持つ．
　それぞれの数値はタプルから`position_of_X1 = returnedTupple[0]`の様に取り出せる(ゲッター・メソッドもある）．
[ここにも使用例があります](https://qiita.com/Lathe/items/5ea65aa1e14d7b201a9e)

`image.line[] image.find_lines([roi[, x_stride=2[, y_stride=1[, threshold=1000[, theta_margin=25[, rho_margin=25]]]]])``

```python
import sensor, image, lcd

#略　カメラ初期化処理

while True:
    img = sensor.snapshot()
    lcd.display(img)
    lines = img.find_lines()
    print(lines)
    #if lines:
        #print(lines[0][0])    #1つめのlineオブジェクトのx1を出力
```

<br>

## ●画像の中から円を探す
　ハフ変換を使って画像の中から円を探します．

シグネチャ
`image image.find_circles([roi[, x_stride=2[, y_stride=1[, threshold=2000[, x_margin=10[, y_margin=10[, r_margin=10]]]]]])`

`roi`　画像の中で特定の矩形範囲だけを処理したい場合，その領域を示すROIオブジェクトを渡す
`x_stride`　ハフ変換でスキップするピクセル数

　円が見つからなければ，空のリストを返す．
円が見つかれば，`circle`オブジェクトのリストが返される．つまり複数の円を見つけられる．
`circle`オブジェクトは，x1（センター）, y1（センター）, 半径，magnitudeを持つ．
　次のプログラムで6fps（画像複雑）～11fps（画像真っ黒）程度で動く．
`set_windowing((128, 64))`の設定で，12（複雑）～16fps（黒）程度．
[ここにも使用例があります](https://qiita.com/Lathe/items/5ea65aa1e14d7b201a9e)
[ここにも使用例があります2](https://qiita.com/Lathe/items/07625e4759eac5eb0280)

```python
import sensor, image, time, lcd

lcd.init(freq=15000000)
lcd.rotation(2)

sensor.reset()

sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QQVGA)
#sensor.set_windowing((128, 64))
sensor.skip_frames(time = 2000)
clock = time.clock()

while(True):
    clock.tick()
    img = sensor.snapshot()
    lcd.display(img)
    obj = img.find_circles()
    print(obj)
    print(clock.fps())
    #time.sleep(1)
```

<br>

## ●画像の中から四角を探す

　`rect`オブジェクトは，x1, y1, 幅，高さ，magnitudeを持つ．

```python
img = sensor.snapshot()
obj = img.find_rects()
print(obj)
```

<br>

## ●画像の中からQRコードを探す

　QRコードとペイロード（URLなど）を読み取れるが，画像の中にいろいろな要素があると，なかなかうまく読み取れないようである．
　QRコードを見つけた場合，QRコードオブジェクトのリストを返す．常にリストを返してくるため，1つのQRコードであっても，`qrcode[0]`の様に扱う必要がある．
　qrcodeオブジェクトは，x1, y1, 幅，高さ，ペイロード，ヴァージョン，ECCレベル，マスク，データ・タイプ，eci，を持つ．それぞれの値は，リストの要素として取得可能であるが，ゲッター・メソッドもある．
　（MaixPy DOCによると，micropythonは1文字あたり10桁のコードをサポートしないため，漢字をデコードする場合，独自にハンドリングしなければならない．）

```python
import sensor, image

img = sensor.snapshot()
qrcode = img.find_qrcodes()
if qrcode:
    print(qrcode[0].pyload())
```

<br>

# 描画系
## ●ドット打つ

　画像をピクセル単位で操作するには次のようにする．
`image.set_pixel(x, y, color)`
colorはRGBを表すタプル．例えば(255, 0, 0)．

```python
import sensor, image, time, lcd

lcd.init(freq=15000000)
lcd.rotation(2)

sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QQVGA)

#sensor.set_windowing((128,64))
sensor.skip_frames(time = 2000)
clock = time.clock()

while(True):
    img = sensor.snapshot()
    lcd.display(img)
    counter = 0
    while counter <160:
        counter += 1
        img.set_pixel(counter, 30, (255,0,0))    #1pixelの赤いドットを打つ
        lcd.display(img)
    time.sleep(3)
```

<br>

## ●画像からピクセルを取得

Grayscale: x,y位置のグレースケール値を返す
RGB565: x,y位置のピクセル値をRGB888形式のタプルで返す（青なら0,0,255が返ってくる）
Bayer image:サポートされない
Compressed image:サポートされない

```python
img = sensor.snapshot()
rgbtuple = img.get_pixel(20,30)    #座標20,30の位置のピクセルをRGB888で取得
print("pixel: ", rgbtuple)
```

<br>

## ●文字を描画

メソッド・シグネチャ
image image.draw_string(x, y, text[, color[, scale=1[, x_spacing=0[, y_spacing=0[, mono_space=True]]]]])

 - *x(int), y(int)* 描画座標（原点は描画される画像の左上が(0,0))
 - *text* 描画するテキスト
 - *color(int,int,int)* 描画色
 - *scale(float)* 文字サイズ（大きくすると文字が崩れる．良いところ3程度か）
 - *x_spacing(int)* x方向文字間隔（ピクセル単位？）
 - *y_spacing(int)* y方向文字間隔．テキストが改行している場合に意味を持つ．
 - *mono_space(bool)* デフォルトはTrue．Falseにすると，x方向の文字間隔が狭く（1ピクセル？）なる

OpenMVの資料にある，以下のオプション引数は定義されていないようで，何も効果はなし．
 - *char_rotation(float)* ?エラーにならないが，変化なし
 - *char_hmirror(bool)* ?エラーにならないが，変化なし
 - *string_rotation(float)* ?エラーにならないが，変化なし
 - *string_hmirror(bool)* ?エラーにならないが，変化なし
 - *string_vflip(bool)* ?エラーにならないが，変化なし

　このメソッドは返り値として描画された`image`オブジェクトを返す．

```python

# 省略
img = sensor.snapshot()
img2 = img.draw_string(50,50, "text\ntext2",(0,0,255) , scale=2.9, x_spacing=1, y_spacing=0)
lcd.display(img)
lcd.display(img2)    #1行上と全く同じ
```

<br>

## ●線を描画

メソッド・シグネチャ
`image image.draw_line(x0, y0, x1, y1[, color[, thickness=1]])`

```tython
    img.draw_line(50,50,100,100);
```

<br>

## ●矩形を描画

　画像オブジェクトに四角形を描画する．
塗りつぶす場合は，引数リストに`fill=True`をつける．

メソッド・シグネチャ
`image image.draw_rectangle(x, y, w, h[, color[, thickness=1[, fill=False]]])`

- *`x(int)`　左座標
- *`y(int)`　上座標
- *`w(int)`　幅
- *`h(int)`　高さ
- *`color(int,int,int)`　色．デフォルトは白（255,255,255）
- *`thickness(int)` 線の太さ（デフォルトは1）
- *`fill(bool)`　塗りつぶし（デフォルトは塗りつぶさない）

返り値は`image`オブジェクト

```python
import image

img = image.Image()    #画像オブジェクト生成
img.draw_rectangle(0,0,50,50)    #デフォルトは黒で描画，塗りつぶしなし
img.draw_rectangle(0,0, 240,130,(0,0,0), fill=True)    #黒で描画して塗りつぶす
img2.draw_rectangle(20,20,100,80,(0,0,200), thickness=3)    #青で描画，線の太さは3ピクセル
```

<br>

## ●円を描画

`image image.draw_circle(x, y, radius[, color[, thickness=1[, fill=False]]])`


```python

```

<br>

## ●十字を描画

メソッド・シグネチャ
`image image.draw_cross(x, y[, color[, size=5[, thickness=1]]])`

 - *`x(int),y(int)`* 十字の中心で指定．
 - *`color(int,int,int)`* 色 
 - *`size(int)`* 十字の大きさ（デフォルトは5くらい）
 - *`thickness(int)`* 線の太さ（デフォルトは1）

　返り値は描画された`image`オブジェクト．

```python
img.draw_cross(100,100,(0,0,255),size=5,thickness=2)
```

<br>
## ●矢印を描画
　引数は，x1, y1, x2, y2, 色，太さ

```python
while(True):
    img = sensor.snapshot()
    img.draw_arrow(20,50,100,70,(255,0,0), thickness=3)
    lcd.display(img)いさい
```

<br>

## ●画像を描画

メソッド・シグネチャ
`image image.draw_image(image, x, y[, x_scale=1.0[, y_scale=1.0[, alpha=256[, mask=None]]]])`

 - *`image`* 描画する画像オブジェクト
 - *`x(int),y(int)`*
 - *`x_scale(float)`* x方向縮尺
 - *`y_scale(float)`*
 - *`alpha(int)`* アルファブレンディング0-256(デフォルトは256．256で100％）
 - *`mask(type image)`* マスク用白黒画像

```python

```

<br>

# 合成系
## ●和演算（image.add）

　画像に，別に用意した白黒画像を重ねる．白い部分だけが，元画像に重ねられる．（2つの画像は同じサイズで無ければならない．
メソッド・シグネチャ
`image.add(add_image[, mask=None])`

 - *`add_image(type image)`* ：白黒画像
 - *`mask`*

<br>

## ●差演算（image.sub）

　画像から，別に用意した白黒画像を引き算する．（2つの画像は同じサイズでなければならない）
メソッド・シグネチャ
`image.sub(sub_image[, reverse=False[, mask=None]])`

 - *`sub_image(type image)`* ：白黒画像
 - *`reverse(bool)`* ：重ねる白黒画像を白黒反転
 - *`mask`*

<br>

## ●乗演算（image.mul）

　画像を掛け算する．2つの画像を重ねるような効果．カラー画像①に白黒画像②をかけると，②の白い部分だけ①を切り抜いた画像になる．
メソッド・シグネチャ
`image.mul(mul_image[, invert=False[, mask=None]])`

 - *`mul_image`*
 - *`invert`*
 - *`mask`*

```python
img = sensor.snapshot()    #1枚目の写真を撮影
img = img.copy((0,0,240,135))    #大きさをそろえる
lcd.display(img)
time.sleep(2)

img2 = sensor.snapshot()    #2枚目の写真を撮影
img2 = img2.copy((0,0,240,135))    #大きさをそろえる
lcd.display(img2)
time.sleep(2)

img3 = img2.mul(img)
lcd.display(img3)
```

<br>

## ●徐演算

　ほかの画像で割り算．
`image.div(image[, invert=False[, mask=None]])`

<br>

## ●2つの画像の最小値

　ピクセル単位で2つの画像を比べ小さい方の画像を返す．
＜現状image_minしか返さないようである＞

`image image.min(image_min[, mask=None])`

 - *`image_min(type image)*`
 - *`mask(type image)`*

<br>

## ●2つの画像の最大値

　ピクセル単位で2つの画像を比べ，大きい方の画像を返す．
　比べる画像は同じ画像サイズ，ピクセル・フォーマットでなければならない．
＜現状image_maxしか返さないようである＞

`image image.max(image_max[, mask=None])`

 - *`image_max(type image)`*
 - *`mask(type image)`*

<br>

## ●差演算(image.difference)

　2つの画像に対してピクセル単位で絶対的な差を取る．
　圧縮画像，ベイヤー画像には対応しない．

メソッド・シグネチャ
`image image.difference(diff_image[, mask=None])`

 - *`diff_image`*
 - *`mask`*　白黒のマスク画像


<br>

## ●ブレンド(image.blend)

　2つの画像を組み合わせる．
　圧縮画像，ベイヤー画像には対応しない．

メソッド・シグネチャ
`image image.blend(ble_image[, alpha=128[, mask=None]])`

 - *`ble_image`*　重ね合わせる画像オブジェクト
 - *`alpha(int)`*　0~256？の整数
 - *`mask`*　白黒のマスク画像

<br>

## ●ヒストグラム平坦化(image.histeq)

　ヒストグラム平坦化により画像のコントラストや明るさを調整する．
　特にヒストグラムが偏った（明るさ的に特定の領域に集中している）画像に対してコントラスト改善に有効．
　顔や，物体認識の前処理として効果的．
　圧縮画像，ベイヤー画像には対応しない．

メソッド・シグネチャ
`image image.histeq([adaptive=False[, clip_limit=-1[, mask=None]]])`

 - *`adaptive(bool)`*　Trueにすると，品質が上がるが処理時間が長くなる．
 - *`clip_limit`*　適用的ヒストグラム平坦化のクリップ・リミット．良い結果を得るには小さな数字を指定してください．
 - *`mask`*　白黒のマスク画像


<br>

## ●ブラー・フィルタ(image.mean)

　ぼやけた画像を作る．
　`threshold=True`では白黒の輪郭抽出画像が得られる．
　圧縮画像，ベイヤー画像には対応しない．

メソッド・シグネチャ
`image image.mean(size, [threshold=False, [offset=0, [invert=False, [mask=None]]]]])`

 - *`size`*　カーネル・サイズ．1（3×3）もしくは2（5×5）
 - *`threshold(bool)`*　
 - *`offset(int)`*　`threshold=True`の場合のオフセット値
 - *`invert(bool)`*
 - *`mask`*　画像と同じサイズの白黒マスク画像

<br>

## ●フィルタ(image.mode)

　2つの画像を組み合わせる．
　圧縮画像，ベイヤー画像には対応しない．

メソッド・シグネチャ
`image image.mode(size[, threshold=False, offset=0, invert=False, mask])`

 - *`size`*　
 - *`threshold(bool)`*　
 - *`offset`*
 - *`invert(bool)`*
 - *`mask`*　白黒のマスク画像

<br>

## ●フィルタ(image.midpoint)

　2つの画像を組み合わせる．
　圧縮画像，ベイヤー画像には対応しない．

メソッド・シグネチャ
`image image.midpoint(size[, bias=0.5, threshold=False, offset=0, invert=False, mask])`

 - *`size`*　
 - *`bias`*
 - *`threshold(bool)`*
 - *`offse`*
 - *`invert(bool)`*　
 - *`mask`*　白黒のマスク画像

<br>

## ●ガウシアン・フィルタ

　ガウシアンカーネルを使って画像をスムースにする．

メソッド・シグネチャ
`image image.gaussian(size[, unsharp=False[, mul[, add=0[, threshold=False[, offset=0[, invert=False[, mask=None]]]]]]])`

 - *`size(int)`* カーネル・サイズ．1 or 2
 - *`unsharp(bool)`* デフォルトはFalse
 - *`mul(float)`*　ピクセルに掛ける値．画像全体のコントラストを調整する
 - *`add(float)`*　ピクセルに足す値．画像全体の明るさを調整する
 - *`threshold(bool)`* デフォルトはFalse
 - *`offset(int)`*
 - *`invert(bool)`*
 - *`mask(type image)`* マスク画像

使用例

```python
img.gaussian(1, unsharp=False, mul=1, add=0, threshold=False, offset=0, invert=False)
```

<br>

## ●ラプラシアン・フィルタ

　輪郭を強調する．
　圧縮画像やベイヤー画像はサポートされていない．

メソッド・シグネチャ
`image image.laplacian(size[, sharpen=False[, mul[, add=0[, threshold=False[, offset=0[, invert=False[, mask=None]]]]]]])`

 - `size(int)` カーネルサイズ．1なら3×3．2なら5×5.
 - `sharpen(bool)`
 - `mul(int)`　各ピクセルに掛ける値．画像全体のコントラストを高める
 - `add(int)`　各ピクセルに足す値．画像全体の明るさを調整する
 - `threshold(bool)`
 - `offset(int)`
 - `invert(bool)`
 - `mask(type image)`

<br>

## ●バイラテラル・フィルタ

　輪郭を保ったまま，画像を滑らかにする．

`image.bilateral(size[, color_sigma=0.1[, space_sigma=1[, threshold=False[, offset=0[, invert=False[, mask=None]]]]]])`

 - size
 - color_sigma(float)
 - space_sigma(float)
 - threshold(bool)
 - offset(int)
 - invert(bool)
 - mask(type image)

```python
img.bilateral(1, color_sigma=0.1, space_sigma=3.1, threshold=False, offset=1, invert=False)
```
<br>

# 画像処理
## ●近似色領域を囲む線を描画

　圧縮画像，ベイヤー画像はサポートしない．

`image image.dilate(size[, threshold[, mask=None]])`

 - *`size`*　
 - *`threshold`*　線描画の閾値，大きいほど描画しない．省略した場合は標準的な動作
 - *`mask(type image)`*

<br>

## ●近似領域を囲む線を消す

`image.erode(size[, threshold[, mask=None]])`

 - *`size`*
 - *`threshold`*
 - *`mask`*

<br>

## ●画像を白黒で反転(image.invert)

　白黒画像の白と黒を反転する．
　カラー画像でも動作する．その場合の結果は`image.negate()`と同じと思われる．

`image image.invert()`

<br>

## ●画像を反転(image.negate)

　各チャネルごとにピクセル単位で反転する（それぞれのチャネルごとに`255-ピクセル値`を行う）
　圧縮画像，ベイヤー画像はサポートされない．

`image image.negate()`

<br>

## ●ディレート

Add pixels to the edges of the segmented area.
　この機能は((size2)+1)x((size2)+1)の畳み込みカーネルで実装されている．
隣接するピクセルとの合計値が`threshold`より大きい場合カーネルの中央ピクセルがセットされる．
`threshold`が設定されない場合，このメソッドは`standard corrosion method`として機能する．
`threshold`が設定されれば， エロード？する特定のピクセルを指定できる．
例えば，2より小さいピクセルを指定するために`threshold`に2を指定する．
　ピクセル単位でマスクをする場合，別にマスク画像を用意する．これは白と黒のピクセルのみで構成される画像で，処理する画像と同じサイズでなければならない．マスク画像でセットされているピクセルのみが変更される．
　圧縮画像やベイヤー画像はサポートされない．
　画像オブジェクトを返すので，メソッドチェーンとして呼び出せる．

`image.dilate(size[, threshold[, mask=None]])`


## ●オープン

 エロ―ジョン，ディレーションの順で画像に適用する．`image.erode()`と`image.dilate()`を参照．
　ピクセル単位でマスクをする場合，別にマスク画像を用意する．これは白と黒のピクセルのみで構成される画像で，処理する画像と同じサイズでなければならない．マスク画像でセットされているピクセルのみが変更される．
　圧縮画像やベイヤー画像はサポートされない．
　画像オブジェクトを返すので，メソッドチェーンとして呼び出せる．

`image.open(size[, threshold[, mask=None]])`


## ●クローズ

　ディレーション，エロ―ジョンの順で画像に適用する．詳しくは`image.erode()`と `image.dilate()`を参照．

　ピクセル単位でマスクをする場合，別にマスク画像を用意する．これは白と黒のピクセルのみで構成される画像で，処理する画像と同じサイズでなければならない．マスク画像でセットされているピクセルのみが変更される．
　圧縮画像やベイヤー画像はサポートされない．
　画像オブジェクトを返すので，メソッドチェーンとして呼び出せる．

`image.close(size[, threshold[, mask=None]])`

## ●トップハット

　オリジナル画像とオープン処理した画像の差を返す．
　ピクセル単位でマスクをする場合，別にマスク画像を用意する．これは白と黒のピクセルのみで構成される画像で，処理する画像と同じサイズでなければならない．マスク画像でセットされているピクセルのみが変更される．
　圧縮画像やベイヤー画像はサポートされない．

`image.top_hat(size[, threshold[, mask=None]])`

## ●ブラックハット

`image.black_hat(size[, threshold[, mask=None]])`
　オリジナル画像とクローズ処理後の画像の差を返す．
　別に用意するマスク画像は白と黒のピクセルのみで構成される画像で，処理する画像と同じサイズでなければならない．マスク画像でセットされているピクセルのみが変更される．
　圧縮画像やベイヤー画像はサポートされない．

## ●縁を強調

メソッド・シグネチャ
`image image.find_edges(edge_type[, threshold])`

 - edge_type(int)
  - image.EDGE_SIMPLE 単純な閾値に基づくハイパス・フィルタ．
  - image.EDGE_CANNY Canny縁検出アルゴリズム
 - threshold(tubple(int, int)) 下側閾値と上側閾値のタプル．省略すると(100,200)

　グレースケールの画像のみ対応している．

```python
img = img.find_edges(image.EDGE_SIMPLE,threshold=(100,200))
```

<br>

## ●似た色の隣接画素を塗りつぶし

メソッド・シグネチャ
`image image.flood_fill(x, y[, seed_threshold=0.05[, floating_threshold=0.05[, color[, invert=False[, clear_background=False[, mask=None]]]]]])`

 - x(int),y(int)
 - seed_threshold(float)
 - floating_threshold(float)
 - color(tuple(int,int,int))
 - invert(bool)
 - clear_background(bool) 塗りつぶす場所以外の画素をクリアするかどうか．デフォルトはFalse．Trueを指定すると，塗りつぶす場所以外は黒になる．
 - mask(type image)
 
<br>

## ●画素を反転

　メソッド・シグネチャ
`image image.invert()`

　画像全体のピクセル値を反転させる．ネガっぽい絵になる．
　グレースケール，およびカラー画像で使用できる．圧縮された画像には非対応．

<br>

## ●画像を縮小

`image.mean_pool()`: 元画像を縮小する
`image.mean_pooled()`: 元画像を変更せず，縮小した画像を返す

```python
while(True):
    img = sensor.snapshot()
    lcd.display(img)
    time.sleep(3)
    img.mean_pool(2,2)    #x,y方向ともに1/2に縮小
    lcd.display(img)
    time.sleep(3)
```

<br>

## ●画像サイズを取得

　バイト単位で画像のサイズを取得する

```python
bytes = img.size()
print(bytes, "bytes")
```
<br>

## ●ピクセル・クリア

　画像の全てのピクセルを0にする．
引数にマスク用の画像を与えて，マスク値のみクリアすることできる．
マスク用の画像は白黒の2値のみで，クリアする画像と同じサイズにする．

```python
img.clear()
```


## ●グレースケール変換

メソッドシグネチャ
`image image.to_grayscale([copy=False])`
　デフォルトで元画像も変換され,返り値としても同じ画像が返される．
　`copy=True`とすると元画像は変更されない．

```python
img.to_grayscale();
img2 = img.to_grayscale(copy=True)    #元画像を残す場合
```

## ●バイナリ変換

メソッドシグネチャ
`image.binary(thresholds[, invert=False[, zero=False[, mask=None]]])`
　引数は，下限値と上限値のタプルを色ごとに与える．そのため第1引数はタプルのリストになる．
グレースケール画像の場合，次のようにする．

　全てのピクセルを白か黒のどちらかにする．白にするか黒にするかは閾値リストに基づいて決める．
　`threshold`のリストはタプルでなければならない．
`[(lo, hi), (lo, hi), ..., (lo, hi)]`
　捕捉したい色の範囲を定義する．
　グレースケールの場合，各タプルは最小のグレー値と最大のグレー値という2つの値を含まなければなりません．これらのしきい値の間に入らないピクセルが対象となります．

RGB565画像の場合，各タプルは6個の値を必要とします​​(l_lo, l_hi, a_lo, a_hi, b_lo, b_hi)．最少と最大のLABのLとAとBチャネルの値です．

<font color=red>注意（意図しないタプルの場合もエラーとならない）</font>
　このメソッドは使いやすいように，指定されていない値を自動で補間します．タプルが6より大きい場合，それらの値は無視されます．タプルが小さい場合，残りの値は最大のレンジとなります．

##### お知らせ
　必要な`threshold`の値を取得するためにMaixpy IDEが使えます．表示されているフレームバッファの対象部分でクリックかドラッグすると，ヒストグラムが更新されます．
Then just write down the starting and falling positions of the color distribution in each histogram channel. These will be the low and high values ​​of thresholds. Since the difference between the upper and lower quartiles is small, it is better to manually determine the threshold.

You can also determine the color threshold by entering Tools -> Machine Vision -> Threshold Editor in OpenMV IDE and dragging the slider from the GUI window.

invert Inversion threshold operation, pixels are matched outside the known color range instead of in the known color range.

Set zero to True to make the threshold pixels zero and keep the pixels not in the threshold list unchanged.

mask is another image used as a pixel-level mask for drawing operations. The mask should be an image with only black or white pixels and should be the same size as the image you are drawing. Only the pixels set in the mask are modified.

　画像オブジェクトを返すので，メソッドチェーンとして呼び出せます．
　圧縮画像やベイヤー画像はサポートしません．

```python
img.binary([(100,150)])
```

カラーの場合，次のようにする．

```python
img.binary([(50, 150,50,150,50,15)]) #L,A,B
```

## ●XOR

　各ピクセルについてXORする．
メソッドシグネチャ
`image.b_xor(image[, mask=None])`

　image.difference()に近い効果になる．

<br>

## ●sub

　ピクセル単位の引き算
`image.sub(image[, reverse=False[, mask=None]])`

## ●difference

　2枚の画像の差分をとる．
`image.difference(image[, mask=None])`
　グレースケール，カラー画像（byte-inverted RGB565）に対応する．
 圧縮画像，ベイヤー画像に非対応．

```python
import sensor, image, time, math

gc.collect()
sensor.reset()
sensor.set_pixformat(sensor.GRAYSCALE)
sensor.set_framesize(sensor.QVGA)
#sensor.set_windowing((128,120))
sensor.skip_frames(time = 1000)
clock = time.clock()
clock.tick()

img2 = sensor.snapshot()

while(True):
    clock.tick()

    img = sensor.snapshot()
    #img.binary([(100,150)])
    img3 = img.copy()
    img = img.difference(img2)
    #img = img.b_xor(img2)
    img2 = img3.copy()
    print(clock.fps())
```

## ●カートゥーン変換

　隣接領域を同じ色で塗りつぶす．
　2つの引数はどちらも指定範囲は0～1
第1引数　Seed threshold: 塗りつぶしの閾値．大元のピクセル値とピクセルの始めの値との差を制御する
第2引数　Floating threshold: 塗りつぶす領域のピクセル値と隣接するピクセル値との差を制御する

```python
while(True):
    img = sensor.snapshot()
    img2 = img.cartoon(0.5, 0.2)
    lcd.display(img2)
    time.sleep(2)
```

<br>

## ●画像からカスケード・オブジェクトを探す

　`image.find_features(cascade[,threshold=0.5[,scale=1.5[,roi]]])`
このメソッドはhaar Cascadeにマッチする形状を画像から探し，その領域の矩形（x,y,幅,高さ）のリストを返す．形状が見つからなければ空のリストを返す．
　`Cascade`はHaar Cascadeオブジェクトである．詳細は`HaarCascade()`を参照．
`threshold`は0.0～1.0の小数値で小さいほどヒットしやすくなるが，間違ったものに反応する可能性も高くなる．数字が大きいほどヒットしにくくなる．
　`Scale`は小数値で1.0より大きくScaleファクタが大きいと速くなるが，イメージ・マッチはプアになる．適切な数値は1.35～1.5程度．
　`Roi`は矩形を表すタプルで，指定されない場合は画像全体が処理の対象になる．処理領域はroi領域に限定される．
　`GRAYSCALE`の画像のみがサポートされる．

```python

```

<br>

# ●カスケード・オブジェクトを作る

　Haar Cascadeフィーチャー記述子は`image.find_features（）メソッドで使用される．このクラスにユーザが直接呼べるメソッドは無い．
　コンストラクタは`Class image.HaarCascade(path[,stages=Auto])である．
　Haar Cascadeバイナリ（OpenMV Cam用フォーマット）からHaar Cascadeを読み込むが，`path`の代わりに`frontalface`という文字列を与えると，ビルトインされた顔分類器がメモリに読み込まれる．`eye`という文字列も指定可能．このメソッドは`image.find_features()`で利用される`Haar Cascade`オブジェクトを返す．
>　OpenMV Camを使って，オリジナルの`Haar Cascadeを作れる．まずGoogle検索で検>出したい物の`Haar Cascade`を誰かがすでに作っていないか確かめる．もしなければ>自分で作らなければならない(結構大変）．自分用の`Haar Cascade`の作り方に関する情報は，次のOpen CVのHaar CascadeをどのようにOpenMV Camで利用できる形に変換するか，を見ること．
Haar Cascadeって何？
　Haar Cascadeは画像の中にオブジェクトが存在するかどうかを確かめる，一連の比較チェックです．この一連の比較はフェーズに分けられます．後のフェーズは前のフェーズの完了の影響を受けます．コントラスト・チェックは複雑ではないですが，画像の中心がエッジ部分より垂直かどうかを調べることに似ている．
　早いステージで幅広い検査が行われ，あとのステージで小さい領域の検査が行われる．
Q: Haar Cascadesはどのように作られるか？
A: Haar Cascades は正解データ画像と非正解データ画像に用いて発生アルゴリズムを鍛えます. 例えば猫を含む数百の画像群（猫にマーカをつけた）と猫を含まない数百の画像群を使って発生アルゴリズムを鍛えます．この発生アルゴリズムは結果として猫分類器を作ります．

```python

```

<br>

## ●RGBからLABへの画素変換

　`image`クラスのスタティック・メソッドでRGB値をLAB値に変換する．
引数でタプルを与えると，変換されたタプルが返ってくる．

```python
import image

lab = image.rgb_to_lab((200,200,100))
print(lab)
```

## 参考文献

https://wiki.sipeed.com/soft/maixpy/en/index.html