Quantifier la quantité d'écriture manuscrite d'une diapositive

Aug 28 2020

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

2 AlexAlex Aug 29 2020 at 14:57

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:

2 HansMusgrave Aug 28 2020 at 08:49

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:

  1. C'est une mesure de distance réelle.
  2. C'est un calcul très bon marché.
  3. 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).
  4. 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)