proof of concept

This commit is contained in:
2025-08-29 06:45:55 +02:00
commit 7a46dfe9b8
6 changed files with 340 additions and 0 deletions

51
550d_config.py Normal file
View File

@@ -0,0 +1,51 @@
import gphoto2 as gp
# def list_config(widget, indent=0):
# name = widget.get_name()
# label = widget.get_label()
# type_name = widget.get_type()
# choices = widget.get_choices()
# if choices:
# choices_str = ", ".join(choices)
# print(" " * indent + f"{name} ({label}) type={type_name} choices=[{choices_str}]")
# else:
# print(" " * indent + f"{name} ({label}) type={type_name}")
# for i in range(widget.count_children()):
# child = widget.get_child(i)
# list_config(child, indent + 1)
def list_config(widget, indent=0):
name = widget.get_name()
label = widget.get_label()
type_name = widget.get_type()
line = " " * indent + f"{name} ({label}) type={type_name}"
# tylko jeśli widget obsługuje choices
try:
choices = widget.get_choices()
if choices:
choices_str = ", ".join(str(c) for c in choices)
line += f" choices=[{choices_str}]"
except gp.GPhoto2Error:
# brak choices -> ignorujemy
pass
print(line)
# rekurencja dla dzieci
for i in range(widget.count_children()):
child = widget.get_child(i)
list_config(child, indent + 1)
camera = gp.Camera()
camera.init()
config = camera.get_config()
list_config(config)
camera.exit()

82
config.txt Normal file
View File

@@ -0,0 +1,82 @@
(.venv) bartool@BARTOOL-PC:~/local_projects/canon_550d$ python 550d_config.py
main (Camera and Driver Configuration) type=0
actions (Camera Actions) type=1
syncdatetime (Synchronize camera date and time with PC) type=4
uilock (UI Lock) type=4
popupflash (Popup Flash) type=4
autofocusdrive (Drive Canon DSLR Autofocus) type=4
manualfocusdrive (Drive Canon DSLR Manual focus) type=5 choices=[Near 1, Near 2, Near 3, None, Far 1, Far 2, Far 3]
cancelautofocus (Cancel Canon DSLR Autofocus) type=4
eoszoom (Canon EOS Zoom) type=2
eoszoomposition (Canon EOS Zoom Position) type=2
viewfinder (Canon EOS Viewfinder) type=4
eosremoterelease (Canon EOS Remote Release) type=5 choices=[None, Press Half, Press Full, Release Half, Release Full, Immediate, Press 1, Press 2, Press 3, Release 1, Release 2, Release 3]
eosmoviemode (Movie Mode) type=4
opcode (PTP Opcode) type=2
settings (Camera Settings) type=1
datetime (Camera Date and Time) type=8
reviewtime (Quick Review Time) type=5 choices=[None, 2 seconds, 4 seconds, 8 seconds, Hold]
output (Camera Output) type=5 choices=[TFT, PC, MOBILE, Off]
movierecordtarget (Recording Destination) type=5 choices=[None]
evfmode (EVF Mode) type=5 choices=[0, 1]
ownername (Owner Name) type=2
artist (Artist) type=2
copyright (Copyright) type=2
customfuncex (Custom Functions Ex) type=2
focusinfo (Focus Info) type=2
strobofiring (Strobo Firing) type=5 choices=[0, 1, 2]
flashcharged (Flash Charging State) type=2
autopoweroff (Auto Power Off) type=2
depthoffield (Depth of Field) type=2
capturetarget (Capture Target) type=5 choices=[Internal RAM, Memory card]
capture (Capture) type=4
remotemode (Remote Mode) type=2
eventmode (Event Mode) type=2
status (Camera Status Information) type=1
serialnumber (Serial Number) type=2
manufacturer (Camera Manufacturer) type=2
cameramodel (Camera Model) type=2
deviceversion (Device Version) type=2
vendorextension (Vendor Extension) type=2
model (Camera Model) type=2
ptpversion (PTP Version) type=2
batterylevel (Battery Level) type=2
batterylevel (Battery Level) type=2
mirrorlockstatus (Mirror Lock Status) type=2
mirrordownstatus (Mirror Down Status) type=2
lensname (Lens Name) type=2
eosserialnumber (Serial Number) type=2
shuttercounter (Shutter Counter) type=2
availableshots (Available Shots) type=2
eosmovieswitch (Movie Switch) type=2
imgsettings (Image Settings) type=1
imageformat (Image Format) type=5 choices=[L, cL, M, cM, S, cS, RAW + L, RAW]
imageformatsd (Image Format SD) type=5 choices=[L, cL, M, cM, S, cS, RAW + L, RAW]
iso (ISO Speed) type=5 choices=[Auto, 100, 200, 400, 800, 1600, 3200, 6400]
whitebalance (WhiteBalance) type=5 choices=[Auto, Daylight, Shadow, Cloudy, Tungsten, Fluorescent, Flash, Manual]
whitebalanceadjusta (WhiteBalance Adjust A) type=5 choices=[-9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
whitebalanceadjustb (WhiteBalance Adjust B) type=5 choices=[-9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
whitebalancexa (WhiteBalance X A) type=5 choices=[0, 1, 2, 3]
whitebalancexb (WhiteBalance X B) type=5 choices=[0, 1, 2, 3]
colorspace (Color Space) type=5 choices=[sRGB, AdobeRGB]
capturesettings (Capture Settings) type=1
exposurecompensation (Exposure Compensation) type=5 choices=[-5, -4.6, -4.3, -4, -3.6, -3.3, -3, -2.6, -2.3, -2, -1.6, -1.3, -1, -0.6, -0.3, 0, 0.3, 0.6, 1, 1.3, 1.6, 2, 2.3, 2.6, 3, 3.3, 3.6, 4, 4.3, 4.6, 5]
focusmode (Focus Mode) type=5 choices=[One Shot, AI Focus, AI Servo, Manual]
afmethod (AF Method) type=5 choices=[Live, LiveFace, Quick]
storageid (Storage Device) type=2
autoexposuremode (Canon Auto Exposure Mode) type=5 choices=[P, TV, AV, Manual, Bulb, A_DEP, DEP, Custom, Lock, Green, Night Portrait, Sports, Portrait, Landscape, Closeup, Flash Off, C2, C3, Creative Auto, Movie, Auto, Handheld Night Scene, HDR Backlight Control, SCN, Food, Grainy B/W, Soft focus, Toy camera effect, Fish-eye effect, Water painting effect, Miniature effect, HDR art standard, HDR art vivid, HDR art bold, HDR art embossed, Panning, HDR, Self Portrait, Hybrid Auto, Smooth skin, Fv]
drivemode (Drive Mode) type=5 choices=[Single, Continuous, Timer 10 sec, Timer 2 sec, Continuous timer]
picturestyle (Picture Style) type=5 choices=[Standard, Portrait, Landscape, Neutral, Faithful, Monochrome, User defined 1, User defined 2, User defined 3]
aperture (Aperture) type=5 choices=[implicit auto]
shutterspeed (Shutter Speed) type=5 choices=[30, 25, 20, 15, 13, 10.3, 8, 6.3, 5, 4, 3.2, 2.5, 2, 1.6, 1.3, 1, 0.8, 0.6, 0.5, 0.4, 0.3, 1/4, 1/5, 1/6, 1/8, 1/10, 1/13, 1/15, 1/20, 1/25, 1/30, 1/40, 1/50, 1/60, 1/80, 1/100, 1/125, 1/160, 1/200, 1/250, 1/320, 1/400, 1/500, 1/640, 1/800, 1/1000, 1/1250, 1/1600, 1/2000, 1/2500, 1/3200, 1/4000]
meteringmode (Metering Mode) type=5 choices=[Evaluative, Partial, Spot, Center-weighted average]
liveviewsize (Live View Size) type=5 choices=[Large, Medium]
bracketmode (Bracket Mode) type=5 choices=[Unknown value 0000]
aeb (Auto Exposure Bracketing) type=5 choices=[off, +/- 1/3, +/- 2/3, +/- 1, +/- 1 1/3, +/- 1 2/3, +/- 2]
alomode (Auto Lighting Optimization) type=5 choices=[Standard (disabled in manual exposure), x1, x2, x3]
other (Other PTP Device Properties) type=1
d402 (Friendly Device Name) type=2
d407 (Perceived Device Type) type=2
d406 (Session Initiator Info) type=2
d303 (PTP Property 0xd303) type=2
5001 (Battery Level) type=6 choices=[100, 0, 75, 50]

29
live_flask.py Normal file
View File

@@ -0,0 +1,29 @@
import cv2
import numpy as np
import gphoto2 as gp
from flask import Flask, Response
app = Flask(__name__)
camera = gp.Camera()
camera.init()
def generate():
while True:
file = camera.capture_preview()
data = file.get_data_and_size()
frame = np.frombuffer(data, dtype=np.uint8)
frame = cv2.imdecode(frame, cv2.IMREAD_COLOR)
if frame is not None:
_, buffer = cv2.imencode('.jpg', frame)
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + buffer.tobytes() + b'\r\n')
@app.route('/liveview')
def liveview():
return Response(generate(),
mimetype='multipart/x-mixed-replace; boundary=frame')
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)

125
live_pyside.py Normal file
View File

@@ -0,0 +1,125 @@
import sys
import cv2
import numpy as np
from PySide6.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QVBoxLayout, QHBoxLayout
from PySide6.QtGui import QImage, QPixmap
from PySide6.QtCore import QTimer
import gphoto2 as gp
class LiveViewApp(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Canon LiveView")
# Widget do obrazu
self.image_label = QLabel("Brak obrazu")
self.image_label.setFixedSize(640, 480)
# Przyciski start/stop
self.start_button = QPushButton("Start LiveView")
self.stop_button = QPushButton("Stop LiveView")
self.stop_button.setEnabled(False)
self.start_button.clicked.connect(self.start_liveview)
self.stop_button.clicked.connect(self.stop_liveview)
# Layout
button_layout = QHBoxLayout()
button_layout.addWidget(self.start_button)
button_layout.addWidget(self.stop_button)
layout = QVBoxLayout()
layout.addWidget(self.image_label)
layout.addLayout(button_layout)
self.setLayout(layout)
# Timer do odświeżania obrazu
self.timer = QTimer()
self.timer.timeout.connect(self.update_frame)
# kamera (na razie None podepniesz gphoto2)
self.camera = None
self.set_dummy_frame()
def set_dummy_frame(self):
self.dummy_frame = np.zeros((480, 640, 3), dtype=np.uint8)
cv2.putText(self.dummy_frame, "LiveView OFF", (200, 240),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
rgb_image = cv2.cvtColor(self.dummy_frame, cv2.COLOR_BGR2RGB)
h, w, ch = rgb_image.shape
qimg = QImage(rgb_image.data, w, h, ch * w, QImage.Format_RGB888)
pixmap = QPixmap.fromImage(qimg)
self.image_label.setPixmap(pixmap)
def start_liveview(self):
print("Start LiveView")
self.start_button.setEnabled(False)
self.stop_button.setEnabled(True)
# TODO: tu zainicjalizujesz kamerę gphoto2
# Przykład inicjalizacji kamery przez gphoto2 (wymaga zainstalowanego python-gphoto2)
try:
self.camera = gp.Camera()
self.camera.init()
except Exception as e:
print(f"Błąd inicjalizacji kamery: {e}")
self.camera = None
self.timer.start(100) # odświeżanie co 100ms
def stop_liveview(self):
print("Stop LiveView")
self.timer.stop()
self.start_button.setEnabled(True)
self.stop_button.setEnabled(False)
self.image_label.setText("Brak obrazu")
self.image_label.setPixmap(QPixmap()) # czyści obraz
self.set_dummy_frame()
# TODO: tu zamkniesz kamerę gphoto2
if self.camera:
self.camera.exit()
self.camera = None
self.start_button.setEnabled(True)
self.stop_button.setEnabled(False)
def update_frame(self):
# Na razie sztuczna klatka testowa z OpenCV (czarne tło + napis)
# frame = np.zeros((480, 640, 3), dtype=np.uint8)
# cv2.putText(frame, "Canon LiveView", (50, 240),
# cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
if self.camera:
try:
file = self.camera.capture_preview()
data = file.get_data_and_size()
frame = np.frombuffer(data, dtype=np.uint8)
frame = cv2.imdecode(frame, cv2.IMREAD_COLOR)
if frame is None:
return
except gp.GPhoto2Error as e:
print(f"Błąd odczytu LiveView: {e}")
return
except Exception as e:
print(f"Nieoczekiwany błąd: {e}")
return
else:
return
# Konwersja OpenCV -> QImage
rgb_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
h, w, ch = rgb_image.shape
qimg = QImage(rgb_image.data, w, h, ch * w, QImage.Format_RGB888)
pixmap = QPixmap.fromImage(qimg)
self.image_label.setPixmap(pixmap)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = LiveViewApp()
window.show()
sys.exit(app.exec())

26
live_view.py Normal file
View File

@@ -0,0 +1,26 @@
import gphoto2 as gp
import cv2
import numpy as np
camera = gp.Camera()
camera.init()
def liveview():
while True:
# Pobierz klatkę z LiveView
file = camera.capture_preview()
data = file.get_data_and_size()
frame = np.frombuffer(data, dtype=np.uint8)
frame = cv2.imdecode(frame, cv2.IMREAD_COLOR)
if frame is not None:
cv2.imshow("LiveView", frame)
if cv2.waitKey(1) == 27: # ESC
break
cv2.destroyAllWindows()
camera.exit()
if __name__ == "__main__":
liveview()

27
test_pyside.py Normal file
View File

@@ -0,0 +1,27 @@
import sys
from PySide6.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QVBoxLayout
class TestWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("PySide6 Test")
# Tworzymy label
self.label = QLabel("Hello from PySide6!", self)
# Tworzymy przycisk do zamykania
self.close_button = QPushButton("Zamknij")
self.close_button.clicked.connect(self.close)
# Układ pionowy
layout = QVBoxLayout()
layout.addWidget(self.label)
layout.addWidget(self.close_button)
self.setLayout(layout)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = TestWindow()
window.show()
sys.exit(app.exec())