Como não perder a passagem do tempo trabalhando no computador. Aplicativo para monitoramento de trabalho e manutenção de estatísticas





Trabalho como professora no parque tecnológico infantil Quantorium. Durante o período de auto-isolamento, nós, como todo mundo, mudamos para o ensino à distância. E devido ao fato de as crianças passarem a ficar ainda mais tempo no computador, a administração decidiu encurtar a carga horária acadêmica e fazer intervalos entre os trabalhos - para preservar a visão. Nós escrevemos um aplicativo que calcula o tempo gasto no computador, mantém as estatísticas em excel - útil para os pais e dá um alerta sonoro na hora de fazer uma pausa.



O aplicativo será útil para aqueles que estão perdidos no tempo trabalhando no PC e querem se inserir no espaço do tempo ou acompanhar que parte da vida se perde no espaço digital.



Link para o repositório



Abaixo do recorte uma análise detalhada.



Para criar um programa, você precisa resolver as seguintes tarefas:



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


web



Consideramos a opção de rastrear a presença no computador pelo movimento do mouse do computador, talvez seja mais fácil e a webcam permanecerá gratuita, mas então não poderemos levar em conta a exibição de programas de TV ou vídeos do youtube, além disso, o reconhecimento de face é muito mais interessante.



Para detecção de rosto, usamos a arquitetura mtcnn. Uma cabeça posicionada em qualquer ângulo, usando óculos, em más condições de iluminação, mesmo que o rosto não esteja totalmente enquadrado ou coberto por uma mão, a rede neural está funcionando corretamente. Não vamos construir por conta própria, vamos conectar a biblioteca de mesmo nome, que resolverá nosso problema usando algumas linhas de código.



Inicialmente, foi utilizado o método de Viola-Jones. A precisão é muito pior que a do mtcnn, o processador carrega o mesmo.











Vamos mover a saída do resultado para uma função separada, é necessário apenas ver como funciona o reconhecimento.



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()


Grande atraso cv2.waitKey (300) para não carregar o processador. O processamento de 3 a 4 quadros por segundo carrega o i3-8145U em uma média de 15%.



Tempo de contagem



O programa não deve gravar no log nos seguintes casos:



  • sentou-se em frente ao computador por 5 segundos para fazer algo byrik;
  • saí para servir café ou esticar as pernas.


Para resolver esses problemas, o programa usa dois cronômetros time_here (conta regressivamente o tempo em que o rosto está no quadro) e time_not_here (quando não há rosto). Ambos os cronômetros são zerados quando uma entrada no registro é feita. Além disso, time_not_here é redefinido toda vez que um rosto aparece no quadro.



A variável min_time_here indica o tempo mínimo gasto no computador, após o qual vale a pena gravar no log. A gravação será feita depois que a pessoa se distrair do computador por um tempo maior do que o especificado em min_time_not_here .



No código abaixo, a gravação é realizada caso você tenha passado pelo menos 5 minutos no computador. O programa não levará em consideração o fato de que fico distraído por 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()


Se o usuário deseja desligar o computador, primeiro é necessário interromper o programa (ctrl + c) e o horário do trabalho atual será registrado. Para isso, um manipulador de exceção é usado.



Gravando dados em documentos do Excel



Para trabalhar com o Excel, a maravilhosa biblioteca openpyxl ajudará, que simultaneamente lê e grava dados.



Todos os dias o programa cria uma nova planilha com a data atual.





Em 1 folha, são registradas as informações diárias, em que dia quanto tempo foi gasto no computador.







A função write_excel abre o documento, grava dados e fecha-o imediatamente.



Você não pode modificar um documento por meio do openpyxl se ele já estiver aberto pelo usuário. Uma exceção foi adicionada 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 sonoro



Para variar, geramos 8 arquivos de áudio usando um sintetizador de voz , que rodam aleatoriamente após uma hora de trabalho. Até você fazer uma pausa, não haverá alertas de áudio repetidos.



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 inicialização ao ligar o PC



Para que o programa seja carregado junto com o Windows, você pode criar um arquivo em lote que executará o script python. O arquivo em lote deve estar localizado no diretório:



C: \ Usuários \% nome de usuário% \ AppData \ Roaming \ Microsoft \ Windows \ Menu Iniciar \ Programas \ Inicializar



Como o arquivo em lote e o arquivo de origem estão armazenados em diretórios diferentes, você não pode usar o os.getcwd ( ), pois ele retornará o caminho para o arquivo em lote. Estamos usando os.path.abspath (__ file __) [: - 11]



UPD: ak545sugeriu uma opção melhor os.path.dirname (os.path.abspath (__ file__))



No futuro



Eu queria que o programa seguisse meu rosto, e não todo mundo que se senta no computador. Para isso, será utilizado o FaceNet, que é capaz de reconhecimento facial.

Também há planos de desenvolver um lindo widget em vez de um console preto feio, para que quando o computador for desligado, a gravação seja feita automaticamente, e não usando interrupção.

Obrigado pela atenção. Se você tiver dúvidas, escreva nos comentários ou em https://www.linkedin.com/in/evg-voronov/



All Articles