Help us understand the problem. What is going on with this article?

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

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

●画像オブジェクト生成

image.Image() :画像オブジェクトを生成できる.
240×135の画像が生成される.
image.Image(filename) :ファイル名で指定した画像のオブジェクトを生成する.ビットマップやJPEGファイルに対応する.

import image

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


●画像の切り出し&コピー

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

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


●画像を保存

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


●画像による位置推定

 画像を元にカメラの位置変化を推定できます.
 急激に画像が変化すると,追従できません.
 フレーム・レートとの調整が必要です.
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を使えと書いてある.

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()) 


●画像の中から線を探す

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

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を出力


●画像の中から円を探す

 円が見つからなければ,空のリストを返す.
円が見つかれば,circleオブジェクトのリストが返される.つまり複数の円を見つけられる.
circleオブジェクトは,x1(センター), y1(センター), 半径,magnitudeを持つ.
 次のプログラムで6fps(画像複雑)~11fps(画像真っ黒)程度で動く.
set_windowing((128, 64))の設定で,12(複雑)~16fps(黒)程度.

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)


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

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

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


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

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

import sensor, image

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


●ドット打つ

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

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)


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

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

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


●文字を描画

メソッド・シグネチャ
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オブジェクトを返す.

#省略
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行上と全く同じ


●十字を描画

メソッド・シグネチャ
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オブジェクト.

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


●画像を描画

メソッド・シグネチャ
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) マスク用白黒画像


●和演算(image.add)

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

  • add_image(type image) :白黒画像
  • mask


●差演算(image.sub)

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

  • sub_image(type image) :白黒画像
  • reverse(bool) :重ねる白黒画像を白黒反転
  • mask


●乗演算(image.mul)

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

  • mul_image
  • invert
  • mask
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)


●徐演算

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


●2つの画像の最小値

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

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

  • `image_min(type image)`
  • mask(type image)


●2つの画像の最大値

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

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

  • image_max(type image)
  • mask(type image)


●差演算(image.difference)

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

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

  • diff_image
  • mask 白黒のマスク画像


●ブレンド(image.blend)

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

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

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


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

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

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

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


●ブラー・フィルタ(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 画像と同じサイズの白黒マスク画像


●フィルタ(image.mode)

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

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

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


●フィルタ(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 白黒のマスク画像


●ガウシアン・フィルタ

メソッド・シグネチャ
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) マスク画像

使用例

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


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

メソッド・シグネチャ
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)


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

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)
img.bilateral(1, color_sigma=0.1, space_sigma=3.1, threshold=False, offset=1, invert=False)


●近似色領域を囲む線を描画

 圧縮画像,ベイヤー画像はサポートしない.

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

  • size 
  • threshold 線描画の閾値,大きいほど描画しない.省略した場合は標準的な動作
  • mask(type image)


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

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

  • size
  • threshold
  • mask


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

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

image image.invert()


●画像を反転(image.negate)

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

image image.negate()


●縁を強調

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

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


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

メソッド・シグネチャ
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)


●画素を反転

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

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


●矩径を描画

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

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ピクセル


●画像を縮小

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

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)


●画像サイズを取得

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

bytes = img.size()
print(bytes, "bytes")


●ピクセル・クリア

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

img.clear()


●矢印を描画

 引数は,x1, y1, x2, y2, 色,太さ

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


●カートゥーン変換

 2つの引数はどちらも指定範囲は0~1
第1引数 Seed threshold: 塗りつぶしの閾値.元になるピクセルとそのピクセルとの差
第2引数 Floating threshold: どれくらい(距離)まで塗りつぶすか?

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


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

 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の画像のみがサポートされる.



●カスケード・オブジェクトを作る
 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 は正解データ画像と非正解データ画像に用いて発生アルゴリズムを鍛えます. 例えば猫を含む数百の画像群(猫にマーカをつけた)と猫を含まない数百の画像群を使って発生アルゴリズムを鍛えます.この発生アルゴリズムは結果として猫分類器を作ります.


●RGBからLABへの画素変換

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

import image

lab = image.rgb_to_lab((200,200,100))
print(lab)
Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away