Quantifier la quantité d'écriture manuscrite d'une diapositive
J'ai une vidéo d'un diaporama, où le présentateur écrit des notes sur la diapositive:
Je voudrais créer un programme qui détecte si une diapositive est en cours de remplissage (par des notes manuscrites par exemple) ou s'il s'agit d'une nouvelle diapositive.
Une méthode à laquelle je pensais est l'OCR du texte, mais cela ne convient pas car ici, le seul texte qui change est soit manuscrit, soit mathématique.
Ce que j'ai fait jusqu'à présent: je parcours la vidéo et je compare toujours l'image précédente et l'image actuelle. J'extrait les coordonnées de la boîte englobante de tous les éléments qui ont été ajoutés par rapport à l'image précédente et je stocke la coordonnée y la plus élevée. La coordonnée y la plus élevée appartient à l'élément le plus éloigné de l'image (vu du haut de l'image). Ainsi cela devrait -en théorie- me donner une indication si je remplis la diapositive ...
En pratique, je ne peux pas vraiment utiliser ces données:
La vidéo en question peut être téléchargée ici: http://www.filedropper.com/00_6
Voici mon code:
from skimage.measure import compare_ssim
import cv2
import numpy as np
# Packages for live plot visualisation
import pyqtgraph as pg
from pyqtgraph.Qt import QtGui, QtCore
from tqdm import tqdm
def get_y_corrd_of_lowest_added_element(prev_frame, frame):
"""
Given Two Images it detects the bounding boxes of all elemnts that
are different betweent the two images and outputs the y coordinate of
the lowest added element (when seen from the top of the image)
Parameters
----------
prev_frame : numpy array
original image.
frame : numpy array
new image, based on original image.
Returns
-------
TYPE
lowest y coordinate of elments that were added.
"""
# Compute SSIM between two images
(score, diff) = compare_ssim(prev_frame, frame, full=True)
# The diff image contains the actual image differences between the two images
# and is represented as a floating point data type in the range [0,1]
# so we must convert the array to 8-bit unsigned integers in the range
# [0,255] before we can use it with OpenCV
diff = (diff * 255).astype("uint8")
# Threshold the difference image, followed by finding contours to
# obtain the regions of the two input images that differ
thresh = cv2.threshold(diff, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
contours = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
# Initialize a list that will hold all y coordinates of all bounding boxes
# of all elements that were added to the frame when compared to the
# previous frame
y_list = [0]
for c in contours:
area = cv2.contourArea(c)
if area > 40:
x,y,w,h = cv2.boundingRect(c)
# Append to y coordinate list
y_list.append(y)
y_list.sort()
return y_list[-1]
def transform(frame):
# convert to greyscale
frame = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
# make smaller
small = cv2.resize(frame, (0,0), fx=0.5, fy=0.5)
return small
vidcap = cv2.VideoCapture(ADD PATH TO VIDEO HERE)
success,prev_frame = vidcap.read()
prev_frame = transform(prev_frame)
# For Real Time Ploting
#Source: http://www.pyqtgraph.org/downloads/0.10.0/pyqtgraph-0.10.0-deb/pyqtgraph-0.10.0/examples/PlotSpeedTest.py
app = QtGui.QApplication([])
win = pg.GraphicsWindow()
win.resize(800, 800)
p = win.addPlot()
p.setTitle('Lowest Y')
plot = p.plot([])
# Store lowest y coordinates of added elements
y_lowest_list = []
while success:
success,frame = vidcap.read()
# convert
frame = transform(frame)
# show frame
cv2.imshow("frame", frame)
cv2.waitKey(1)
#extract lowest y corrd
y = get_y_corrd_of_lowest_added_element(prev_frame, frame)
y_lowest_list.append(y)
# Real-time plot
plot.setData(y_lowest_list)
# close real-time plot
win.close()
est-ce que quelqu'un a une idée?
Réponses
Vous pouvez essayer ce code, voir les commentaires:
import cv2
import numpy as np
def get_bg_and_ink_level(frame):
frame = cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)
background=cv2.threshold(frame[:,:,2], 245, 255, cv2.THRESH_BINARY)[1]
background_level=cv2.mean(background) # for future use if you need to select frames without hands.
ink_color_low = (117,60,150)
ink_color_high = (130,207,225)
only_ink = cv2.inRange(frame, ink_color_low, ink_color_high)
ink_level=cv2.mean(only_ink)
return background_level[0], ink_level[0]
vidcap = cv2.VideoCapture('0_0.mp4')
success,frame = vidcap.read()
bg = []
ink=[]
i=0
while success:
lv= get_bg_and_ink_level(frame)
bg.append(lv[0])
ink.append(lv[1])
success,frame = vidcap.read()
# search for frames where the blue ink is removed from the picture.
d_ink=np.diff(ink)
d_ink[-1]=-2.0 #add last frame
idx=np.where(d_ink<-1.0)
#save frames
for i in idx[0]:
vidcap.set(cv2.CAP_PROP_POS_FRAMES, i)
flag, frame = vidcap.read()
out_name='frame'+str(i)+'.jpg'
cv2.imwrite(out_name, frame)
Résultat 15708 cadre:
Pour résoudre le problème, je voudrais probablement simplement compter le nombre de pixels différents entre les deux images. Il possède plusieurs propriétés souhaitables:
- C'est une mesure de distance réelle.
- C'est un calcul très bon marché.
- Les diapositives avec plus d'écriture manuscrite sont plus éloignées de l'original que les diapositives avec peu d'écriture (par exemple, si vous avez progressivement ajouté plus d'écriture et que vous vouliez les commander).
- S'il y a même une quantité modérée de contenu sur les diapositives, il est plausible (pas nécessairement) que deux diapositives non liées soient plus éloignées l'une de l'autre que deux diapositives qui sont identiques mais qui diffèrent simplement par l'écriture manuscrite (en particulier avec une écriture fine comme celle-ci. ).
Ce n'est pas une solution parfaite bien sûr - par exemple, si vous acquérez les diapositives en prenant des photos, presque chaque diapositive différera à chaque pixel. Prenez un moment pour y réfléchir en fonction de votre cas d'utilisation et des méthodes de collecte de données.
Il est assez courant que les images en python soient représentées sous forme de tableaux numpy. En supposant que ce soit le cas pour vous également, l'exemple suivant calculerait la métrique en question (ou pourrait être facilement modifié pour vous donner une similitude plutôt qu'une distance):
def dist(a, b):
# Supposes some sort of pixel representation like bgr or hsl with
# shape (w, h, other) or (h, w, other)
return np.sum(np.sum(a!=b, axis=-1)!=0)