Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
41e7bfe426
|
|
@ -1,6 +1,7 @@
|
||||||
[hardware_config]
|
[hardware_config]
|
||||||
; archive_synchronizer: present
|
; archive_synchronizer: present
|
||||||
; galaxy_camera: present
|
; galaxy_camera: present
|
||||||
|
; uvc_camera: absent
|
||||||
; label_printer: present
|
; label_printer: present
|
||||||
; multicomp: present
|
; multicomp: present
|
||||||
; neo_pixels: present
|
; neo_pixels: present
|
||||||
|
|
@ -25,6 +26,12 @@ balance_red: 1.85
|
||||||
balance_green: 1.0
|
balance_green: 1.0
|
||||||
balance_blue: 1.5
|
balance_blue: 1.5
|
||||||
|
|
||||||
|
[uvc_camera]
|
||||||
|
horizontal_resolution: 2448
|
||||||
|
vertical_resolution: 2048
|
||||||
|
exposure_time: 10000
|
||||||
|
focus: 100
|
||||||
|
|
||||||
[vision_saver]
|
[vision_saver]
|
||||||
time_format: %Y-%m-%d_%H-%M-%S
|
time_format: %Y-%m-%d_%H-%M-%S
|
||||||
path: ./data/images
|
path: ./data/images
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
[hostnames]
|
[hostnames]
|
||||||
DESKTOP-VOCOB38: vm
|
DESKTOP-VOCOB38: vm
|
||||||
mino: mino
|
mino: mino
|
||||||
neodl-MS-7A62: stten3
|
neodl-MS-7A62: stten1
|
||||||
ST-TEN-1: stten1
|
ST-TEN-1: stten1
|
||||||
ST-TEN-2: stten2
|
ST-TEN-2: stten2
|
||||||
stten3: stten3
|
stten3: stten3
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
[hardware_config]
|
[hardware_config]
|
||||||
archive_synchronizer: absent
|
archive_synchronizer: absent
|
||||||
galaxy_camera: present
|
#galaxy_camera: present
|
||||||
|
uvc_camera: present
|
||||||
label_printer: present
|
label_printer: present
|
||||||
neo_pixels: present
|
neo_pixels: present
|
||||||
remote_api: absent
|
remote_api: absent
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,11 @@ void setup() {
|
||||||
set_color(-1, 0, 0, 0);
|
set_color(-1, 0, 0, 0);
|
||||||
delay(1000);
|
delay(1000);
|
||||||
set_color(-1, 255, 255, 255);
|
set_color(-1, 255, 255, 255);
|
||||||
|
delay(2000);
|
||||||
|
for(int cnt=255;cnt>=0;cnt--){
|
||||||
|
set_color(-1, cnt, cnt,cnt);
|
||||||
|
delay(20);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t parse_message(uint8_t *message, Address *address, Color color) {
|
uint8_t parse_message(uint8_t *message, Address *address, Color color) {
|
||||||
|
|
|
||||||
213
src/components/uvc_camera.py
Normal file
213
src/components/uvc_camera.py
Normal file
|
|
@ -0,0 +1,213 @@
|
||||||
|
import pathlib
|
||||||
|
import sys
|
||||||
|
from itertools import cycle
|
||||||
|
|
||||||
|
import cv2
|
||||||
|
import imutils
|
||||||
|
import numpy as np
|
||||||
|
from PyQt5.QtCore import QMutex, Qt, QThread, pyqtSignal
|
||||||
|
from PyQt5.QtGui import QImage, QPixmap
|
||||||
|
from PyQt5.QtWidgets import (QDialog, QFormLayout, QLabel, QMessageBox,
|
||||||
|
QPushButton, QSizePolicy, QSlider)
|
||||||
|
|
||||||
|
|
||||||
|
if "--sim-camera" in sys.argv:
|
||||||
|
from components.dummies.gxpy import DummyCamera
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from .component import Component
|
||||||
|
|
||||||
|
|
||||||
|
class UVCCamera(Component):
|
||||||
|
_edits_new_frame = pyqtSignal(object)
|
||||||
|
|
||||||
|
def __init__(self, config=None, name=None, period=1, lazy=True, paused=False, threaded=True, registers=None):
|
||||||
|
super().__init__(config=config, name=name, period=period, lazy=lazy, paused=paused, threaded=threaded)
|
||||||
|
self.lock = QMutex()
|
||||||
|
self.simulate = "--sim-camera" in sys.argv
|
||||||
|
self._last_frame = None
|
||||||
|
|
||||||
|
def config_changed(self):
|
||||||
|
if self.simulate:
|
||||||
|
self.device_manager = None
|
||||||
|
self.camera = DummyCamera()
|
||||||
|
self.sim_imgs = cycle(sorted(pathlib.Path("data/simulation_images/").glob("*.png")))
|
||||||
|
else:
|
||||||
|
self.camera = cv2.VideoCapture(0)
|
||||||
|
|
||||||
|
self.resolution = {
|
||||||
|
"w": int(self.round_4(self.config[self.name]["horizontal_resolution"])),
|
||||||
|
"h": int(self.round_4(self.config[self.name]["vertical_resolution"])),
|
||||||
|
}
|
||||||
|
self._period = int(self.config[self.name].get("frame_time_ms", 300)) / 1000
|
||||||
|
self.exposure_time = int(self.config[self.name]["exposure_time"])
|
||||||
|
self.roi = {
|
||||||
|
"x": int(self.round_4(self.config[self.name].get("horizontal_crop_offset", 0))),
|
||||||
|
"w": int(self.round_4(self.config[self.name].get("horizontal_crop_resolution", self.resolution["w"]))),
|
||||||
|
"y": int(self.round_4(self.config[self.name].get("vertical_crop_offset", 0))),
|
||||||
|
"h": int(self.round_4(self.config[self.name].get("vertical_crop_resolution", self.resolution["h"]))),
|
||||||
|
"r": int(self.config[self.name].get("rotate_90_clockwise_times", 0)),
|
||||||
|
}
|
||||||
|
self.auto_white_balance = self.config[self.name].get("auto_white_balance", "").lower() in {"on", "1", "y", "yes", "true", "enable", "enabled", }
|
||||||
|
self.balance = {
|
||||||
|
"r": float(self.config[self.name].get("balance_red", 1)),
|
||||||
|
"g": float(self.config[self.name].get("balance_green", 1)),
|
||||||
|
"b": float(self.config[self.name].get("balance_blue", 1)),
|
||||||
|
}
|
||||||
|
self.lock.lock()
|
||||||
|
|
||||||
|
self.edits = None
|
||||||
|
self.lock.unlock()
|
||||||
|
self.edits_enabled = "--camera-edits" in sys.argv
|
||||||
|
if self.edits_enabled:
|
||||||
|
self.init_edits()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def round_4(x):
|
||||||
|
return 4 * round(int(x) / 4)
|
||||||
|
|
||||||
|
@Component.reconfig_on_error
|
||||||
|
def _get(self):
|
||||||
|
frame = None
|
||||||
|
self.lock.lock()
|
||||||
|
if self.simulate:
|
||||||
|
img_path = next(self.sim_imgs)
|
||||||
|
self.log.debug(f"loading image {img_path}")
|
||||||
|
frame = cv2.cvtColor(cv2.imread(str(img_path)), cv2.COLOR_BGR2RGB)
|
||||||
|
QThread.msleep(1000)
|
||||||
|
else:
|
||||||
|
self.camera.TriggerSoftware.send_command()
|
||||||
|
frame = self.camera.read()
|
||||||
|
if frame is not None:
|
||||||
|
frame = np.rot90(frame, self.roi["r"])
|
||||||
|
self.lock.unlock()
|
||||||
|
if frame is None:
|
||||||
|
self.log.error("failed to get frame")
|
||||||
|
elif self.edits_enabled:
|
||||||
|
frame = self.edit(frame, self.edits)
|
||||||
|
self._edits_new_frame.emit([frame])
|
||||||
|
super()._get([frame])
|
||||||
|
|
||||||
|
def __del__(self, event=None):
|
||||||
|
if self.camera is not None:
|
||||||
|
self.camera.stream_off()
|
||||||
|
self.camera.close_device()
|
||||||
|
|
||||||
|
def init_edits(self):
|
||||||
|
self.edits_enabled = True
|
||||||
|
self.edits_dialog = EditsDialog(self.roi)
|
||||||
|
self.edits_dialog.edits_changed.connect(self.set_edits)
|
||||||
|
self._edits_new_frame.connect(self.edits_dialog.save_and_show_edits_new_frame)
|
||||||
|
self.edits_dialog.edits_pause.connect(self.toggle_paused)
|
||||||
|
|
||||||
|
def toggle_paused(self):
|
||||||
|
if self.running:
|
||||||
|
self.pause()
|
||||||
|
else:
|
||||||
|
self.resume()
|
||||||
|
|
||||||
|
def set_edits(self, edits=None):
|
||||||
|
self.edits = edits
|
||||||
|
|
||||||
|
def edit(self, img, edits=None):
|
||||||
|
if edits is None:
|
||||||
|
return img
|
||||||
|
# BRIGHTNESS AND CONTRAST
|
||||||
|
contrast = float(edits.get("contrast", 0))
|
||||||
|
brightness = float(edits.get("brightness", 0))
|
||||||
|
if not (contrast == 0 and brightness == 0):
|
||||||
|
img = imutils.adjust_brightness_contrast(img, brightness=brightness, contrast=contrast / 255 * 500)
|
||||||
|
# ROTATE AND SCALE
|
||||||
|
rotation = -float(edits.get("rotation", 0))
|
||||||
|
scale = float(edits.get("scale", 1))
|
||||||
|
if not (rotation == 0 and scale == 1):
|
||||||
|
img = imutils.rotate(img, rotation, scale=scale)
|
||||||
|
# TRANSLATE
|
||||||
|
translation_x = float(edits.get("translation_x", 0))
|
||||||
|
translation_y = float(edits.get("translation_y", 0))
|
||||||
|
if not (translation_x == 0 and translation_y == 0):
|
||||||
|
img = imutils.translate(img, translation_x, translation_y)
|
||||||
|
return img
|
||||||
|
|
||||||
|
|
||||||
|
class EditsDialog(QDialog):
|
||||||
|
edits_changed = pyqtSignal(object)
|
||||||
|
edits_pause = pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self, roi):
|
||||||
|
super().__init__()
|
||||||
|
self.frame = None
|
||||||
|
self.edits = {
|
||||||
|
"brightness": 0,
|
||||||
|
"contrast": 0,
|
||||||
|
"rotation": 0,
|
||||||
|
"scale": 1,
|
||||||
|
"translation_x": 0,
|
||||||
|
"translation_y": 0,
|
||||||
|
}
|
||||||
|
self.edits_specs = {
|
||||||
|
"brightness": [[-255, 255], 1, QSlider(Qt.Horizontal), QLabel()],
|
||||||
|
"contrast": [[-255, 255], 1, QSlider(Qt.Horizontal), QLabel()],
|
||||||
|
"rotation": [[-180, 180], 1, QSlider(Qt.Horizontal), QLabel()],
|
||||||
|
"scale": [[0, 5], 100, QSlider(Qt.Horizontal), QLabel()],
|
||||||
|
"translation_x": [[-roi["w"], roi["w"]], 1, QSlider(Qt.Horizontal), QLabel()],
|
||||||
|
"translation_y": [[-roi["h"], roi["h"]], 1, QSlider(Qt.Horizontal), QLabel()],
|
||||||
|
}
|
||||||
|
layout = QFormLayout()
|
||||||
|
self.edits_frame_l = QLabel()
|
||||||
|
self.edits_frame_l.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||||
|
layout.addRow(self.edits_frame_l)
|
||||||
|
self.edits_pause_b = QPushButton("toggle paused")
|
||||||
|
layout.addRow(self.edits_pause_b)
|
||||||
|
self.edits_pause_b.clicked.connect(self.edits_pause)
|
||||||
|
for edit, limits in self.edits_specs.items():
|
||||||
|
limit, multiplier, slider, label = limits
|
||||||
|
slider.setRange(limit[0] * multiplier, limit[1] * multiplier)
|
||||||
|
slider.setSingleStep(1)
|
||||||
|
slider.setValue(self.edits[edit] * multiplier)
|
||||||
|
label.setText(f"{edit}: {self.edits[edit]}")
|
||||||
|
layout.addRow(label, slider)
|
||||||
|
slider.valueChanged.connect(self.update_edits)
|
||||||
|
self.edits_save_frame_b = QPushButton("save frame")
|
||||||
|
layout.addRow(self.edits_save_frame_b)
|
||||||
|
self.edits_save_frame_b.clicked.connect(self.edits_save_frame)
|
||||||
|
self.setLayout(layout)
|
||||||
|
self.update_edits()
|
||||||
|
self.show()
|
||||||
|
|
||||||
|
def update_edits(self):
|
||||||
|
for edit, limits in self.edits_specs.items():
|
||||||
|
limit, multiplier, slider, label = limits
|
||||||
|
self.edits[edit] = slider.value() / multiplier
|
||||||
|
label.setText(f"{edit}: {self.edits[edit]}")
|
||||||
|
self.edits_changed.emit(self.edits)
|
||||||
|
|
||||||
|
def save_and_show_edits_new_frame(self, frame):
|
||||||
|
self.frame = frame[0]
|
||||||
|
if self.frame is not None:
|
||||||
|
self.edits_frame_l.setPixmap(
|
||||||
|
QPixmap.fromImage(
|
||||||
|
QImage(
|
||||||
|
self.frame.data,
|
||||||
|
self.frame.shape[1], # width
|
||||||
|
self.frame.shape[0], # height
|
||||||
|
self.frame.shape[2] * self.frame.shape[1], # width * channels
|
||||||
|
QImage.Format_RGB888
|
||||||
|
)
|
||||||
|
).scaled(
|
||||||
|
max(self.edits_frame_l.width(), 640),
|
||||||
|
max(self.edits_frame_l.height(), 480),
|
||||||
|
Qt.KeepAspectRatio,
|
||||||
|
Qt.SmoothTransformation
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def edits_save_frame(self):
|
||||||
|
if self.frame is None:
|
||||||
|
return
|
||||||
|
out_path = f"./{datetime.now().isoformat()}.png"
|
||||||
|
self.log.info(f"saving frame: {out_path!r}")
|
||||||
|
img = cv2.cvtColor(self.frame, cv2.COLOR_RGB2BGR)
|
||||||
|
cv2.imwrite(out_path, img)
|
||||||
|
return out_path
|
||||||
22
src/test/uvc_camera.py
Normal file
22
src/test/uvc_camera.py
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
import time
|
||||||
|
|
||||||
|
import cv2
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("Initializing......")
|
||||||
|
|
||||||
|
cap = cv2.VideoCapture(0)
|
||||||
|
num = 10
|
||||||
|
for i in range(num):
|
||||||
|
# Capture frame-by-frame
|
||||||
|
ret, numpy_image = cap.read()
|
||||||
|
img = Image.fromarray(numpy_image, 'RGB')
|
||||||
|
img.show()
|
||||||
|
|
||||||
|
|
||||||
|
cap.release()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Loading…
Reference in New Issue
Block a user