[PYTHON] Post-it anhand von Whiteboard-Bildern erkennen

Guten Abend. mixi Group Adventskalender 2015 Dies ist der Artikel am 14. Tag! ~~ Eigentlich habe ich mich auch bei [Diverse Adventskalender 2015] registriert (http://qiita.com/advent-calendar/2015/diverse). ~~ Es schien, dass eine doppelte Registrierung nicht möglich war m (_ _) m Vielen Dank.

Hintergrund

Verwenden Sie plötzlich alle Whiteboards? In der Mixy Group haben einige Abteilungen Scrum in ihre Entwicklungsmethoden integriert, und in vielen Fällen spielt Whiteboard + Post-it eine Rolle bei der Aufgabenverwaltung. Es mag für IT-Unternehmen im IT-Zeitalter unangenehm sein, analoge Methoden für das Aufgabenmanagement zu verwenden, wenn auch teilweise, aber es ist wichtig, die Vorteile von digital und analog gut zu nutzen.

Übrigens bin ich auch eine Person, die für ein solches IT-Unternehmen arbeitet, also habe ich natürlich ein Whiteboard zu Hause (↓ Abbildung des Whiteboards zu Hause). 我が家のホワイトボードの図 (Es war schmutzig, also habe ich mein Bestes versucht, es zu reinigen)

Es ist sehr schön, jeden Morgen aufwachen zu können und verschiedene Dinge gleichzeitig zu überprüfen, indem ich die Aufgabe auf Post-it schreibe und einfüge, aber wenn ich ausgehe, vergesse ich oft, was die Aufgabe war.

Dieses Mal habe ich versucht, ein Bild des Whiteboards zu machen und Post-it digital zu verwalten, um den Wunsch zu verwirklichen, "das Whiteboard auch dann zu überprüfen, wenn ich unterwegs bin!", Also werde ich den Pfad vorstellen. (Wenn es einen besseren Weg gibt! Ich würde es begrüßen, wenn Sie darauf hinweisen könnten!)

Umgebung: Python 3.4.3 (pyenv), OpenCV 3.0.0

fließen

OpenCV-Installation

In der Pyenv-Umgebung können Sie es einfach installieren, wenn Sie Anaconda installiert haben. conda install -c https://conda.binstar.org/menpo opencv

Dieses Mal wollte ich es mit pyenv in der vorhandenen Umgebung verwenden, also habe ich es mit Homebrew installiert.

brew tap homebrew/science
brew install opencv3 --with-python3
brew link opencv3 --force

Sie müssen einen Link erstellen, um OpenCV in Python 3 in der Pyenv-Umgebung verfügbar zu machen.

ln -s /usr/local/Cellar/opencv3/3.0.0/lib/python3.4/site-packages/cv2.so ~/[pyenv_path]/versions/3.4.3/lib/python3.4/site-packages/cv2.so 

Wenn Sie cv2 in eine Interpreter-Umgebung wie ipython importieren können, sind Sie fertig.

$ ipython
In [1]: import cv2
In [2]: cv2.__version__
Out[2]: '3.0.0'

Konturerkennung durch Bildbinarisierung

Da das Bild mit OpenCV binärisiert wird und nur der Post-It-Teil daraus extrahiert wird, wird der Gliederungsteil des Post-It erfasst. Diesmal habe ich dieses Foto von meinem Whiteboard verwendet!

Example

Zunächst der Teil bis zur Binarisierung.

image_dir = './image/'
image_file = 'xxx.jpg'
im = cv2.imread(image_dir + image_file, 1) #(A)
im_gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY) #(B)
im_blur = cv2.GaussianBlur(im_gray, (11, 11), 0) #(C)

Das Bild wird in (A) in 3 Farbkanälen gelesen, es ist jedoch erforderlich, es in ein Graustufenbild umzuwandeln, um eine Binärisierung durchzuführen, und dies erfolgt in (B). Das zweite Argument von (B) ist eine Konstante, und verschiedene andere Konvertierungen wie BGR2HSV können mit cvtColor durchgeführt werden. In (C) wird eine Gaußsche Unschärfe angewendet, um die Bestimmung des Schwellenwerts des Graustufenbilds zu erleichtern.

Das vorverarbeitete Bild wird binärisiert. Die Binarisierung ersetzt ein Bild in Graustufen durch (im Grunde genommen) zwei Farben, 0 und 255. Die Schwellenwertfunktion wird hier verwendet, aber es gibt die Schwellenwertfunktion, die den Schwellenwert für das Ganze festlegt, und die adaptive Schwellenwertfunktion, die den Schwellenwert adaptiv entsprechend dem Teil festlegt.

ret1, th1 = cv2.threshold(im_blur, 127, 255, cv2.THRESH_BINARY_INV)
th2 = cv2.adaptiveThreshold(im_blur, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 3)

Der Schwellenwert verwendet das zweite Argument als Schwellenwert und digitalisiert das gesamte Bild. Das 5. Argument von adaptiveThreshold ist der Bereich, der beim Ermitteln des Schwellenwerts angezeigt wird, und der letzte Schwellenwert ist der aus diesem Bereich berechnete Schwellenwert abzüglich des 6. Arguments. Es gibt zwei Möglichkeiten, den Schwellenwert innerhalb eines Bereichs zu berechnen: cv2.ADAPTIVE_THRESH_MEAN_C, bei dem einfach die Pixel im Bereich gemittelt werden, und cv2.ADAPTIVE_THRESH_GAUSSIAN_C, bei dem Gauß gewichtet und gemittelt wird.

Das 4. Argument der Schwelle und das 3. Argument der adaptiven Schwelle nehmen verschiedene andere Konstanten an. Weitere Informationen finden Sie in der Referenzliste.

Die folgende Abbildung zeigt die Anwendung auf ein geeignetes Bild.

example_threshold

Da ich dieses Mal den Post-It-Teil erkennen möchte, habe ich den OTSU-Algorithmus, einen Algorithmus, der den Schwellenwert gut bestimmt, als den Schwellenwert verwendet, der den Schwellenwert als Ganzes bestimmt. Es ist sehr einfach zu bedienen, fügen Sie einfach ein wenig zur vorherigen Funktion hinzu.

th = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)[1]

Einfach ausgedrückt ist der OTSU-Algorithmus (Otsu-Algorithmus) nicht besonders schwierig und wird allgemein als Diskriminanzanalyseverfahren, Fisher's lineare Diskriminante usw. bezeichnet. Wenn es zwei Klassen gibt (hier den Hintergrund des Bildes und des Objekts usw.), ist (1) die Varianz innerhalb jeder Klasse gering und (2) die Varianz zwischen den Klassen groß. Wir suchen nach einer Schwelle, die erreicht wird. Daher muss kein Schwellenwert von Personen festgelegt werden. Wenn Sie nur das Bild übergeben, wird der optimale Schwellenwert automatisch berechnet. Wenn Sie weitere Details erfahren möchten, wenden Sie sich bitte an "Diskriminierungsanalysemethode".

Wenn ich dies auf das erste Foto anwende, das ich aufgenommen habe, sieht es so aus:

example_try

** Ich kann es überhaupt nicht bekommen! !! !! !! !! ** **. Es scheint nur, dass die Schmutzigkeit der Buchstaben auffällt. Ich konnte es nicht einfach durch Graustufen erhalten, also habe ich dieses Mal die Summe der folgenden fünf binärisierten Bilder als endgültiges binärisiertes Bild verwendet.

  1. Binarisiertes Originalbild mit cvtColor
  2. Ein eindimensionales Graustufenbild der Werte jedes der drei ursprünglichen Farbkanäle
  3. Die Summe des Rotes der drei Farbkanäle abzüglich des anderen Blaus und Grüns (erstellt, um das Rot zu erzwingen, da es schwierig war, die folgenden Werte unter Verwendung von im als Originalbild zu entfernen).
(np.abs(int_im[:,:,2] - int_im[:,:,1]) + np.abs(int_im[:,:,2] - int_im[:,:,0]))

Hier sind die Bilder, die erstellt wurden, indem 5 Arten von binärisierten Bildern daraus erstellt und die Summe aller Bilder genommen wurden!

example_success

Es wird so.

Als nächstes möchte ich die Koordinaten der Kontur abrufen, aber da OpenCV bereits über eine findContours-Funktion zum Extrahieren der Kontur verfügt, werde ich sie verwenden.

contours = cv2.findContours(im_th, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[1]

Dies ermöglicht es, Informationen über das Konturteil zu erhalten.

Holen Sie sich den Post-It-Teil

Danach wird basierend auf dem Scheitelpunkt der erfassten Kontur nur der Teil extrahiert, dessen Fläche größer als eine bestimmte Größe ist. Die Funktion approxPolyDP führt eine lineare Approximation durch, und der erhaltene quadratische Teil wird aus dem Originalbild ausgeschnitten und gespeichert.

# filtered with area over (all area / 100 )
th_area = im.shape[0] * im.shape[1] / 100
contours_large = list(filter(lambda c:cv2.contourArea(c) > th_area, contours))

outputs = []
rects = []
approxes = []

for (i,cnt) in enumerate(contours_large):
	arclen = cv2.arcLength(cnt, True)
	approx = cv2.approxPolyDP(cnt, 0.02*arclen, True)
	if len(approx) < 4:
		continue
	approxes.append(approx)
	rect = getRectByPoints(approx)
	rects.append(rect)
	outputs.append(getPartImageByRect(rect))
	cv2.imwrite('./out/output'+str(i)+'.jpg', getPartImageByRect(rect))

Hier haben wir als getPartImageByRect und getPartImageByRect eine Funktion vorbereitet, um das Bild zu erhalten, das durch Ausschneiden des angegebenen Bereichs aus dem folgenden Bild erhalten wird.

def getRectByPoints(points):
	# prepare simple array 
	points = list(map(lambda x: x[0], points))

	points = sorted(points, key=lambda x:x[1])
	top_points = sorted(points[:2], key=lambda x:x[0])
	bottom_points = sorted(points[2:4], key=lambda x:x[0])
	points = top_points + bottom_points
	
	left = min(points[0][0], points[2][0])
	right = max(points[1][0], points[3][0])
	top = min(points[0][1], points[1][1])
	bottom = max(points[2][1], points[3][1])
	return (top, bottom, left, right)

def getPartImageByRect(rect):
	img = cv2.imread(image_dir + image_file, 1)
	return img[rect[0]:rect[1], rect[2]:rect[3]]

Jetzt können Sie den Post-It-Teil aus dem Originalbild ausschneiden. Das folgende Bild ist das, in das ich einen Rahmen in das Originalbild eingefügt habe.

元画像with黒枠

Alle Post-It können sicher erkannt werden. Wenn Sie danach jedes Image in der Anwendung haben oder es auf dem Server verwalten, können Sie den Weg zur digitalen Verwaltung öffnen.

Clustering mit Post-It-Farben

Ich denke, wenn Sie Post-it verwenden, kategorisieren Sie Ihre Aufgaben häufig nach Farben. Es ist eine große Sache, also lasst uns das auch schaffen. Betrachten Sie den folgenden Ablauf.

  1. Erfassung der repräsentativen Farbe
  2. Clustering
  3. Rahmen Sie die durchschnittliche Farbe desselben Clusters wie die Farbe dieser Kategorie ein

Holen Sie sich zunächst die repräsentativen Farben jedes zugeschnittenen Bildes. Derzeit wird nur der Post-It-Teil ausgeschnitten, sodass angenommen wird, dass dieses Bild drei Farben enthält: Umgebungsweiß, Post-It-Farbe und Textfarbe Schwarz. Mit anderen Worten, wenn der Medianwert jedes Farbkanals leicht als repräsentative Farbe verwendet werden kann, wird wahrscheinlich die Post-it-Farbe erhalten.

t_colors = []
for (i,out) in enumerate(outputs):
	color = np.zeros(3)
	for j in range(3):
		color[j] = np.median(out[:,:,j])
	t_colors.append(color)
t_colors = np.array(t_colors)

Als nächstes wurden die repräsentativen Werte jedes der erhaltenen Post-its unter Verwendung der KMeans-Methode geclustert. In Bezug auf das Clustering nach der KMeans-Methode gibt es im Internet viele leicht verständliche Erklärungen wie Qiita. Bitte beziehen Sie sich darauf.

from sklearn.cluster import KMeans
# KMeans
cluster_num = 4 # num of colors
kmeans = KMeans(n_clusters=cluster_num).fit(t_colors)
labels = kmeans.labels_
centers = np.array(kmeans.cluster_centers_).astype(np.int) # convert into int to express color

Hier ist die Farbe (und Nummer) des Cluster-Ergebnisses, das dem Originalbild hinzugefügt wurde! Es ist ziemlich schwer zu erkennen, aber ich habe in der oberen linken Ecke von Postit eine 0 bis 3-Cluster-Ergebnisbezeichnung hinzugefügt.

clustering

Wenn man sich die Ergebnisse ansieht, sind Blau und Rot gut gruppiert, aber die Klasse ist eine Mischung aus Gelb und Gelbgrün, wahrscheinlich wegen des Lichts. Dies kann sich erneut ändern, wenn Sie in HSV konvertieren und Sättigungsinformationen einschließen, da wir nur die Elemente jedes RGB-Kanals des Bildes für das Clustering verwendet haben, aber das war's für diese Zeit.

von jetzt an

In diesem Beispiel habe ich den Betrieb meines Whiteboards nur zu Hause bestätigt, daher muss ich es breiter nutzbar machen. Darüber hinaus ist das Clustering ebenfalls unzureichend, und es ist derzeit nicht möglich, zu reagieren, wenn die Anzahl der Farben zunimmt. Außerdem scheint es dumm zu sein, jeden Morgen selbst Fotos zu machen, also möchte ich den stolzen Raspberry Pi ziehen und alles automatisieren, also schreibe ich es als Fortsetzung.

Zusammenfassung

Als ich versuchte, einen Artikel über die Einführung von Whiteboards zu Hause zu schreiben, konnte ich ihn nicht wirklich anfassen. Lassen Sie uns zu Hause eine weiße Tafel vorstellen. Sie können Aufgaben verwalten, berechnen und Notizen schreiben. Sie können viel Fortschritt machen.

Morgen wird @isaoshimizu etwas schreiben. Vielen Dank.

Referenz

Erstellen Sie eine Umgebung für Python 3.4 + OpenCV 3.0 auf Ihrem Mac https://librabuch.jp/2015/07/python-34_opencv-30_mac/

Konturextraktion mit OpenCV http://docs.opencv.org/master/d4/d73/tutorial_py_contours_begin.html#gsc.tab=0

cv2.threshold (OpenCV 2.1) http://opencv.jp/opencv-2.1/cpp/miscellaneous_image_transformations.html#cv-threshold

cv2.adaptiveThreshold (OpenCV 2.1) http://opencv.jp/opencv-2.1/cpp/miscellaneous_image_transformations.html#cv-adaptivethreshold

cv2.findContours (OpenCV 2.1) http://opencv.jp/opencv-2.1/cpp/structural_analysis_and_shape_descriptors.html#cv-findcontours

Recommended Posts

Post-it anhand von Whiteboard-Bildern erkennen
Sofort von Google-Bildern abkratzen!