質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

2067閲覧

Python ラベリングした画像の平行移動

tomo754

総合スコア11

OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/10/29 06:30

前提・実現したいこと

初心者&独学ですが...
Python tkinterを使って画像ビュワーを製作しております。
この時、画像にあるように画像内の白点をラベリングします。実際は2000×2000pixelの画像を適度に縮小させてCanvasに表示させており
現在表示させている範囲以外の見えていないエリアをマウスドラッグし平行移動させて表示させたいです。
Canvasに表示させる為、OpenCVで処理した画像をPillow に変換しアフィン変換しているせいか下記エラーが発生し上手く行きません。
シンプルに画像の移動を行う事は出来るのでしょうか?

申し訳ありませんが、ご教授下さい!
イメージ説明

発生している問題・エラーメッセージ

drag_img = cv2.warpAffine(image, M, (w, h)) cv2.error: OpenCV(4.5.4-dev) :-1: error: (-5:Bad argument) in function 'warpAffine' > Overload resolution failed: > - src is not a numpy array, neither a scalar > - Expected Ptr<cv::UMat> for argument 'src'

該当のソースコード

python

1#ライブラリを取り込む 2import tkinter as tk 3from tkinter import filedialog 4import cv2 5from PIL import Image, ImageTk 6import os 7import numpy as np 8 9 10# coding: utf-8 11 12img = None #初期化 13filepath = None #初期化 14 15#ウインドウを作成 16root = tk.Tk() 17root.title("XXXXXX")#タイトル 18root.geometry("800x800")#サイズ 19 20#パーツを配置 21#ラベル1を作成 22label1 = tk.Label(text='■画像読込') 23label1.place(x=10, y=10) 24 25#ラベル2を作成 26label2 = tk.Label(text='ファイル名:') 27label2.place(x=10, y=40) 28 29#ファイルパスの表示欄を作成 30input_box1 = tk.Entry(width=75, bd=4) 31input_box1.place(x=80, y=40) 32 33 34 35#参照ボタンの動作 36def file_select(): 37 global filepath #グローバル宣言 38 39 #ファイルパスを表示欄に表示 40 idir = r'C:\descktop' 41 filetype = [("すべて","*")] 42 filepath = tk.filedialog.askopenfilename(filetypes = filetype, initialdir = idir) 43 input_box1.delete(0, tk.END) 44 input_box1.insert(0, filepath) 45 46 #選択したファイルを表示 47 if filepath and os.path.isfile(filepath): 48 49 global img #グローバル宣言 50 global image_info 51 global image 52 global h, w 53 54 img = cv2.imread(filepath) 55 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)#グレースケールに変換 56 bin = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]#二値化 57 label = cv2.connectedComponentsWithStats(bin)#ラベリング処理 58 59 # ラベリング結果書き出し用に二値画像をカラー変換 60 color_src = cv2.cvtColor(bin, cv2.COLOR_GRAY2BGR) 61 62 63 64 65 #ブロブ情報を項目別に抽出 66 n = label[0] - 1 67 data = np.delete(label[2], 0, 0) 68 center = np.delete(label[3], 0, 0) 69 70 #オブジェクト情報を利用してラベリング結果を画面に表示 71 for i in range(n): 72 73 #各オブジェクトの外接矩形を赤枠で表示 74 x0 = data[i][0] 75 y0 = data[i][1] 76 x1 = data[i][0] + data[i][2] 77 y1 = data[i][1] + data[i][3] 78 cv2.rectangle(color_src, (x0, y0), (x1, y1), (0, 255, 255), thickness= 2) 79 80 81 #各オブジェクトのラベル番号を黄文字で表示 82 cv2.putText(color_src, "ID: " +str(i + 1), (x1 - 30, y1 - 30), cv2.FONT_HERSHEY_PLAIN, 2, (0, 255, 255)) 83 84 85 #各オブジェクトの重心座標をに黄文字で表示 86 cv2.putText(color_src, "X: " + str(int(center[i][0])), (x1 - 30, y1 + 20), cv2.FONT_HERSHEY_PLAIN, 2, (0, 255, 255)) 87 cv2.putText(color_src, "Y: " + str(int(center[i][1])), (x1 - 30, y1 + 50), cv2.FONT_HERSHEY_PLAIN, 2, (0, 255, 255)) 88 89 #結果の表示 90 #cv2.imshow("color_src", color_src01) 91 img_pil = Image.fromarray(color_src)#PILへ変換 92 #img_tk = ImageTk.PhotoImage(img_pil)#ImageTkへ変換 93 94 #画像の情報を取得 95 w, h = img_pil.size 96 97 #画像リサイズ 98 image = ImageTk.PhotoImage(img_pil.resize((int(h/2), int(w/2)))) 99 100 #表示位置調整 101 canvas.image = image 102 canvas.create_image(image.width()/2, image.height()/2, image=image) 103 104 105 106 # ラベルの個数nだけ色を用意 107 print("ブロブの個数:", n) 108 #print("各ブロブの外接矩形の左上x座標", data[:,0]) 109 #print("各ブロブの外接矩形の左上y座標", data[:,1]) 110 #print("各ブロブの外接矩形の幅", data[:,2]) 111 #print("各ブロブの外接矩形の高さ", data[:,3]) 112 #print("各ブロブの面積", data[:,4]) 113 #print("各ブロブの中心座標:\n",center) 114 115 canvas.bind("<Button-1>", click) 116 canvas.bind('<B1-Motion>', drag) 117 118def click(event): 119 global posx 120 global posy 121 122 posx = event.x 123 posy = event.y 124 print("posx", posx) 125 print("posy", posy) 126 127def drag(event): 128 129 movex = event.x 130 movey = event.y 131 dx = movex - posx 132 dy = movey - posy 133 print("movex", dx) 134 print("movey", dy) 135 136 M = np.float32([[1, 0, dx], [0, 1, dy]]) 137 print(M) 138 drag_img = cv2.warpAffine(image, M, (w, h)) 139 140 # 表示位置調整 141 canvas.image = drag_img 142 canvas.create_image(drag_img.width() / 2, drag_img.height() / 2, image=drag_img) 143 144 145 146 147 148 149 150 151#Canvasの作成(メイン画像) 152canvas = tk.Canvas(root, width=500, height=500, bg="white",) 153canvas.place(x=10, y=70) 154 155#参照ボタン1を作成 156button1 = tk.Button(text="参照", command=file_select, width=8, bd=4) 157button1.place(x=630, y=35) 158 159#閉じるボタンを作成 160button2 = tk.Button(text="閉じる", command=root.destroy, width=8, bd=4) 161button2.place(x=730, y=35) 162 163 164 165#GUIをそのまま表示 166root.mainloop() 167 168 169

試したこと

Pillow -> NumPyへ変換してみましたが、別Errorになりました。

補足情報(FW/ツールのバージョンなど)

Python       3.6.1
opencv-python 4.5.3.56
Pillow 8.3.2

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

問題点: PIL.Image, cv2.UMat, numpy array, PhotoImage の混同

エラー内容は、cv2 で扱えるデータ型 を期待するところに PhotoImage のオブジェクトを渡しています。
変数 image, img_pil, color_src の型をそれぞれ確認してください。

変数 image の PhotoImage オブジェクトはキャンバスへの描画に必要なデータです。
cv2 及び numpy や PIL での操作には直接使えません。元の画像データが必要です。

Pillow -> NumPyへ変換してみましたが、別Errorになりました

こちらの方が解決に近そうです。
何処のデータを変換しましたか?また、別エラーの内容は何でしょう?

cv2.warpAffine の結果から再度 PILの画像に変換し、
この場合であれば、Motionイベント内で PhotoImage オブジェクトを毎回生成する必要があります。


他の問題点1

Motion イベント内で create_image を複数回呼ぶ場合は、
canvas.delete がないとメモリーリークになります。

PhotoImage オブジェクト自体は、デストラクタにより破棄されますが、
キャンバス内のcreate_imageで生成したアイテムは残り続けます。

他の問題点2

結果の表示がforループ内にあるので、見た目には解りませんが、表示が重なってます。
インデントを確認して見て下さい。ループ毎ではなく、ループ後が意図した位置ではないでしょうか。


他の解決策

キャンバス範囲の画像データを切り出しているようですが、
(解像度次第ですが)2000x2000であれば、画像をそのまま載せて、ドラッグに応じて表示範囲のみを
スクロールさせる方法を取ってみてはどうでしょう。画像処理は読み込み時の一度で済みます。

大元のデータがより大規模で、一部分のみ表示したいとなると変わってきますが、
可能であれば、別の実装方法も検討して見て下さい。

関連: https://teratail.com/questions/54103

投稿2021/10/29 09:07

編集2021/10/29 09:09
teamikl

総合スコア8664

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tomo754

2021/10/29 09:46

teamiklさん 詳しく説明を記載して頂き有難う御座います。 他の問題点も指摘して頂き助かります。 一度、これを参考にコードの修正と再検討をしてみたいと思います。
tomo754

2021/11/01 04:02

申し訳ありませんが、追加で質問です。 ********************************* def drag(event): canvas.delete('image') movex = event.x movey = event.y dx = movex - posx dy = movey - posy M = np.float32([[1, 0, dx], [0, 1, dy]]) drag_img = cv2.warpAffine(color_src, M, (w, h)) img_move = Image.fromarray(drag_img) #画像の情報を取得 w1, h1 = img_move.size #画像リサイズ image = ImageTk.PhotoImage(img_move.resize((int(h1 / 2), int(w1 / 2)))) #表示位置調整 canvas.image = image canvas.create_image(image.width()/2, image.height()/2, image=image) 上記のようにする事で画像のマウスドラッグでの移動は可能となりました。 しかし、「canvas.create_image(image.width()/2, image.height()/2, image=image)」で描写させている為、移動前の位置に戻ってしまいます。 移動後の位置を保持して、継続した画像の移動をさせる方法は御座いますでしょうか?
teamikl

2021/11/01 05:14

通常であれば、move/moveto/coords メソッドで座標のみ移動させるのですが、 delete/create 方式の場合であれば、<ButtonRelease-1> イベントで前回の座標を記録しておき、 次の移動時に補正として加える感じでしょうか。 ==== 他の問題: canvas.delete("image") では意図通りの削除になりません。 以下の何れかの方法で削除します - canvas.delete("all") すべて削除 - canvas.delete(canvas.image) - create_image 時に "image" タグを指定
teamikl

2021/11/03 04:30

回答の最後で触れた別の実装方法で(関連URL参照) > 現在表示させている範囲以外の見えていないエリアをマウスドラッグし平行移動させて表示 要件に合うか解りませんが、以下のscan_mark/scan_dragto を試してみてください。 ## キャンバス内をドラッグして表示範囲のみスクロールさせる方法 import tkinter as tk root = tk.Tk() canvas = tk.Canvas(root) canvas.pack() image = tk.PhotoImage(root, file="PNG画像ファイルのパス") canvas.create_image(0, 0, image=image, anchor=tk.NW) canvas.bind("<ButtonPress-1>", lambda e: canvas.scan_mark(e.x, e.y)) canvas.bind("<B1-Motion>", lambda e: canvas.scan_dragto(e.x, e.y, gain=1)) root.mainloop() ※ tk.PhotoImage はサンプルコードの為pngファイルを想定。 必要に応じて、PIL.ImageTkのPhotoImageを使うように変更。
tomo754

2021/11/04 05:37

teamiklさん 追加でご親切に回答を下さり有難う御座います。 確かに頂いたコードだとイメージの動きをしています。 まだテストで動作を確認させて頂いただけなので、自身のコードに実装してみて試してみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問