Initial commit

This commit is contained in:
ZockerKatze
2025-02-27 16:04:23 +01:00
commit 623051c116
15 changed files with 2041 additions and 0 deletions

63
.gitignore vendored Normal file
View 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

294
opencam/main.py Normal file
View 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
View 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 "")

View 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="
}
}
}

View 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="
}
}
}

View 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"

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

7
opencamcpp/icon.svg Normal file
View 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
View 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();
}

View 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
View 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