Cómo no perder el paso del tiempo trabajando en la computadora. Aplicación para monitorear el trabajo y mantener estadísticas





Trabajo como profesora en el parque tecnológico infantil Quantorium. Durante el período de autoaislamiento, nosotros, como todos los demás, pasamos al aprendizaje a distancia. Y debido al hecho de que los niños comenzaron a pasar aún más tiempo frente a la computadora, la administración decidió acortar la hora académica y tomar descansos entre el trabajo para preservar la vista. Hemos escrito una aplicación que calcula el tiempo que se pasa frente a la computadora, mantiene las estadísticas en Excel, lo que es útil para los padres, y brinda una alerta sonora cuando es el momento de tomar un descanso.



La aplicación será útil para aquellos que están perdidos en el tiempo trabajando en la PC y quieren manejarse en el marco de tiempo o hacer un seguimiento de qué parte de la vida se pierde en el espacio digital.



Enlace al repositorio



Debajo del corte un análisis detallado.



Para crear un programa, debe resolver las siguientes tareas:



  • web ( mtcnn);
  • ( time)
  • excel ( openpyxl);
  • ;
  • .


web



Consideramos la opción de rastrear la presencia en la computadora mediante el movimiento del mouse de la computadora, tal vez sea más fácil y la cámara web se quede libre, pero luego no podremos tomar en cuenta el visionado de programas de TV o videos de youtube, y además, usar el reconocimiento facial es mucho más interesante.



Para la detección de rostros, utilizamos la arquitectura mtcnn. Una cabeza colocada en cualquier ángulo, con gafas, en condiciones de poca luz, incluso si la cara no cae completamente en el marco o está cubierta por una mano, la red neuronal está funcionando correctamente. No lo construiremos por nuestra cuenta, conectaremos la biblioteca del mismo nombre, lo que resolverá nuestro problema usando unas pocas líneas de código.



Originalmente se utilizó el método Viola-Jones. La precisión es mucho peor que la de mtcnn, el procesador carga lo mismo.











Tomemos la salida del resultado en una función separada, solo es necesaria para ver cómo funciona el reconocimiento.



import cv2
import os
from mtcnn.mtcnn import MTCNN


def show_face_frame():
    if faces:
        bounding_box = faces[0]['box']
        keypoints = faces[0]['keypoints']
        cv2.rectangle(img,
                      (bounding_box[0], bounding_box[1]),
                      (bounding_box[0] + bounding_box[2], 
                      bounding_box[1] + bounding_box[3]),
                      (0, 0, 255),
                      2)
        cv2.circle(img, (keypoints['left_eye']), 3, (0, 0, 255), 2)
        cv2.circle(img, (keypoints['right_eye']), 2, (0, 0, 255), 2)
        cv2.circle(img, (keypoints['nose']), 3, (0, 0, 255), 2)
        cv2.circle(img, (keypoints['mouth_left']), 3, (0, 0, 255), 2)
        cv2.circle(img, (keypoints['mouth_right']), 3, (0, 0, 255), 2)
    cv2.imshow('frame', img)

cap = cv2.VideoCapture(0)
detector = MTCNN()

while cap.isOpened():
        _, img = cap.read()
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        faces = detector.detect_faces(img)
        show_face_frame()
        cv2.waitKey(300)

cap.release()
cv2.destroyAllWindows()


Gran retraso cv2.waitKey (300) para no cargar el procesador. El procesamiento de 3-4 fotogramas por segundo carga el i3-8145U en un promedio del 15%.



Contando el tiempo



El programa no debe escribir en el registro en los siguientes casos:



  • se sentó frente a la computadora durante 5 segundos para hacer algo byrik;
  • Salí a servir café o estirar las piernas.


Para resolver estos problemas, el programa utiliza dos cronómetros time_here (cuenta atrás el tiempo cuando el rostro está en el marco) y time_not_here (cuando no hay rostro). Ambos cronómetros se reinician cuando se realiza una entrada en el registro. Además, time_not_here se restablece cada vez que aparece un rostro en el marco.



La variable min_time_here indica el tiempo mínimo pasado en la computadora, después del cual vale la pena escribir en el registro. La grabación se realizará después de que la persona se distraiga de la computadora por un tiempo mayor al especificado en min_time_not_here .



En el código siguiente, la grabación se realiza si ha pasado al menos 5 minutos en la computadora. El programa no tendrá en cuenta el hecho de que estoy distraído menos de 2 minutos.



import cv2
import time
import os
from mtcnn.mtcnn import MTCNN

cap = cv2.VideoCapture(0)
path = os.path.abspath(__file__)[:-11]  #      

here, not_here = 0, 0  # 
time_here, time_not_here = 0, 0  #  
switch = True  #        
min_time_here = 300  #     ,     ( ) 
min_time_not_here = 120  #          ( )

detector = MTCNN()

try:
    while cap.isOpened():
        _, img = cap.read()
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        faces = detector.detect_faces(img)
       show_face_frame()
       audio_message(path, here)

        if faces and switch:
            not_here = 0
            switch = False
            if here == 0:
                here = time.time()
                print('     ' + time.strftime("%H:%M:%S", time.localtime(here)))
        elif not faces and not switch:
            not_here = time.time()
            switch = True

        if not_here != 0:
            time_not_here = time.time() - not_here
        if here != 0 and not switch:
            time_here = time.time() - here

        if time_here > min_time_here and time_not_here > min_time_not_here:  #        min_time_here
            write_excel(path, here)
            print('    ' + time.strftime('%H:%M:%S', time.gmtime(time.time() - here)))
            time_here, time_not_here = 0, 0
            here, not_here = 0, 0
        elif time_not_here > min_time_not_here and  time_here < min_time_here:  #      min_time_here,    
            print('  ')
            time_here, time_not_here = 0, 0
            here, not_here = 0, 0

        cv2.waitKey(300)
except KeyboardInterrupt:
    if time_here > min_time_here:  #        min_time_here
        write_excel(path, here)
    cap.release()
    cv2.destroyAllWindows()


Si el usuario desea apagar la computadora, primero debe interrumpir el programa (ctrl + c) y se registrará el tiempo del trabajo actual. Para ello, se utiliza un controlador de excepciones.



Escribir datos en un documento de Excel



Para trabajar con Excel, la maravillosa biblioteca openpyxl ayudará, que lee y escribe datos simultáneamente.



Cada día, el programa crea una nueva hoja con la fecha actual.





En 1 hoja, se registra la información diaria, en qué día cuánto tiempo se pasó en la computadora.







La función write_excel abre el documento, escribe datos y lo cierra inmediatamente.



No puede modificar un documento a través de openpyxl si el usuario ya lo ha abierto. Se ha agregado una excepción para este caso.



def write_excel(path_excel, time_start):
    wb = openpyxl.load_workbook(path_excel + r'\dnevnik.xlsx')
    today = time.strftime("%d.%m.%Y", time.localtime(time.time()))
    if today not in wb.sheetnames:  #        ,   
        wb.create_sheet(title=today)
        sheet = wb[today]
        sheet.column_dimensions['A'].width = 20
        sheet.column_dimensions['B'].width = 20
        sheet.column_dimensions['C'].width = 20
        sheet.column_dimensions['D'].width = 31
        sheet['A1'] = '   '
        sheet['B1'] = '   '
        sheet['C1'] = ' '
        sheet['D1'] = '  :'
        sheet['D2'] = '    :'
        sheet = wb[today]
        row = 2
        all_time = 0
    else:  #      
        sheet = wb[today]
        row = sheet['E1'].value  #    excel 
        all_time = sheet['E2'].value  #        
        all_time = all_time.split(':')
        all_time = int(all_time[0]) * 3600 + int(all_time[1]) * 60 + int(all_time[2])  #    
        row = row + 2  #      

    sheet['A' + str(row)] = time.strftime("%H:%M:%S", time.localtime(time_start))  #      
    sheet['C' + str(row)] = time.strftime("%H:%M:%S", time.localtime(time.time()))  #   - 
    seconds = time.time() - time_start  #      
    sheet['B' + str(row)] = time.strftime('%H:%M:%S', time.gmtime(seconds))
    sheet['E1'] = row - 1  #   
    all_time = all_time + seconds
    all_time = time.strftime('%H:%M:%S', time.gmtime(all_time))
    sheet['E2'] = all_time
    #   
    sheet_0 = wb.worksheets[0]
    sheet_0['A' + str(len(wb.sheetnames))] = today
    sheet_0['B' + str(len(wb.sheetnames))] = all_time
    while True:  #  
        try:
            wb.save(path_excel + r'\dnevnik.xlsx')
        except PermissionError:
            input('  excel     enter')
        else:
            break


Alerta sonora



Para variar, generamos 8 archivos de audio usando un sintetizador de voz , que se ejecutan aleatoriamente después de una hora de trabajo. Hasta que se tome un descanso, no habrá alertas de audio repetidas.



def audio_message(path, here):
    if here == 0:
        pass
    elif time.strftime('%H:%M:%S', time.gmtime(time.time()-here)) == "01:00:00":
        wav = random.choice(os.listdir(path + r'\audio'))
        winsound.PlaySound(path + r'\audio\\' + wav, winsound.SND_FILENAME)


Script de inicio al encender la PC



Para que el programa se cargue junto con Windows, puede crear un archivo por lotes que ejecutará el script Python. El archivo por lotes debe estar ubicado en el directorio:



C: \ Users \% username% \ AppData \ Roaming \ Microsoft \ Windows \ Start Menu \ Programs \ Startup



Dado que el archivo por lotes y el archivo de origen se almacenan en directorios diferentes, no puede usar os.getcwd ( ) ya que devolverá la ruta al archivo por lotes. Estamos usando os.path.abspath (__ archivo __) [: - 11]



UPD: ak545sugirió una mejor opción os.path.dirname (os.path.abspath (__ file__))



En el futuro



Quería que el programa siguiera mi rostro y no todos los que se sientan frente a la computadora. Para ello, se utilizará FaceNet, que es capaz de reconocimiento facial.

También hay planes para desarrollar un hermoso widget en lugar de una fea consola negra, para que cuando la computadora esté apagada, la grabación se realice automáticamente y sin usar una interrupción.

Gracias por su atención. Si tiene preguntas, escriba en los comentarios o en https://www.linkedin.com/in/evg-voronov/



All Articles