100 lines
3.2 KiB
Python
100 lines
3.2 KiB
Python
# app.py — PS3 Eye + Flask — FINAL, LED STAYS ON, 60 fps
|
||
from flask import Flask, Response, render_template_string
|
||
import cv2
|
||
import time
|
||
import numpy as np
|
||
|
||
app = Flask(__name__)
|
||
|
||
# ←←← CAMERA IS NO LONGER GLOBAL — created on first use
|
||
camera = None
|
||
|
||
def get_camera():
|
||
global camera
|
||
if camera is None or not camera.isOpened():
|
||
print("Initializing PS3 Eye (index 2, V4L2)...")
|
||
camera = cv2.VideoCapture(2, cv2.CAP_V4L2)
|
||
if not camera.isOpened():
|
||
raise RuntimeError("Cannot open PS3 Eye")
|
||
|
||
camera.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M','J','P','G'))
|
||
camera.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
|
||
camera.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
|
||
camera.set(cv2.CAP_PROP_FPS, 60)
|
||
camera.set(cv2.CAP_PROP_BUFFERSIZE, 1)
|
||
|
||
# Wake-up sequence — LED turns on here and stays on
|
||
print("Warming up camera — LED should turn ON now...")
|
||
for i in range(60):
|
||
ret, _ = camera.read()
|
||
if ret and i == 30:
|
||
print("Camera alive — LED ON permanently")
|
||
time.sleep(0.5)
|
||
|
||
w = camera.get(cv2.CAP_PROP_FRAME_WIDTH)
|
||
h = camera.get(cv2.CAP_PROP_FRAME_HEIGHT)
|
||
fps = camera.get(cv2.CAP_PROP_FPS)
|
||
print(f"Camera initialized → {w}x{h} @ {fps} fps")
|
||
return camera
|
||
|
||
frame_counter = 0
|
||
last_time = time.time()
|
||
|
||
def generate_frames():
|
||
global frame_counter, last_time
|
||
cam = get_camera() # ← ensures camera exists and is warm
|
||
while True:
|
||
success, frame = cam.read()
|
||
if not success:
|
||
frame = np.zeros((480, 640, 3), np.uint8)
|
||
cv2.putText(frame, "NO FRAME", (100, 240), cv2.FONT_HERSHEY_SIMPLEX, 2, (0,0,255), 5)
|
||
else:
|
||
now = time.time()
|
||
if now - last_time >= 1.0:
|
||
print(f"Live → ~{frame_counter/(now-last_time):.1f} fps")
|
||
frame_counter = 0
|
||
last_time = now
|
||
frame_counter += 1
|
||
|
||
ret, buffer = cv2.imencode('.jpg', frame, [int(cv2.IMWRITE_JPEG_QUALITY), 82])
|
||
yield (b'--frame\r\n'
|
||
b'Content-Type: image/jpeg\r\n\r\n' + buffer.tobytes() + b'\r\n')
|
||
|
||
@app.route('/')
|
||
def index():
|
||
return render_template_string('''
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<title>PS3 Eye — 60 fps Live</title>
|
||
<style>
|
||
body {background:#000; color:#0f0; font-family:monospace; text-align:center; margin:0; padding:20px;}
|
||
img {width:90vw; max-width:960px; border:8px solid #0f0; border-radius:15px;}
|
||
h1 {text-shadow:0 0 20px #0f0;}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<h1>PS3 Eye — 640×480 @ 60 fps</h1>
|
||
<img src="/video_feed">
|
||
<p>LED should stay ON permanently</p>
|
||
</body>
|
||
</html>
|
||
''')
|
||
|
||
@app.route('/video_feed')
|
||
def video_feed():
|
||
return Response(generate_frames(),
|
||
mimetype='multipart/x-mixed-replace; boundary=frame')
|
||
|
||
# Only release when the entire Python process exits
|
||
import atexit
|
||
atexit.register(lambda: camera.release() if camera and camera.isOpened() else None)
|
||
|
||
if __name__ == '__main__':
|
||
try:
|
||
app.run(host='0.0.0.0', port=5000, threaded=True)
|
||
finally:
|
||
if camera and camera.isOpened():
|
||
camera.release()
|
||
print("Camera released on shutdown")
|