├── README.md ├── OpenCV_Webcam ├── Main.h ├── OpenCV_Webcam.vcxproj.filters ├── Main.cpp └── OpenCV_Webcam.vcxproj └── OpenCV_Webcam.sln /README.md: -------------------------------------------------------------------------------- 1 | # OpenCV-Webcam 2 | A simple OpenCV learning project to stream video from a webcam. 3 | 4 | Has an option to save a frame to disk every n seconds. Press H to enable background subtraction, F toggles Morphological filtering of foreground mask. 5 | 6 | Hackishly supports passed commandline parameters. First paramter is frame save interval, second the ID of the camera device to use and the third the window scaling factor. -------------------------------------------------------------------------------- /OpenCV_Webcam/Main.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "opencv2\opencv.hpp" 3 | #include 4 | #include 5 | 6 | int main(int argc, char* argv[]); 7 | 8 | // Handle commandline arguments 9 | void handleArgs(int argc, char* argv[]); 10 | 11 | // Timestamp an image 12 | void timestamp(cv::Mat * image_to_stamp); 13 | 14 | // Print a message on a image 15 | void drawMessage(cv::Mat inframe, cv::OutputArray outframe, char message[]); 16 | 17 | // Draw window update logic 18 | int updateWindow(cv::VideoCapture * capture_stream); -------------------------------------------------------------------------------- /OpenCV_Webcam.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.24720.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OpenCV_Webcam", "OpenCV_Webcam\OpenCV_Webcam.vcxproj", "{E0A567F4-E466-4116-819A-5081DB6900B8}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Release|x64 = Release|x64 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {E0A567F4-E466-4116-819A-5081DB6900B8}.Debug|x64.ActiveCfg = Debug|x64 15 | {E0A567F4-E466-4116-819A-5081DB6900B8}.Debug|x64.Build.0 = Debug|x64 16 | {E0A567F4-E466-4116-819A-5081DB6900B8}.Release|x64.ActiveCfg = Release|x64 17 | {E0A567F4-E466-4116-819A-5081DB6900B8}.Release|x64.Build.0 = Release|x64 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /OpenCV_Webcam/OpenCV_Webcam.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | 23 | 24 | Source Files 25 | 26 | 27 | -------------------------------------------------------------------------------- /OpenCV_Webcam/Main.cpp: -------------------------------------------------------------------------------- 1 | #include "Main.h" 2 | 3 | bool saveFrames = false; // Should we save frames at every n interval? 4 | unsigned int savedFrameInterval = 30; // How many seconds inbetween frame captures 5 | int webcamID = 0; // What device number is your webcam 6 | float windowscale = 1; // Scale of render window 7 | bool backgroundTrainOnInit = true; // Enable training of MOG2 background subtraction model using only the first n frames 8 | int backgroundTrainTime = 80; // Time to train for 9 | 10 | int main(int argc, char* argv[]) 11 | { 12 | cv::VideoCapture captureStream; 13 | int updateStatus; 14 | 15 | // Handle any passed args 16 | if (argc > 0) 17 | handleArgs(argc, argv); 18 | 19 | // Start the camera stream if valid 20 | captureStream.open(webcamID); 21 | if (!captureStream.isOpened()) 22 | { 23 | std::cout << "Failed to get webcam." << std::endl; 24 | return -1; 25 | } 26 | 27 | // Run the main update logic and save the resulting run code once finished 28 | updateStatus = updateWindow(&captureStream); 29 | 30 | //Error printout 31 | switch (updateStatus) 32 | { 33 | case -1: 34 | std::cout << "Failed to draw: frame data was empty." << std::endl; 35 | break; 36 | default: 37 | break; 38 | } 39 | 40 | return updateStatus; 41 | } 42 | 43 | void handleArgs(int argc, char* argv[]) 44 | { 45 | // Future notes: look into getopt (Unix!!!) or stdarg.h (Win) 46 | // Definity replace alll thiiss with something more legit e.g. ^^^ becuase good god these indexes are all over the place 47 | 48 | for (int i = 0; i < argc; i++) 49 | { 50 | switch (i) 51 | { 52 | case 0: // Save frame interval, negitive numbers disable 53 | savedFrameInterval = std::atoi(argv[0]); 54 | if (savedFrameInterval < 1) 55 | { 56 | savedFrameInterval = 60; 57 | saveFrames = false; 58 | } 59 | break; 60 | case 1: // ID of camera 61 | webcamID = std::atoi(argv[2]); 62 | break; 63 | case 2: // Window scale 64 | windowscale = std::atof(argv[3]); 65 | break; 66 | default: 67 | break; 68 | } 69 | } 70 | } 71 | 72 | void timestamp(cv::Mat * frame) 73 | { 74 | std::time_t epochtime; 75 | char timestr[26]; 76 | std::tm timestruct; 77 | 78 | // Get the time and update the timestr 79 | epochtime = std::time(nullptr); 80 | localtime_s(×truct, &epochtime); 81 | asctime_s(timestr, sizeof timestr, ×truct); 82 | timestr[24] = '\0'; // Clear the last character, else opencv's putText draws it as an unknown 83 | 84 | // Draw the timestamp 85 | cv::rectangle(*frame, cvPoint(0, frame->size().height), cvPoint((sizeof(timestr) - 1) * 8 + 1, frame->size().height - 13), cvScalar(0, 0, 0), CV_FILLED, 8, 0); 86 | cv::putText(*frame, timestr, cvPoint(0, frame->size().height - 2.0), CV_FONT_HERSHEY_PLAIN, 0.9, cvScalar(255, 255, 255), 1, 8, false); 87 | } 88 | 89 | void drawMessage(cv::Mat inframe, cv::OutputArray outframe, char message[]) 90 | { 91 | // Draw the timestamp 92 | cv::rectangle(inframe, cvPoint(0, inframe.size().height), cvPoint((sizeof(message) - 1) * 8 + 1, inframe.size().height - 13), cvScalar(0, 0, 0), CV_FILLED, 8, 0); 93 | cv::putText(inframe, message, cvPoint(0, inframe.size().height - 2.0), CV_FONT_HERSHEY_PLAIN, 0.9, cvScalar(255, 255, 255), 1, 8, false); 94 | inframe.copyTo(outframe); 95 | } 96 | 97 | int updateWindow(cv::VideoCapture * captureStream) 98 | { 99 | // Time stuff 100 | std::time_t epochtime; 101 | std::tm timestruct; 102 | 103 | // frame stuff 104 | cv::Mat * frame = new cv::Mat(); 105 | bool savedFrame = false; 106 | 107 | // background subtraction 108 | cv::Ptr bsMOG2; 109 | cv::Mat fgMask; 110 | int bgsubTrainTime; 111 | bool bgsubEnabled = false; 112 | bool bgFiltered = true; 113 | int filterSize = 2; 114 | 115 | // Loop until we hit ESC 116 | while (true) 117 | { 118 | epochtime = std::time(nullptr); 119 | 120 | // Convert to local time 121 | localtime_s(×truct, &epochtime); 122 | 123 | // Get the camera frame 124 | *captureStream >> *frame; 125 | 126 | // Ensure the frame is valid 127 | if (frame->empty()) 128 | return -1; 129 | 130 | // If foreground detection is enabled perform the operations 131 | if (bgsubEnabled) 132 | { 133 | cv::Mat fgImage; 134 | cv::Mat bgImage = cv::imread("./beach.jpg", 1); 135 | 136 | // Perform the MOG2 function depending on training options 137 | if (!backgroundTrainOnInit) 138 | { 139 | // Training is disabled, run the function with default settings 140 | bsMOG2->apply(*frame, fgMask); 141 | } 142 | else if(bgsubTrainTime > 0) 143 | { 144 | // Training is enabled but not yet run, train for the n amount of time 145 | bsMOG2->apply(*frame, fgMask, 0.5); 146 | bgsubTrainTime--; 147 | } 148 | else 149 | { 150 | // Training is enabled and done, run the function using only the generated bg data 151 | bsMOG2->apply(*frame, fgMask, 0); 152 | } 153 | 154 | // Filter if enabled 155 | if (bgFiltered) 156 | { 157 | cv::Mat element = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(filterSize, filterSize)); 158 | cv::morphologyEx(fgMask, fgMask, cv::MORPH_OPEN, element); 159 | cv::morphologyEx(fgMask, fgMask, cv::MORPH_CLOSE, element); 160 | } 161 | 162 | // Mask our input image (onto a nice beach image!) 163 | bgImage.copyTo(fgImage); 164 | frame->copyTo(fgImage, fgMask); 165 | 166 | if (backgroundTrainOnInit && bgsubTrainTime > 0) 167 | drawMessage(fgMask, fgMask, "TRAINING"); 168 | 169 | // Draw windows 170 | cv::imshow("Foreground mask", fgMask); 171 | cv::imshow("Foreground image", fgImage); 172 | } 173 | 174 | // Timestamp the image 175 | timestamp(frame); 176 | 177 | // Save a frame every 30 seconds 178 | if (saveFrames && (timestruct.tm_sec % savedFrameInterval) == 0) 179 | { 180 | if (!savedFrame) 181 | { 182 | // Generate the filename 183 | std::string filename = "FRAME_" + std::to_string(timestruct.tm_mon + 1) + "_" + std::to_string(timestruct.tm_mday) + "_" + std::to_string(timestruct.tm_hour) + "." + std::to_string(timestruct.tm_min) + "." + std::to_string(timestruct.tm_sec) + ".jpg"; 184 | 185 | // Save the frame 186 | cv::imwrite(filename, *frame); 187 | 188 | savedFrame = true; 189 | } 190 | } 191 | else 192 | savedFrame = false; 193 | 194 | // Upscale the image 195 | cv::Mat resizedFrame; 196 | cv::resize(*frame, resizedFrame, resizedFrame.size(), windowscale, windowscale, cv::INTER_CUBIC); 197 | 198 | // Draw the window 199 | cv::imshow("OpenCV Webcam", resizedFrame); 200 | 201 | // Input handling 202 | switch (cv::waitKey(10)) 203 | { 204 | case '+': 205 | filterSize++; 206 | break; 207 | case '-': 208 | if (filterSize > 1) 209 | filterSize--; 210 | break; 211 | case 'f': // Toggle filtering of foreground mask 212 | bgFiltered = !bgFiltered; 213 | break; 214 | case 'h': // bg subtraction initalize and enable 215 | bsMOG2 = cv::createBackgroundSubtractorMOG2(500,16.0,false); 216 | if (backgroundTrainOnInit) 217 | bgsubTrainTime = backgroundTrainTime; 218 | bgsubEnabled = true; 219 | break; 220 | case 27: // Esc exit 221 | return 0; 222 | break; 223 | default: 224 | break; 225 | } 226 | } 227 | 228 | return 0; 229 | } -------------------------------------------------------------------------------- /OpenCV_Webcam/OpenCV_Webcam.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {E0A567F4-E466-4116-819A-5081DB6900B8} 23 | Win32Proj 24 | OpenCV_Webcam 25 | 8.1 26 | 27 | 28 | 29 | Application 30 | true 31 | v140 32 | Unicode 33 | 34 | 35 | Application 36 | false 37 | v140 38 | true 39 | Unicode 40 | 41 | 42 | Application 43 | true 44 | v140 45 | Unicode 46 | 47 | 48 | Application 49 | false 50 | v140 51 | true 52 | Unicode 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | true 74 | 75 | 76 | true 77 | 78 | 79 | false 80 | 81 | 82 | false 83 | 84 | 85 | 86 | 87 | 88 | Level3 89 | Disabled 90 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 91 | true 92 | C:\Program Files\opencv\build\include 93 | 94 | 95 | Console 96 | true 97 | C:\Program Files\opencv\build\x64\vc14\lib;%(AdditionalLibraryDirectories) 98 | opencv_world310d.lib;%(AdditionalDependencies) 99 | 100 | 101 | 102 | 103 | 104 | 105 | Level3 106 | Disabled 107 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 108 | true 109 | $(OPENCV_DIR)\..\..\include;%(AdditionalIncludeDirectories) 110 | 111 | 112 | Console 113 | true 114 | $(OPENCV_DIR)\lib;%(AdditionalLibraryDirectories) 115 | opencv_world330d.lib;%(AdditionalDependencies) 116 | 117 | 118 | 119 | 120 | Level3 121 | 122 | 123 | MaxSpeed 124 | true 125 | true 126 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 127 | true 128 | C:\Program Files\opencv\build\include 129 | 130 | 131 | Console 132 | true 133 | true 134 | true 135 | opencv_world310.lib;%(AdditionalDependencies) 136 | C:\Program Files\opencv\build\x64\vc14\lib;%(AdditionalLibraryDirectories) 137 | 138 | 139 | 140 | 141 | Level3 142 | 143 | 144 | MaxSpeed 145 | true 146 | true 147 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 148 | true 149 | $(OPENCV_DIR)\..\..\include;%(AdditionalIncludeDirectories) 150 | 151 | 152 | Console 153 | true 154 | true 155 | true 156 | $(OPENCV_DIR)\lib;%(AdditionalLibraryDirectories) 157 | opencv_world330.lib;%(AdditionalDependencies) 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | --------------------------------------------------------------------------------