Fix: Session-Zugriff außerhalb des Request-Kontexts behoben

Problem: RuntimeError beim Zugriff auf session in Generator-Funktionen
Lösung:
- Liniendaten werden vor dem Generator aus der Session gelesen
- Als Parameter an detect_objects_from_webcam() übergeben
- Als Parameter an detect_objects_from_video() übergeben
- Reset-Button lädt jetzt die Seite neu (mit Bestätigungsdialog)

Technische Details:
- Generator-Funktionen haben keinen Request-Kontext
- Session-Werte müssen vor Generator-Start erfasst werden
- Counter-Reset funktioniert durch Seiten-Reload

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-08 14:48:09 +00:00
parent 504f8c0b69
commit 9145872ef1
3 changed files with 44 additions and 55 deletions

51
app.py
View File

@@ -66,11 +66,12 @@ def get_counting_line():
@app.route('/api/reset_count', methods=['POST']) @app.route('/api/reset_count', methods=['POST'])
def reset_count(): def reset_count():
"""API endpoint to reset the vehicle count""" """API endpoint to reset the vehicle count (requires page reload to take effect)"""
session['reset_count'] = True # Note: Count reset requires reloading the video stream
return jsonify({'status': 'success'}) # The count is stored in the generator function's local variables
return jsonify({'status': 'success', 'message': 'Please reload the page to reset the count'})
def detect_objects_from_webcam(): def detect_objects_from_webcam(line_data):
count = 0 count = 0
cap = cv2.VideoCapture(0) # 0 for the default webcam cap = cv2.VideoCapture(0) # 0 for the default webcam
@@ -80,6 +81,10 @@ def detect_objects_from_webcam():
vehicle_count = 0 vehicle_count = 0
vehicle_type_counts = {'car': 0, 'truck': 0, 'bus': 0, 'motorcycle': 0} vehicle_type_counts = {'car': 0, 'truck': 0, 'bus': 0, 'motorcycle': 0}
# Get line coordinates
line_start = (line_data['x1'], line_data['y1'])
line_end = (line_data['x2'], line_data['y2'])
while True: while True:
ret, frame = cap.read() ret, frame = cap.read()
if not ret: if not ret:
@@ -90,18 +95,6 @@ def detect_objects_from_webcam():
# Resize the frame to (1020, 600) # Resize the frame to (1020, 600)
frame = cv2.resize(frame, (1020, 600)) frame = cv2.resize(frame, (1020, 600))
# Get counting line from session (default to horizontal middle line)
line_data = session.get('counting_line', {'x1': 0, 'y1': 300, 'x2': 1020, 'y2': 300})
line_start = (line_data['x1'], line_data['y1'])
line_end = (line_data['x2'], line_data['y2'])
# Check if count should be reset
if session.get('reset_count', False):
counted_ids.clear()
vehicle_count = 0
vehicle_type_counts = {'car': 0, 'truck': 0, 'bus': 0, 'motorcycle': 0}
session['reset_count'] = False
# Draw counting line (dashed yellow line with black gaps) # Draw counting line (dashed yellow line with black gaps)
cv2.line(frame, line_start, line_end, (0, 255, 255), 2, cv2.LINE_AA) cv2.line(frame, line_start, line_end, (0, 255, 255), 2, cv2.LINE_AA)
# Draw dashed effect # Draw dashed effect
@@ -166,7 +159,9 @@ def detect_objects_from_webcam():
@app.route('/webcam_feed') @app.route('/webcam_feed')
def webcam_feed(): def webcam_feed():
return Response(detect_objects_from_webcam(), # Get line data from session before starting generator
line_data = session.get('counting_line', {'x1': 0, 'y1': 300, 'x2': 1020, 'y2': 300})
return Response(detect_objects_from_webcam(line_data),
mimetype='multipart/x-mixed-replace; boundary=frame') mimetype='multipart/x-mixed-replace; boundary=frame')
@app.route('/upload', methods=['POST']) @app.route('/upload', methods=['POST'])
@@ -199,7 +194,7 @@ def play_video(filename):
def send_video(filename): def send_video(filename):
return send_from_directory('uploads', filename) return send_from_directory('uploads', filename)
def detect_objects_from_video(video_path): def detect_objects_from_video(video_path, line_data):
cap = cv2.VideoCapture(video_path) cap = cv2.VideoCapture(video_path)
count = 0 count = 0
@@ -209,6 +204,10 @@ def detect_objects_from_video(video_path):
vehicle_count = 0 vehicle_count = 0
vehicle_type_counts = {'car': 0, 'truck': 0, 'bus': 0, 'motorcycle': 0} vehicle_type_counts = {'car': 0, 'truck': 0, 'bus': 0, 'motorcycle': 0}
# Get line coordinates
line_start = (line_data['x1'], line_data['y1'])
line_end = (line_data['x2'], line_data['y2'])
while cap.isOpened(): while cap.isOpened():
ret, frame = cap.read() ret, frame = cap.read()
if not ret: if not ret:
@@ -220,18 +219,6 @@ def detect_objects_from_video(video_path):
# Resize the frame to (1020, 600) # Resize the frame to (1020, 600)
frame = cv2.resize(frame, (1020, 600)) frame = cv2.resize(frame, (1020, 600))
# Get counting line from session (default to horizontal middle line)
line_data = session.get('counting_line', {'x1': 0, 'y1': 300, 'x2': 1020, 'y2': 300})
line_start = (line_data['x1'], line_data['y1'])
line_end = (line_data['x2'], line_data['y2'])
# Check if count should be reset
if session.get('reset_count', False):
counted_ids.clear()
vehicle_count = 0
vehicle_type_counts = {'car': 0, 'truck': 0, 'bus': 0, 'motorcycle': 0}
session['reset_count'] = False
# Draw counting line (dashed yellow line with black gaps) # Draw counting line (dashed yellow line with black gaps)
cv2.line(frame, line_start, line_end, (0, 255, 255), 2, cv2.LINE_AA) cv2.line(frame, line_start, line_end, (0, 255, 255), 2, cv2.LINE_AA)
# Draw dashed effect # Draw dashed effect
@@ -297,7 +284,9 @@ def detect_objects_from_video(video_path):
@app.route('/video_feed/<filename>') @app.route('/video_feed/<filename>')
def video_feed(filename): def video_feed(filename):
video_path = os.path.join('uploads', filename) video_path = os.path.join('uploads', filename)
return Response(detect_objects_from_video(video_path), # Get line data from session before starting generator
line_data = session.get('counting_line', {'x1': 0, 'y1': 300, 'x2': 1020, 'y2': 300})
return Response(detect_objects_from_video(video_path, line_data),
mimetype='multipart/x-mixed-replace; boundary=frame') mimetype='multipart/x-mixed-replace; boundary=frame')
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -174,18 +174,18 @@
}); });
resetCountBtn.addEventListener('click', () => { resetCountBtn.addEventListener('click', () => {
fetch('/api/reset_count', { if (confirm('Zähler zurücksetzen? Die Seite wird neu geladen.')) {
method: 'POST', fetch('/api/reset_count', {
headers: { 'Content-Type': 'application/json' } method: 'POST',
}) headers: { 'Content-Type': 'application/json' }
.then(res => res.json()) })
.then(data => { .then(res => res.json())
console.log('Count reset:', data); .then(data => {
infoText.textContent = 'Zähler wurde zurückgesetzt!'; console.log('Count reset:', data);
setTimeout(() => { // Reload page to reset the video stream and counter
infoText.textContent = 'Klicke auf "Zähllinie setzen" und dann zweimal auf das Video, um die Zähllinie zu definieren.'; location.reload();
}, 2000); });
}); }
}); });
function drawLine() { function drawLine() {

View File

@@ -174,18 +174,18 @@
}); });
resetCountBtn.addEventListener('click', () => { resetCountBtn.addEventListener('click', () => {
fetch('/api/reset_count', { if (confirm('Zähler zurücksetzen? Die Seite wird neu geladen.')) {
method: 'POST', fetch('/api/reset_count', {
headers: { 'Content-Type': 'application/json' } method: 'POST',
}) headers: { 'Content-Type': 'application/json' }
.then(res => res.json()) })
.then(data => { .then(res => res.json())
console.log('Count reset:', data); .then(data => {
infoText.textContent = 'Zähler wurde zurückgesetzt!'; console.log('Count reset:', data);
setTimeout(() => { // Reload page to reset the video stream and counter
infoText.textContent = 'Klicke auf "Zähllinie setzen" und dann zweimal auf das Video, um die Zähllinie zu definieren.'; location.reload();
}, 2000); });
}); }
}); });
function drawLine() { function drawLine() {