Initial commit
This commit is contained in:
63
.gitignore
vendored
Normal file
63
.gitignore
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
.Python
|
||||
.venv/
|
||||
venv/
|
||||
ENV/
|
||||
|
||||
# C++ build
|
||||
build/
|
||||
out/
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# IDE
|
||||
.vs/
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*.user
|
||||
|
||||
# AppImage specific
|
||||
*.AppImage
|
||||
AppDir/
|
||||
|
||||
# Large model files and datasets
|
||||
*.weights
|
||||
*.h5
|
||||
*.pt
|
||||
*.onnx
|
||||
|
||||
# Temporary files
|
||||
*.log
|
||||
*.tmp
|
||||
|
||||
# Qt specific
|
||||
*.pro.user
|
||||
*.pro.user.*
|
||||
*.qmake.stash
|
||||
moc_*.cpp
|
||||
qrc_*.cpp
|
||||
ui_*.h
|
||||
|
||||
# CMake
|
||||
CMakeCache.txt
|
||||
CMakeFiles/
|
||||
cmake_install.cmake
|
||||
install_manifest.txt
|
||||
compile_commands.json
|
||||
|
||||
# OpenCV build artifacts
|
||||
opencv/
|
||||
opencv_build/
|
||||
|
||||
# System specific
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
115
README.md
Normal file
115
README.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# OpenCam
|
||||
|
||||
Real-time object detection application using OpenCV and YOLO, with both Python and C++ implementations.
|
||||
|
||||
## Features
|
||||
|
||||
- Real-time object detection using YOLOv3
|
||||
- Support for multiple camera inputs
|
||||
- GPU acceleration with CUDA (optional)
|
||||
- Cross-platform support (Windows, Linux)
|
||||
- Modern Qt-based user interface
|
||||
- Portable Linux AppImage build support
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### For C++ Version
|
||||
|
||||
- CMake 3.16 or higher
|
||||
- C++17 compatible compiler
|
||||
- Qt 5.12 or higher
|
||||
- OpenCV 4.x with CUDA support (optional)
|
||||
- CUDA Toolkit 10.0 or higher (optional)
|
||||
|
||||
### For Python Version
|
||||
|
||||
- Python 3.8 or higher
|
||||
- OpenCV-Python
|
||||
- PyQt5
|
||||
- NumPy
|
||||
|
||||
## Installation
|
||||
|
||||
### Building from Source (C++)
|
||||
|
||||
1. Clone the repository:
|
||||
```bash
|
||||
git clone https://github.com/yourusername/opencam.git
|
||||
cd opencam/opencamcpp
|
||||
```
|
||||
|
||||
2. Build OpenCV with CUDA (optional):
|
||||
```bash
|
||||
chmod +x build_opencv.sh
|
||||
./build_opencv.sh
|
||||
```
|
||||
|
||||
3. Build the application:
|
||||
```bash
|
||||
mkdir build && cd build
|
||||
cmake ..
|
||||
make -j$(nproc)
|
||||
```
|
||||
|
||||
### Creating AppImage (Linux)
|
||||
|
||||
1. Ensure all dependencies are installed:
|
||||
```bash
|
||||
sudo apt-get install cmake build-essential qt5-default libopencv-dev librsvg2-bin
|
||||
```
|
||||
|
||||
2. Build the AppImage:
|
||||
```bash
|
||||
chmod +x build_appimage.sh
|
||||
./build_appimage.sh
|
||||
```
|
||||
|
||||
### Python Version Setup
|
||||
|
||||
1. Create a virtual environment:
|
||||
```bash
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate # Linux
|
||||
# or
|
||||
.venv\Scripts\activate # Windows
|
||||
```
|
||||
|
||||
2. Install dependencies:
|
||||
```bash
|
||||
pip install opencv-python pyqt5 numpy
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Running the C++ Version
|
||||
|
||||
```bash
|
||||
./opencam
|
||||
```
|
||||
|
||||
### Running the Python Version
|
||||
|
||||
```bash
|
||||
python main.py
|
||||
```
|
||||
|
||||
## Model Files
|
||||
|
||||
The application requires YOLOv3 model files:
|
||||
- `yolov3.weights`
|
||||
- `yolov3.cfg`
|
||||
- `coco.names`
|
||||
|
||||
Download the weights file from: https://pjreddie.com/media/files/yolov3.weights
|
||||
|
||||
## License
|
||||
|
||||
[Your chosen license]
|
||||
|
||||
## Contributing
|
||||
|
||||
1. Fork the repository
|
||||
2. Create your feature branch
|
||||
3. Commit your changes
|
||||
4. Push to the branch
|
||||
5. Create a new Pull Request
|
||||
80
coco.names
Normal file
80
coco.names
Normal file
@@ -0,0 +1,80 @@
|
||||
person
|
||||
bicycle
|
||||
car
|
||||
motorbike
|
||||
aeroplane
|
||||
bus
|
||||
train
|
||||
truck
|
||||
boat
|
||||
traffic light
|
||||
fire hydrant
|
||||
stop sign
|
||||
parking meter
|
||||
bench
|
||||
bird
|
||||
cat
|
||||
dog
|
||||
horse
|
||||
sheep
|
||||
cow
|
||||
elephant
|
||||
bear
|
||||
zebra
|
||||
giraffe
|
||||
backpack
|
||||
umbrella
|
||||
handbag
|
||||
tie
|
||||
suitcase
|
||||
frisbee
|
||||
skis
|
||||
snowboard
|
||||
sports ball
|
||||
kite
|
||||
baseball bat
|
||||
baseball glove
|
||||
skateboard
|
||||
surfboard
|
||||
tennis racket
|
||||
bottle
|
||||
wine glass
|
||||
cup
|
||||
fork
|
||||
knife
|
||||
spoon
|
||||
bowl
|
||||
banana
|
||||
apple
|
||||
sandwich
|
||||
orange
|
||||
broccoli
|
||||
carrot
|
||||
hot dog
|
||||
pizza
|
||||
donut
|
||||
cake
|
||||
chair
|
||||
sofa
|
||||
pottedplant
|
||||
bed
|
||||
diningtable
|
||||
toilet
|
||||
tvmonitor
|
||||
laptop
|
||||
mouse
|
||||
remote
|
||||
keyboard
|
||||
cell phone
|
||||
microwave
|
||||
oven
|
||||
toaster
|
||||
sink
|
||||
refrigerator
|
||||
book
|
||||
clock
|
||||
vase
|
||||
scissors
|
||||
teddy bear
|
||||
hair drier
|
||||
toothbrush
|
||||
BIN
opencam/image.jpg
Normal file
BIN
opencam/image.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 133 KiB |
294
opencam/main.py
Normal file
294
opencam/main.py
Normal file
@@ -0,0 +1,294 @@
|
||||
import sys
|
||||
import cv2
|
||||
import numpy as np
|
||||
from PyQt5.QtWidgets import (QApplication, QMainWindow, QLabel, QPushButton,
|
||||
QVBoxLayout, QWidget, QComboBox, QMessageBox,
|
||||
QProgressBar, QDialog)
|
||||
from PyQt5.QtGui import QImage, QPixmap
|
||||
from PyQt5.QtCore import QTimer, Qt
|
||||
import os
|
||||
import urllib.request
|
||||
|
||||
class DownloadProgressBar(QDialog):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle("Downloading YOLO Files")
|
||||
self.setFixedSize(400, 100)
|
||||
self.setWindowModality(Qt.ApplicationModal)
|
||||
|
||||
layout = QVBoxLayout()
|
||||
|
||||
self.label = QLabel("Downloading...")
|
||||
layout.addWidget(self.label)
|
||||
|
||||
self.progress = QProgressBar()
|
||||
self.progress.setMinimum(0)
|
||||
self.progress.setMaximum(100)
|
||||
layout.addWidget(self.progress)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
def update_progress(self, current, total):
|
||||
percentage = int((current / total) * 100)
|
||||
self.progress.setValue(percentage)
|
||||
|
||||
def set_file_label(self, filename):
|
||||
self.label.setText(f"Downloading {filename}...")
|
||||
|
||||
class DownloadProgressHandler:
|
||||
def __init__(self, progress_dialog):
|
||||
self.progress_dialog = progress_dialog
|
||||
self.current_size = 0
|
||||
self.total_size = 0
|
||||
|
||||
def handle_progress(self, count, block_size, total_size):
|
||||
self.total_size = total_size
|
||||
self.current_size += block_size
|
||||
self.progress_dialog.update_progress(self.current_size, self.total_size)
|
||||
|
||||
class CameraApp(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle("Object Detection Camera Viewer")
|
||||
self.setGeometry(100, 100, 800, 600)
|
||||
|
||||
self.camera_index = 0
|
||||
self.cap = None
|
||||
self.timer = QTimer()
|
||||
self.timer.timeout.connect(self.update_frame)
|
||||
self.available_cameras = []
|
||||
|
||||
# Initialize object detection
|
||||
self.net = None
|
||||
self.classes = None
|
||||
self.output_layers = None
|
||||
self.load_yolo()
|
||||
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
self.central_widget = QWidget()
|
||||
self.setCentralWidget(self.central_widget)
|
||||
layout = QVBoxLayout()
|
||||
|
||||
self.video_label = QLabel(self)
|
||||
layout.addWidget(self.video_label)
|
||||
|
||||
self.camera_select = QComboBox(self)
|
||||
self.detect_cameras()
|
||||
layout.addWidget(self.camera_select)
|
||||
self.camera_select.currentIndexChanged.connect(self.change_camera)
|
||||
|
||||
self.start_button = QPushButton("Start Camera", self)
|
||||
self.start_button.clicked.connect(self.start_camera)
|
||||
layout.addWidget(self.start_button)
|
||||
|
||||
self.stop_button = QPushButton("Stop Camera", self)
|
||||
self.stop_button.clicked.connect(self.stop_camera)
|
||||
layout.addWidget(self.stop_button)
|
||||
|
||||
self.central_widget.setLayout(layout)
|
||||
|
||||
def detect_cameras(self):
|
||||
self.camera_select.clear()
|
||||
self.available_cameras = []
|
||||
|
||||
# Try to get the list of available cameras using DirectShow backend
|
||||
for i in range(10): # Check first 10 indexes
|
||||
cap = cv2.VideoCapture(i, cv2.CAP_DSHOW)
|
||||
if cap.isOpened():
|
||||
# Get camera name
|
||||
cap.set(cv2.CAP_PROP_SETTINGS, 1) # This might show camera properties dialog
|
||||
name = f"Camera {i}"
|
||||
|
||||
# Try to get camera resolution to verify it's working
|
||||
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
|
||||
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
|
||||
|
||||
if width and height:
|
||||
name = f"{name} ({int(width)}x{int(height)})"
|
||||
self.available_cameras.append(i)
|
||||
self.camera_select.addItem(name, i)
|
||||
|
||||
cap.release()
|
||||
|
||||
if len(self.available_cameras) == 0:
|
||||
QMessageBox.warning(self, "Warning", "No cameras detected!")
|
||||
print("Error: No available cameras detected.")
|
||||
else:
|
||||
print(f"Detected {len(self.available_cameras)} cameras")
|
||||
|
||||
def change_camera(self, index):
|
||||
if index >= 0: # Only change if a valid camera is selected
|
||||
self.camera_index = self.camera_select.itemData(index)
|
||||
if self.cap is not None and self.cap.isOpened():
|
||||
self.stop_camera()
|
||||
self.start_camera()
|
||||
|
||||
def start_camera(self):
|
||||
if self.cap is not None:
|
||||
self.stop_camera()
|
||||
|
||||
try:
|
||||
self.cap = cv2.VideoCapture(self.camera_index, cv2.CAP_DSHOW)
|
||||
if not self.cap.isOpened():
|
||||
QMessageBox.warning(self, "Error", f"Cannot open camera {self.camera_index}")
|
||||
print(f"Error: Cannot open camera {self.camera_index}")
|
||||
return
|
||||
|
||||
# Set camera properties for better performance
|
||||
self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
|
||||
self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
|
||||
self.cap.set(cv2.CAP_PROP_FPS, 30)
|
||||
|
||||
self.timer.start(30)
|
||||
print(f"Started camera {self.camera_index}")
|
||||
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Error", f"Error starting camera: {str(e)}")
|
||||
print(f"Error starting camera: {str(e)}")
|
||||
|
||||
def stop_camera(self):
|
||||
if self.cap:
|
||||
self.timer.stop()
|
||||
self.cap.release()
|
||||
self.cap = None
|
||||
self.video_label.clear()
|
||||
print("Camera stopped")
|
||||
|
||||
def load_yolo(self):
|
||||
# Download YOLO files if they don't exist
|
||||
weights_path = "yolov3.weights"
|
||||
config_path = "yolov3.cfg"
|
||||
classes_path = "coco.names"
|
||||
|
||||
if not all(os.path.exists(f) for f in [weights_path, config_path, classes_path]):
|
||||
QMessageBox.information(self, "Download", "Downloading YOLO model files. This may take a moment...")
|
||||
self.download_yolo_files()
|
||||
|
||||
try:
|
||||
self.net = cv2.dnn.readNet(weights_path, config_path)
|
||||
with open(classes_path, "r") as f:
|
||||
self.classes = [line.strip() for line in f.readlines()]
|
||||
|
||||
layer_names = self.net.getLayerNames()
|
||||
self.output_layers = [layer_names[i - 1] for i in self.net.getUnconnectedOutLayers()]
|
||||
|
||||
print("YOLO model loaded successfully")
|
||||
except Exception as e:
|
||||
print(f"Error loading YOLO model: {str(e)}")
|
||||
QMessageBox.warning(self, "Error", "Failed to load object detection model")
|
||||
|
||||
def download_yolo_files(self):
|
||||
files = {
|
||||
"yolov3.weights": "https://pjreddie.com/media/files/yolov3.weights",
|
||||
"yolov3.cfg": "https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/yolov3.cfg",
|
||||
"coco.names": "https://raw.githubusercontent.com/pjreddie/darknet/master/data/coco.names"
|
||||
}
|
||||
|
||||
progress_dialog = DownloadProgressBar(self)
|
||||
progress_dialog.show()
|
||||
|
||||
for file_name, url in files.items():
|
||||
if not os.path.exists(file_name):
|
||||
print(f"Downloading {file_name}...")
|
||||
progress_dialog.set_file_label(file_name)
|
||||
try:
|
||||
progress_handler = DownloadProgressHandler(progress_dialog)
|
||||
urllib.request.urlretrieve(
|
||||
url,
|
||||
file_name,
|
||||
reporthook=progress_handler.handle_progress
|
||||
)
|
||||
print(f"Downloaded {file_name}")
|
||||
except Exception as e:
|
||||
print(f"Error downloading {file_name}: {str(e)}")
|
||||
QMessageBox.critical(self, "Error", f"Failed to download {file_name}")
|
||||
|
||||
progress_dialog.close()
|
||||
|
||||
def detect_objects(self, frame):
|
||||
if self.net is None or self.classes is None:
|
||||
return frame
|
||||
|
||||
height, width, _ = frame.shape
|
||||
blob = cv2.dnn.blobFromImage(frame, 0.00392, (416, 416), (0, 0, 0), True, crop=False)
|
||||
|
||||
self.net.setInput(blob)
|
||||
outs = self.net.forward(self.output_layers)
|
||||
|
||||
# Showing information on the screen
|
||||
class_ids = []
|
||||
confidences = []
|
||||
boxes = []
|
||||
|
||||
# Showing information on the screen
|
||||
for out in outs:
|
||||
for detection in out:
|
||||
scores = detection[5:]
|
||||
class_id = np.argmax(scores)
|
||||
confidence = scores[class_id]
|
||||
if confidence > 0.5:
|
||||
# Object detected
|
||||
center_x = int(detection[0] * width)
|
||||
center_y = int(detection[1] * height)
|
||||
w = int(detection[2] * width)
|
||||
h = int(detection[3] * height)
|
||||
|
||||
# Rectangle coordinates
|
||||
x = int(center_x - w / 2)
|
||||
y = int(center_y - h / 2)
|
||||
|
||||
boxes.append([x, y, w, h])
|
||||
confidences.append(float(confidence))
|
||||
class_ids.append(class_id)
|
||||
|
||||
indexes = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.4)
|
||||
|
||||
for i in range(len(boxes)):
|
||||
if i in indexes:
|
||||
x, y, w, h = boxes[i]
|
||||
label = str(self.classes[class_ids[i]])
|
||||
confidence = confidences[i]
|
||||
color = (0, 255, 0) # Green
|
||||
cv2.rectangle(frame, (x, y), (x + w, y + h), color, 2)
|
||||
cv2.putText(frame, f"{label} {confidence:.2f}", (x, y - 10),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)
|
||||
|
||||
return frame
|
||||
|
||||
def update_frame(self):
|
||||
if self.cap is None or not self.cap.isOpened():
|
||||
return
|
||||
|
||||
ret, frame = self.cap.read()
|
||||
if ret:
|
||||
# Perform object detection
|
||||
frame = self.detect_objects(frame)
|
||||
|
||||
# Convert the frame from BGR to RGB
|
||||
rgb_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
||||
h, w, ch = rgb_image.shape
|
||||
bytes_per_line = ch * w
|
||||
|
||||
# Convert the frame to QImage
|
||||
q_image = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888)
|
||||
|
||||
# Scale the image to fit the label while maintaining aspect ratio
|
||||
scaled_pixmap = QPixmap.fromImage(q_image).scaled(
|
||||
self.video_label.size(),
|
||||
aspectRatioMode=1 # Qt.KeepAspectRatio
|
||||
)
|
||||
self.video_label.setPixmap(scaled_pixmap)
|
||||
else:
|
||||
print("Failed to get frame from camera")
|
||||
|
||||
def closeEvent(self, event):
|
||||
self.stop_camera()
|
||||
event.accept()
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
window = CameraApp()
|
||||
window.show()
|
||||
sys.exit(app.exec_())
|
||||
137
opencamcpp/CMakeLists.txt
Normal file
137
opencamcpp/CMakeLists.txt
Normal file
@@ -0,0 +1,137 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(opencam LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
|
||||
# Add OpenCV directory to CMAKE_PREFIX_PATH
|
||||
list(APPEND CMAKE_PREFIX_PATH "/usr/local")
|
||||
|
||||
# Find Qt5
|
||||
find_package(Qt5 COMPONENTS
|
||||
Core
|
||||
Gui
|
||||
Widgets
|
||||
Network
|
||||
REQUIRED
|
||||
)
|
||||
|
||||
# Find OpenCV
|
||||
find_package(OpenCV REQUIRED)
|
||||
message(STATUS "OpenCV version: ${OpenCV_VERSION}")
|
||||
message(STATUS "OpenCV libraries: ${OpenCV_LIBS}")
|
||||
message(STATUS "OpenCV include dirs: ${OpenCV_INCLUDE_DIRS}")
|
||||
|
||||
# Check for CUDA support in OpenCV
|
||||
if(";${OpenCV_LIBS};" MATCHES ";opencv_cudaimgproc;")
|
||||
message(STATUS "OpenCV CUDA support found")
|
||||
find_package(CUDA REQUIRED)
|
||||
enable_language(CUDA)
|
||||
set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS};-O3)
|
||||
add_definitions(-DWITH_CUDA)
|
||||
set(HAS_CUDA TRUE)
|
||||
else()
|
||||
message(STATUS "OpenCV was built without CUDA support. GPU acceleration will be disabled.")
|
||||
set(HAS_CUDA FALSE)
|
||||
endif()
|
||||
|
||||
# Add source files
|
||||
add_executable(${PROJECT_NAME}
|
||||
main.cpp
|
||||
)
|
||||
|
||||
# Link libraries
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||
Qt5::Core
|
||||
Qt5::Gui
|
||||
Qt5::Widgets
|
||||
Qt5::Network
|
||||
${OpenCV_LIBS}
|
||||
)
|
||||
|
||||
if(HAS_CUDA)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||
${CUDA_LIBRARIES}
|
||||
)
|
||||
endif()
|
||||
|
||||
# Include directories
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE
|
||||
${OpenCV_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
if(HAS_CUDA)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE
|
||||
${CUDA_INCLUDE_DIRS}
|
||||
)
|
||||
endif()
|
||||
|
||||
# Installation rules
|
||||
install(TARGETS ${PROJECT_NAME}
|
||||
RUNTIME DESTINATION bin
|
||||
COMPONENT applications
|
||||
)
|
||||
|
||||
# Function to safely install a library and its dependencies
|
||||
function(install_library lib_path)
|
||||
if(EXISTS "${lib_path}")
|
||||
file(REAL_PATH "${lib_path}" real_path)
|
||||
install(FILES "${real_path}"
|
||||
DESTINATION lib
|
||||
COMPONENT libraries
|
||||
)
|
||||
|
||||
# Get library dependencies
|
||||
execute_process(
|
||||
COMMAND ldd "${real_path}"
|
||||
OUTPUT_VARIABLE LDD_OUTPUT
|
||||
)
|
||||
|
||||
# Extract and install dependencies
|
||||
string(REGEX MATCHALL "/[^)]*\\.so[^)]*" DEPS "${LDD_OUTPUT}")
|
||||
foreach(dep ${DEPS})
|
||||
if(EXISTS "${dep}" AND NOT "${dep}" MATCHES "^/lib" AND NOT "${dep}" MATCHES "^/usr/lib")
|
||||
install(FILES "${dep}"
|
||||
DESTINATION lib
|
||||
COMPONENT libraries
|
||||
)
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Install OpenCV libraries
|
||||
foreach(lib ${OpenCV_LIBS})
|
||||
get_target_property(lib_location ${lib} LOCATION)
|
||||
if(lib_location)
|
||||
install_library("${lib_location}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Install Qt plugins
|
||||
if(Qt5_DIR)
|
||||
file(GLOB_RECURSE QT_PLUGINS "${Qt5_DIR}/../../../plugins/*.so")
|
||||
foreach(plugin ${QT_PLUGINS})
|
||||
get_filename_component(plugin_path "${plugin}" DIRECTORY)
|
||||
get_filename_component(plugin_dir "${plugin_path}" NAME)
|
||||
install(FILES "${plugin}"
|
||||
DESTINATION plugins/${plugin_dir}
|
||||
COMPONENT plugins
|
||||
)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# Print configuration summary
|
||||
message(STATUS "")
|
||||
message(STATUS "Configuration Summary")
|
||||
message(STATUS "--------------------")
|
||||
message(STATUS "OpenCV version: ${OpenCV_VERSION}")
|
||||
message(STATUS "CUDA support: ${HAS_CUDA}")
|
||||
message(STATUS "Qt version: ${Qt5_VERSION}")
|
||||
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
|
||||
message(STATUS "C++ compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}")
|
||||
message(STATUS "Installation prefix: ${CMAKE_INSTALL_PREFIX}")
|
||||
message(STATUS "")
|
||||
22
opencamcpp/CMakePresets.json
Normal file
22
opencamcpp/CMakePresets.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"version": 3,
|
||||
"configurePresets": [
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "Qt",
|
||||
"cacheVariables": {
|
||||
"CMAKE_PREFIX_PATH": "$env{QTDIR}"
|
||||
},
|
||||
"vendor": {
|
||||
"qt-project.org/Qt": {
|
||||
"checksum": "wVa86FgEkvdCTVp1/nxvrkaemJc="
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"vendor": {
|
||||
"qt-project.org/Presets": {
|
||||
"checksum": "67SmY24ZeVbebyKD0fGfIzb/bGI="
|
||||
}
|
||||
}
|
||||
}
|
||||
40
opencamcpp/CMakeUserPresets.json
Normal file
40
opencamcpp/CMakeUserPresets.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"version": 3,
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "Qt-Debug",
|
||||
"inherits": "Qt-Default",
|
||||
"binaryDir": "${sourceDir}/out/build/debug",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"CMAKE_CXX_FLAGS": "-DQT_QML_DEBUG"
|
||||
},
|
||||
"environment": {
|
||||
"QML_DEBUG_ARGS": "-qmljsdebugger=file:{ff00bfed-29c2-47f8-b4aa-00b2873b1a26},block"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Qt-Release",
|
||||
"inherits": "Qt-Default",
|
||||
"binaryDir": "${sourceDir}/out/build/release",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release"
|
||||
}
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "Qt-Default",
|
||||
"inherits": null,
|
||||
"vendor": {
|
||||
"qt-project.org/Default": {
|
||||
"checksum": "VoalogTkyWuFomeO1TLFx0olLJ4="
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"vendor": {
|
||||
"qt-project.org/Presets": {
|
||||
"checksum": "azRZtZDqJVYwlIJYZufPfOYPwkE="
|
||||
}
|
||||
}
|
||||
}
|
||||
90
opencamcpp/build_appimage.sh
Normal file
90
opencamcpp/build_appimage.sh
Normal file
@@ -0,0 +1,90 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Exit on error
|
||||
set -e
|
||||
|
||||
# Function to copy library and its dependencies
|
||||
copy_dependencies() {
|
||||
local binary="$1"
|
||||
local target_dir="$2"
|
||||
|
||||
# Create target directory if it doesn't exist
|
||||
mkdir -p "$target_dir"
|
||||
|
||||
# Get all dependencies
|
||||
ldd "$binary" | while read -r line; do
|
||||
# Extract the library path
|
||||
if [[ $line =~ '=>' ]]; then
|
||||
lib_path=$(echo "$line" | awk '{print $3}')
|
||||
else
|
||||
lib_path=$(echo "$line" | awk '{print $1}')
|
||||
fi
|
||||
|
||||
# Skip system libraries and non-existent files
|
||||
if [[ -f "$lib_path" && ! "$lib_path" =~ ^/lib && ! "$lib_path" =~ ^/usr/lib ]]; then
|
||||
cp -L "$lib_path" "$target_dir/"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Create AppDir structure
|
||||
mkdir -p AppDir/usr/{bin,lib,share/applications,share/icons/hicolor/256x256/apps}
|
||||
|
||||
# Build the application
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release
|
||||
make -j$(nproc)
|
||||
|
||||
# Install to AppDir
|
||||
make DESTDIR=../AppDir install
|
||||
cd ..
|
||||
|
||||
# Copy desktop file and icon
|
||||
cp opencam.desktop AppDir/usr/share/applications/
|
||||
cp icon.png AppDir/usr/share/icons/hicolor/256x256/apps/opencam.png
|
||||
|
||||
# Copy Qt plugins
|
||||
mkdir -p AppDir/usr/plugins
|
||||
for plugin_dir in platforms imageformats xcbglintegrations; do
|
||||
if [ -d "/usr/lib/x86_64-linux-gnu/qt5/plugins/$plugin_dir" ]; then
|
||||
cp -r "/usr/lib/x86_64-linux-gnu/qt5/plugins/$plugin_dir" AppDir/usr/plugins/
|
||||
fi
|
||||
done
|
||||
|
||||
# Copy dependencies for the main executable
|
||||
copy_dependencies "AppDir/usr/bin/opencam" "AppDir/usr/lib"
|
||||
|
||||
# Copy dependencies for Qt plugins
|
||||
find AppDir/usr/plugins -type f -name "*.so" | while read plugin; do
|
||||
copy_dependencies "$plugin" "AppDir/usr/lib"
|
||||
done
|
||||
|
||||
# Create AppRun script
|
||||
cat > AppDir/AppRun << 'EOF'
|
||||
#!/bin/bash
|
||||
HERE="$(dirname "$(readlink -f "${0}")")"
|
||||
export LD_LIBRARY_PATH="${HERE}/usr/lib:${LD_LIBRARY_PATH}"
|
||||
export QT_PLUGIN_PATH="${HERE}/usr/plugins"
|
||||
export QT_QPA_PLATFORM_PLUGIN_PATH="${HERE}/usr/plugins/platforms"
|
||||
exec "${HERE}/usr/bin/opencam" "$@"
|
||||
EOF
|
||||
|
||||
chmod +x AppDir/AppRun
|
||||
|
||||
# Download linuxdeploy if not present
|
||||
if [ ! -f linuxdeploy-x86_64.AppImage ]; then
|
||||
wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
|
||||
chmod +x linuxdeploy-x86_64.AppImage
|
||||
fi
|
||||
|
||||
if [ ! -f linuxdeploy-plugin-qt-x86_64.AppImage ]; then
|
||||
wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage
|
||||
chmod +x linuxdeploy-plugin-qt-x86_64.AppImage
|
||||
fi
|
||||
|
||||
# Create the AppImage
|
||||
export OUTPUT="OpenCam-x86_64.AppImage"
|
||||
./linuxdeploy-x86_64.AppImage --appdir AppDir --plugin qt --output appimage
|
||||
|
||||
echo "AppImage created successfully: $OUTPUT"
|
||||
83
opencamcpp/build_opencv.sh
Normal file
83
opencamcpp/build_opencv.sh
Normal file
@@ -0,0 +1,83 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Exit on error
|
||||
set -e
|
||||
|
||||
# Function to check CUDA installation
|
||||
check_cuda() {
|
||||
if ! command -v nvcc &> /dev/null; then
|
||||
echo "CUDA not found! Please install CUDA toolkit first."
|
||||
exit 1
|
||||
fi
|
||||
echo "Found CUDA installation: $(nvcc --version | head -n1)"
|
||||
}
|
||||
|
||||
# Function to check GPU
|
||||
check_gpu() {
|
||||
if ! command -v nvidia-smi &> /dev/null; then
|
||||
echo "No NVIDIA GPU found or drivers not installed!"
|
||||
exit 1
|
||||
fi
|
||||
echo "Found GPU: $(nvidia-smi -L)"
|
||||
}
|
||||
|
||||
# Check CUDA and GPU
|
||||
check_cuda
|
||||
check_gpu
|
||||
|
||||
# Get CUDA compute capability
|
||||
CUDA_ARCH=$(nvidia-smi --query-gpu=compute_cap --format=csv,noheader)
|
||||
echo "GPU Compute Capability: $CUDA_ARCH"
|
||||
|
||||
# Create build directory
|
||||
cd ~/opencv_build/opencv
|
||||
mkdir -p build
|
||||
cd build
|
||||
|
||||
# Configure OpenCV build
|
||||
cmake -D CMAKE_BUILD_TYPE=RELEASE \
|
||||
-D CMAKE_INSTALL_PREFIX=/usr/local \
|
||||
-D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib/modules \
|
||||
-D WITH_CUDA=ON \
|
||||
-D WITH_CUDNN=OFF \
|
||||
-D OPENCV_DNN_CUDA=ON \
|
||||
-D ENABLE_FAST_MATH=1 \
|
||||
-D CUDA_FAST_MATH=1 \
|
||||
-D CUDA_ARCH_BIN=$CUDA_ARCH \
|
||||
-D WITH_CUBLAS=1 \
|
||||
-D WITH_TBB=ON \
|
||||
-D WITH_V4L=ON \
|
||||
-D WITH_QT=ON \
|
||||
-D WITH_OPENGL=ON \
|
||||
-D WITH_GSTREAMER=ON \
|
||||
-D OPENCV_GENERATE_PKGCONFIG=ON \
|
||||
-D OPENCV_PC_FILE_NAME=opencv.pc \
|
||||
-D OPENCV_ENABLE_NONFREE=ON \
|
||||
-D INSTALL_PYTHON_EXAMPLES=OFF \
|
||||
-D INSTALL_C_EXAMPLES=OFF \
|
||||
-D BUILD_EXAMPLES=OFF \
|
||||
-D BUILD_opencv_cudacodec=OFF \
|
||||
-D OPENCV_ENABLE_MEMALIGN=ON \
|
||||
-D WITH_OPENEXR=ON \
|
||||
-D OPENCV_ENABLE_MEMALIGN=ON \
|
||||
-D WITH_EIGEN=ON \
|
||||
-D ENABLE_PRECOMPILED_HEADERS=OFF \
|
||||
..
|
||||
|
||||
# Build using all available CPU cores
|
||||
make -j$(nproc)
|
||||
|
||||
# Install
|
||||
sudo make install
|
||||
sudo ldconfig
|
||||
|
||||
# Print OpenCV installation information
|
||||
echo "OpenCV installation completed!"
|
||||
echo "OpenCV version: $(pkg-config --modversion opencv4)"
|
||||
echo "Installation location: $(pkg-config --variable=prefix opencv4)"
|
||||
|
||||
# Create a pkg-config path file
|
||||
sudo sh -c 'echo "/usr/local/lib/pkgconfig" > /etc/ld.so.conf.d/opencv.conf'
|
||||
sudo ldconfig
|
||||
|
||||
echo "Setup complete! You can now use OpenCV with CUDA support."
|
||||
BIN
opencamcpp/icon.png
Normal file
BIN
opencamcpp/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
7
opencamcpp/icon.svg
Normal file
7
opencamcpp/icon.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" viewBox="0 0 256 256">
|
||||
<circle cx="128" cy="128" r="120" fill="#2196F3"/>
|
||||
<circle cx="128" cy="128" r="90" fill="#1976D2"/>
|
||||
<circle cx="128" cy="128" r="45" fill="#FFFFFF"/>
|
||||
<circle cx="128" cy="128" r="25" fill="#1976D2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 359 B |
311
opencamcpp/main.cpp
Normal file
311
opencamcpp/main.cpp
Normal file
@@ -0,0 +1,311 @@
|
||||
#include <opencv2/opencv.hpp>
|
||||
#ifdef WITH_CUDA
|
||||
#include <opencv2/cudaimgproc.hpp>
|
||||
#include <opencv2/cudawarping.hpp>
|
||||
#include <opencv2/cudaobjdetect.hpp>
|
||||
#include <opencv2/cudabgsegm.hpp>
|
||||
#include <opencv2/cudacodec.hpp>
|
||||
#include <opencv2/core/cuda.hpp>
|
||||
#endif
|
||||
#include <QApplication>
|
||||
#include <QMainWindow>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <QComboBox>
|
||||
#include <QMessageBox>
|
||||
#include <QProgressDialog>
|
||||
#include <QTimer>
|
||||
#include <QThread>
|
||||
#include <QString>
|
||||
#include <QFile>
|
||||
#include <QProgressBar>
|
||||
#include <QDialog>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
class FrameProcessor : public QThread {
|
||||
Q_OBJECT
|
||||
public:
|
||||
FrameProcessor(QObject* parent = nullptr) : QThread(parent), running(false) {
|
||||
#ifdef WITH_CUDA
|
||||
// Initialize CUDA device
|
||||
cv::cuda::setDevice(0);
|
||||
stream = cv::cuda::Stream();
|
||||
#endif
|
||||
}
|
||||
|
||||
void startProcessing() {
|
||||
running = true;
|
||||
if (!isRunning()) start();
|
||||
}
|
||||
|
||||
void stopProcessing() {
|
||||
running = false;
|
||||
wait();
|
||||
}
|
||||
|
||||
void setNet(cv::dnn::Net& net) {
|
||||
std::lock_guard<std::mutex> lock(netMutex);
|
||||
this->net = net;
|
||||
}
|
||||
|
||||
void queueFrame(const cv::Mat& frame) {
|
||||
std::lock_guard<std::mutex> lock(queueMutex);
|
||||
frameQueue.push(frame);
|
||||
condition.notify_one();
|
||||
}
|
||||
|
||||
signals:
|
||||
void frameProcessed(QImage image);
|
||||
|
||||
protected:
|
||||
void run() override {
|
||||
while (running) {
|
||||
cv::Mat frame;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queueMutex);
|
||||
condition.wait(lock, [this] { return !frameQueue.empty() || !running; });
|
||||
if (!running) break;
|
||||
frame = frameQueue.front();
|
||||
frameQueue.pop();
|
||||
}
|
||||
|
||||
if (frame.empty()) continue;
|
||||
|
||||
#ifdef WITH_CUDA
|
||||
// GPU processing path
|
||||
cv::cuda::GpuMat gpuFrame;
|
||||
gpuFrame.upload(frame, stream);
|
||||
|
||||
cv::cuda::GpuMat gpuResized;
|
||||
cv::cuda::resize(gpuFrame, gpuResized, cv::Size(416, 416), 0, 0, cv::INTER_CUBIC, stream);
|
||||
|
||||
// Download for DNN processing (until we implement CUDA DNN)
|
||||
cv::Mat resized;
|
||||
gpuResized.download(resized, stream);
|
||||
#else
|
||||
// CPU processing path
|
||||
cv::Mat resized;
|
||||
cv::resize(frame, resized, cv::Size(416, 416), 0, 0, cv::INTER_CUBIC);
|
||||
#endif
|
||||
|
||||
// Object detection
|
||||
cv::Mat blob;
|
||||
cv::dnn::blobFromImage(resized, blob, 1/255.0, cv::Size(416, 416), cv::Scalar(0,0,0), true, false);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(netMutex);
|
||||
net.setInput(blob);
|
||||
std::vector<cv::Mat> outs;
|
||||
net.forward(outs, net.getUnconnectedOutLayersNames());
|
||||
|
||||
std::vector<int> classIds;
|
||||
std::vector<float> confidences;
|
||||
std::vector<cv::Rect> boxes;
|
||||
|
||||
for (const auto& out : outs) {
|
||||
float* data = (float*)out.data;
|
||||
for (int j = 0; j < out.rows; ++j, data += out.cols) {
|
||||
cv::Mat scores = out.row(j).colRange(5, out.cols);
|
||||
cv::Point classIdPoint;
|
||||
double confidence;
|
||||
cv::minMaxLoc(scores, 0, &confidence, 0, &classIdPoint);
|
||||
|
||||
if (confidence > 0.5) {
|
||||
int centerX = (int)(data[0] * frame.cols);
|
||||
int centerY = (int)(data[1] * frame.rows);
|
||||
int width = (int)(data[2] * frame.cols);
|
||||
int height = (int)(data[3] * frame.rows);
|
||||
int left = centerX - width / 2;
|
||||
int top = centerY - height / 2;
|
||||
|
||||
classIds.push_back(classIdPoint.x);
|
||||
confidences.push_back((float)confidence);
|
||||
boxes.push_back(cv::Rect(left, top, width, height));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> indices;
|
||||
cv::dnn::NMSBoxes(boxes, confidences, 0.5, 0.4, indices);
|
||||
|
||||
for (size_t i = 0; i < indices.size(); ++i) {
|
||||
int idx = indices[i];
|
||||
cv::Rect box = boxes[idx];
|
||||
cv::rectangle(frame, box, cv::Scalar(0, 255, 0), 2);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WITH_CUDA
|
||||
// Convert to RGB on GPU
|
||||
cv::cuda::GpuMat gpuRGB;
|
||||
cv::cuda::cvtColor(gpuFrame, gpuRGB, cv::COLOR_BGR2RGB, 0, stream);
|
||||
cv::Mat rgb;
|
||||
gpuRGB.download(rgb, stream);
|
||||
#else
|
||||
// Convert to RGB on CPU
|
||||
cv::Mat rgb;
|
||||
cv::cvtColor(frame, rgb, cv::COLOR_BGR2RGB);
|
||||
#endif
|
||||
|
||||
// Convert to QImage
|
||||
QImage qimg(rgb.data, rgb.cols, rgb.rows, rgb.step, QImage::Format_RGB888);
|
||||
emit frameProcessed(qimg.copy());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<bool> running;
|
||||
#ifdef WITH_CUDA
|
||||
cv::cuda::Stream stream;
|
||||
#endif
|
||||
cv::dnn::Net net;
|
||||
std::queue<cv::Mat> frameQueue;
|
||||
std::mutex queueMutex;
|
||||
std::mutex netMutex;
|
||||
std::condition_variable condition;
|
||||
};
|
||||
|
||||
class CameraApp : public QMainWindow {
|
||||
Q_OBJECT
|
||||
public:
|
||||
CameraApp(QWidget* parent = nullptr) : QMainWindow(parent) {
|
||||
setWindowTitle("GPU-Accelerated Object Detection");
|
||||
setGeometry(100, 100, 1280, 720);
|
||||
|
||||
#ifdef WITH_CUDA
|
||||
// Check CUDA device
|
||||
int deviceCount = cv::cuda::getCudaEnabledDeviceCount();
|
||||
if (deviceCount == 0) {
|
||||
QMessageBox::warning(this, "Warning", "No CUDA capable devices found. Falling back to CPU processing.");
|
||||
} else {
|
||||
cv::cuda::printCudaDeviceInfo(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
setupUI();
|
||||
initializeObjectDetection();
|
||||
processor = new FrameProcessor(this);
|
||||
connect(processor, &FrameProcessor::frameProcessed, this, &CameraApp::updateFrame);
|
||||
}
|
||||
|
||||
~CameraApp() {
|
||||
stopCamera();
|
||||
if (processor) {
|
||||
processor->stopProcessing();
|
||||
delete processor;
|
||||
}
|
||||
}
|
||||
|
||||
private slots:
|
||||
void startCamera() {
|
||||
if (!cap.isOpened()) {
|
||||
cap.open(currentCamera);
|
||||
cap.set(cv::CAP_PROP_FRAME_WIDTH, 1920);
|
||||
cap.set(cv::CAP_PROP_FRAME_HEIGHT, 1080);
|
||||
cap.set(cv::CAP_PROP_FPS, 120); // Request maximum FPS
|
||||
|
||||
#ifdef WITH_CUDA
|
||||
// Try to use CUDA video decoder if available
|
||||
cap.set(cv::CAP_PROP_CUDA_DEVICE, 0);
|
||||
#endif
|
||||
|
||||
if (!cap.isOpened()) {
|
||||
QMessageBox::critical(this, "Error", "Failed to open camera!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
processor->startProcessing();
|
||||
timer->start(0); // Run as fast as possible
|
||||
}
|
||||
|
||||
void stopCamera() {
|
||||
timer->stop();
|
||||
processor->stopProcessing();
|
||||
if (cap.isOpened()) {
|
||||
cap.release();
|
||||
}
|
||||
}
|
||||
|
||||
void captureFrame() {
|
||||
if (!cap.isOpened()) return;
|
||||
|
||||
cv::Mat frame;
|
||||
cap >> frame;
|
||||
if (frame.empty()) return;
|
||||
|
||||
processor->queueFrame(frame);
|
||||
}
|
||||
|
||||
void updateFrame(const QImage& image) {
|
||||
QPixmap pixmap = QPixmap::fromImage(image);
|
||||
videoLabel->setPixmap(pixmap.scaled(videoLabel->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||
}
|
||||
|
||||
private:
|
||||
void setupUI() {
|
||||
QWidget* centralWidget = new QWidget(this);
|
||||
setCentralWidget(centralWidget);
|
||||
QVBoxLayout* layout = new QVBoxLayout(centralWidget);
|
||||
|
||||
videoLabel = new QLabel(this);
|
||||
videoLabel->setMinimumSize(640, 480);
|
||||
layout->addWidget(videoLabel);
|
||||
|
||||
cameraSelect = new QComboBox(this);
|
||||
detectCameras();
|
||||
layout->addWidget(cameraSelect);
|
||||
|
||||
startButton = new QPushButton("Start Camera", this);
|
||||
connect(startButton, &QPushButton::clicked, this, &CameraApp::startCamera);
|
||||
layout->addWidget(startButton);
|
||||
|
||||
stopButton = new QPushButton("Stop Camera", this);
|
||||
connect(stopButton, &QPushButton::clicked, this, &CameraApp::stopCamera);
|
||||
layout->addWidget(stopButton);
|
||||
|
||||
timer = new QTimer(this);
|
||||
connect(timer, &QTimer::timeout, this, &CameraApp::captureFrame);
|
||||
}
|
||||
|
||||
void detectCameras() {
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
cv::VideoCapture temp(i);
|
||||
if (temp.isOpened()) {
|
||||
cameraSelect->addItem("Camera " + QString::number(i), i);
|
||||
temp.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void initializeObjectDetection() {
|
||||
// Download and load YOLO model (implementation similar to Python version)
|
||||
// ...
|
||||
}
|
||||
|
||||
QLabel* videoLabel;
|
||||
QComboBox* cameraSelect;
|
||||
QPushButton* startButton;
|
||||
QPushButton* stopButton;
|
||||
QTimer* timer;
|
||||
cv::VideoCapture cap;
|
||||
int currentCamera = 0;
|
||||
FrameProcessor* processor;
|
||||
};
|
||||
|
||||
#include "main.moc"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
QApplication app(argc, argv);
|
||||
CameraApp window;
|
||||
window.show();
|
||||
return app.exec();
|
||||
}
|
||||
10
opencamcpp/opencam.desktop
Normal file
10
opencamcpp/opencam.desktop
Normal file
@@ -0,0 +1,10 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=OpenCam
|
||||
Comment=Real-time object detection using OpenCV and YOLO
|
||||
Exec=opencam
|
||||
Icon=opencam
|
||||
Categories=Graphics;Video;
|
||||
Keywords=camera;detection;opencv;yolo;
|
||||
Terminal=false
|
||||
StartupNotify=true
|
||||
789
yolov3.cfg
Normal file
789
yolov3.cfg
Normal file
@@ -0,0 +1,789 @@
|
||||
[net]
|
||||
# Testing
|
||||
# batch=1
|
||||
# subdivisions=1
|
||||
# Training
|
||||
batch=64
|
||||
subdivisions=16
|
||||
width=608
|
||||
height=608
|
||||
channels=3
|
||||
momentum=0.9
|
||||
decay=0.0005
|
||||
angle=0
|
||||
saturation = 1.5
|
||||
exposure = 1.5
|
||||
hue=.1
|
||||
|
||||
learning_rate=0.001
|
||||
burn_in=1000
|
||||
max_batches = 500200
|
||||
policy=steps
|
||||
steps=400000,450000
|
||||
scales=.1,.1
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=32
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
# Downsample
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=64
|
||||
size=3
|
||||
stride=2
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=32
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=64
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[shortcut]
|
||||
from=-3
|
||||
activation=linear
|
||||
|
||||
# Downsample
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=128
|
||||
size=3
|
||||
stride=2
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=64
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=128
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[shortcut]
|
||||
from=-3
|
||||
activation=linear
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=64
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=128
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[shortcut]
|
||||
from=-3
|
||||
activation=linear
|
||||
|
||||
# Downsample
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=256
|
||||
size=3
|
||||
stride=2
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=128
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=256
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[shortcut]
|
||||
from=-3
|
||||
activation=linear
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=128
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=256
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[shortcut]
|
||||
from=-3
|
||||
activation=linear
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=128
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=256
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[shortcut]
|
||||
from=-3
|
||||
activation=linear
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=128
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=256
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[shortcut]
|
||||
from=-3
|
||||
activation=linear
|
||||
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=128
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=256
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[shortcut]
|
||||
from=-3
|
||||
activation=linear
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=128
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=256
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[shortcut]
|
||||
from=-3
|
||||
activation=linear
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=128
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=256
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[shortcut]
|
||||
from=-3
|
||||
activation=linear
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=128
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=256
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[shortcut]
|
||||
from=-3
|
||||
activation=linear
|
||||
|
||||
# Downsample
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=512
|
||||
size=3
|
||||
stride=2
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=256
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=512
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[shortcut]
|
||||
from=-3
|
||||
activation=linear
|
||||
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=256
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=512
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[shortcut]
|
||||
from=-3
|
||||
activation=linear
|
||||
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=256
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=512
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[shortcut]
|
||||
from=-3
|
||||
activation=linear
|
||||
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=256
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=512
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[shortcut]
|
||||
from=-3
|
||||
activation=linear
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=256
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=512
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[shortcut]
|
||||
from=-3
|
||||
activation=linear
|
||||
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=256
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=512
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[shortcut]
|
||||
from=-3
|
||||
activation=linear
|
||||
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=256
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=512
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[shortcut]
|
||||
from=-3
|
||||
activation=linear
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=256
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=512
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[shortcut]
|
||||
from=-3
|
||||
activation=linear
|
||||
|
||||
# Downsample
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=1024
|
||||
size=3
|
||||
stride=2
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=512
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=1024
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[shortcut]
|
||||
from=-3
|
||||
activation=linear
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=512
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=1024
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[shortcut]
|
||||
from=-3
|
||||
activation=linear
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=512
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=1024
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[shortcut]
|
||||
from=-3
|
||||
activation=linear
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=512
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=1024
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[shortcut]
|
||||
from=-3
|
||||
activation=linear
|
||||
|
||||
######################
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=512
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
filters=1024
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=512
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
filters=1024
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=512
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
filters=1024
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
filters=255
|
||||
activation=linear
|
||||
|
||||
|
||||
[yolo]
|
||||
mask = 6,7,8
|
||||
anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326
|
||||
classes=80
|
||||
num=9
|
||||
jitter=.3
|
||||
ignore_thresh = .7
|
||||
truth_thresh = 1
|
||||
random=1
|
||||
|
||||
|
||||
[route]
|
||||
layers = -4
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=256
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[upsample]
|
||||
stride=2
|
||||
|
||||
[route]
|
||||
layers = -1, 61
|
||||
|
||||
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=256
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
filters=512
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=256
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
filters=512
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=256
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
filters=512
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
filters=255
|
||||
activation=linear
|
||||
|
||||
|
||||
[yolo]
|
||||
mask = 3,4,5
|
||||
anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326
|
||||
classes=80
|
||||
num=9
|
||||
jitter=.3
|
||||
ignore_thresh = .7
|
||||
truth_thresh = 1
|
||||
random=1
|
||||
|
||||
|
||||
|
||||
[route]
|
||||
layers = -4
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=128
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[upsample]
|
||||
stride=2
|
||||
|
||||
[route]
|
||||
layers = -1, 36
|
||||
|
||||
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=128
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
filters=256
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=128
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
filters=256
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=128
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
filters=256
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
filters=255
|
||||
activation=linear
|
||||
|
||||
|
||||
[yolo]
|
||||
mask = 0,1,2
|
||||
anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326
|
||||
classes=80
|
||||
num=9
|
||||
jitter=.3
|
||||
ignore_thresh = .7
|
||||
truth_thresh = 1
|
||||
random=1
|
||||
|
||||
Reference in New Issue
Block a user