Колба Opencv Перекодирует мини-видеоклипы

#python #opencv

Вопрос:

Я борюсь с созданием мини-видеоклипов(каждый из которых состоит из 10 видеоклипов) при записи моей веб-камеры с помощью open cv и python.

В принципе, я хочу вырезать видео с веб-камеры на 10-секундные клипы и сохранить в папке. Когда я делал это, видеоклипы вырезались, но когда я проверил первый видеоклип, в нем было 100% полного видео. Во втором-около 75% полного видео, в третьем-около 50% и т. Д.

Итак, как я могу это решить. Я введу свой код отверстия ниже. Надеюсь, вы поможете это исправить

 
camera = cv2.VideoCapture(0)
global rec, img, out
rec = 0

def gen_frames():
    global img
    while True:
        success, img = camera.read()

def record(out):
    global rec, img

    while(rec != False):
        time.sleep(0.05)
        out.write(img)

@app.route('/requests',methods=['POST','GET'])
def tasks():

    if request.form.get('rec') == 'Start/Stop Recording':
        global rec, img, out
        rec= not rec

        ############### This Part work when manualy recode on and off ###############

        # if rec:
        #     print("start")
        #     global out
        #     now=datetime.datetime.now()
        #     fourcc = cv2.VideoWriter_fourcc(*'XVID')
        #     p = os.path.sep.join(['clips', "vid_{}.avi".format(str(now).replace(":",''))])
        #     out = cv2.VideoWriter(p, fourcc, 25.0, size)
        #     thread = Thread(target = record, args=[out,])
        #     thread.start()

        # if(rec==False):
        #     print("stop")
        #     out.release()

        class TimerClass(threading.Thread):
            def __init__(self):
                threading.Thread.__init__(self)
                self.event = threading.Event()

            def run(self):
                while (rec != False) and not self.event.is_set():
                    now=datetime.datetime.now()
                    fourcc = cv2.VideoWriter_fourcc(*'XVID')
                    p = os.path.sep.join(['clips', "vid_{}.avi".format(str(now).replace(":",''))])
                    out = cv2.VideoWriter(p, fourcc, 25.0, size)
                    
                    thread = Thread(target = record, args=[out,])
                    thread.start()
                    self.event.wait(10)

            def stop(self):
                self.event.set()

        tmr = TimerClass()

        if(rec):
            print("start")
            tmr.start()

        if(rec==False):
            print("stop")
            tmr.stop()

    elif request.method=='GET':
        return render_template('index.html')
    return render_template('index.html')

if __name__ == '__main__':
    app.run(host='0.0.0.0', threaded=True)
 

Комментарии:

1. Я нахожу это немного большим кодом. можно ли это еще больше упростить? колба, вероятно, не нужна для воспроизведения проблемы. удалите все , что не является жизненно важным для возникновения проблемы. чем больше вещей там есть, тем меньше вероятность, что кто-нибудь на это посмотрит.

2. это длинный код, с запуском которого у нас могут возникнуть проблемы — сначала вы должны использовать print() или log просмотреть все значения во всех переменных. Может быть, вы измените какое-то значение и не сбросите его позже.

3. все переменные, создаваемые внешней функцией, являются глобальными, и вам не нужно использовать global внешние функции. Вам нужны global только внутренние функции, чтобы сообщить функции, что она должна использовать внешнюю/глобальную переменную вместо создания локальной.

4. вместо os.mkdir с try/except вы можете использовать os.makedirs(..., exist_ok=True)

5. Я думаю, ваша проблема в том, что вы создаете так много потоков. В TimerClass цикле запуска, который каждые 10 секунд создает другой поток для запуска record , но вы никогда не останавливаете предыдущий поток record . Я думаю, что если бы вы использовали print() , чтобы увидеть, какая часть кода выполняется, то вы ее увидите.

Ответ №1:

Что касается меня, проблема в том, что в цикле TimerClass вы создаете новый поток каждые 10 секунд, но вы никогда не останавливаете предыдущий поток, и он все еще записывает кадры. Я бы написал фреймы напрямую, TimerClass вместо того, чтобы использовать другой поток.

ИЛИ поток record() должен проверить время и остановиться через 10 секунд.

Я использую timedelta , чтобы рассчитать, когда прекратить запись одного файла и создать следующий. И я делаю это, TimerClass но вы могли бы сделать что-то подобное в record()

VideoWriter(..., 25.0) не пишет с 25fps помощью, но это только информация для видеоплееров о том, как быстро отображается это видео. Чтобы получить 10-секундное видео, вам нужно спать 0.04 , потому 1s / 25fps = 0.04 что иначе вам пришлось бы записать 250 кадров (10 секунд * 25 кадров в секунду = 250 кадров в секунду).

Поскольку коду также требуется некоторое время для работы, мне пришлось бы использовать 0.03 его вместо 0.04 того, чтобы получать 10-секундное видео.


Полный рабочий код.

Я использую render_template_string вместо render_template того, чтобы хранить все в одном файле — и каждый может просто скопировать и протестировать его.

 from flask import Flask, Response, request, render_template, render_template_string
import cv2
import os
import datetime, time
import threading

# global varaibles

capture = False
rec = False
out = None
img = None

app = Flask(__name__)
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 300

camera = cv2.VideoCapture(0)

frame_width = int(camera.get(3))
frame_height = int(camera.get(4))
size = (frame_width, frame_height)

os.makedirs('./shots', exist_ok=True)
os.makedirs('./clips', exist_ok=True)

def gen_frames():
    global capture
    global img
    
    print('[DEBUG] gen_frames: start')

    while True:
        success, img = camera.read()
        
        if not success:
            break
        
        if capture:
            capture = False

            now = datetime.datetime.now()
            filename = "shot_{}.png".format(str(now).replace(":",''))
            path = os.path.sep.join(['shots', filename])

            print('[DEBUG] capture:', path)

            cv2.imwrite(path, img)

        frame = cv2.imencode('.jpg', img)[1].tobytes()
        
        yield (b'--framern'
               b'Content-Type: image/jpegrnrn'   frame   b'rn')

@app.route('/video_feed')
def video_feed():
    return Response(gen_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')

@app.route('/')
def index():
    #return render_template('index.html')
    return render_template_string('''
Go to <a href="/requests">FORM</a>
''')

# define class only once
class TimerClass(threading.Thread):
    
    def __init__(self):
        threading.Thread.__init__(self)
        self.event = threading.Event()

    def run(self):
        seconds_10 = datetime.timedelta(seconds=10)
        
        while rec and not self.event.is_set():
            now = datetime.datetime.now()
            filename = "vid_{}.avi".format(str(now).replace(":", ''))
            path = os.path.sep.join(['clips', filename])
            
            fourcc = cv2.VideoWriter_fourcc(*'XVID')
            out = cv2.VideoWriter(path, fourcc, 25.0, size)

            end = now   seconds_10
            
            print('[DEBUG] end:', end)
            
            while now < end and rec and not self.event.is_set():
                if img is not None:  # `img` can be `numpy.array` so it can't check `if img:`
                    out.write(img)
                time.sleep(0.03)  # 1s / 25fps = 0.04  # it needs some time for code.
                now = datetime.datetime.now()
        
            out.release()
            
    def stop(self):
        self.event.set()

@app.route('/requests', methods=['POST', 'GET'])
def tasks():
    global capture
    global rec
    
    print('[DEBUG] click:', request.form.get('click'))
    print('[DEBUG] rec  :', request.form.get('rec'))
    
    if request.method == 'POST':
        if request.form.get('click') == 'Capture':
            capture = True

        if request.form.get('rec') == 'Start/Stop Recording':
            rec = not rec
    
            tmr = TimerClass()
    
            if rec:
                print("start")
                tmr.start()
            else:
                print("stop")
                tmr.stop()
    
    #return render_template_string('index.html')
    return render_template_string('''
<img src="/video_feed"><br/>
<form method="POST">
<button type="submit" name="click" value="Capture">Capture</button>
<button type="submit" name="rec" value="Start/Stop Recording">Start/Stop Recording</button>
</form>
''')

if __name__ == '__main__':
    thread_cam = threading.Thread(target=gen_frames)
    thread_cam.start()
    app.run(host='0.0.0.0', threaded=True)
 

Редактировать:

То же самое с нитью record()

 def record(seconds):
    now = datetime.datetime.now()
    
    filename = "vid_{}.avi".format(str(now).replace(":", ''))
    path = os.path.sep.join(['clips', filename])
    
    fourcc = cv2.VideoWriter_fourcc(*'XVID')
    out = cv2.VideoWriter(path, fourcc, 25.0, size)

    end = now   datetime.timedelta(seconds=seconds)
    
    print('[DEBUG] end:', end)
    
    while now < end and rec:
        if img is not None:  # `img` can be `numpy.array` so it can't check `if img:`
            out.write(img)
        time.sleep(0.03)  # 1s / 25fps = 0.04  # it needs some time for code.
        now = datetime.datetime.now()

    out.release()

# define class only once
class TimerClass(threading.Thread):
    
    def __init__(self):
        threading.Thread.__init__(self)
        self.event = threading.Event()

    def run(self):
        length = 10
        while rec and not self.event.is_set():
            t = threading.Thread(target=record, args=(length,))
            t.start()
            self.event.wait(length)
            
    def stop(self):
        self.event.set()