├── .gitignore ├── javascript ├── package.json ├── desktop.js ├── index.htm ├── double-click.htm └── web.js ├── python ├── desktop.py └── web.py ├── csharp ├── Web │ ├── Web.csproj │ └── Program.cs └── Desktop │ ├── Desktop.csproj │ └── Program.cs ├── go ├── desktop.go └── web.go ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | obj 2 | bin 3 | -------------------------------------------------------------------------------- /javascript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webcam", 3 | "version": "1.0.0", 4 | "description": "Open webcam in any web browsers", 5 | "main": "web.js", 6 | "author": "yushulx", 7 | "license": "MIT", 8 | "dependencies": { 9 | "opencv4nodejs": "^5.6.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /python/desktop.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | 3 | cap = cv2.VideoCapture(0) 4 | 5 | while True: 6 | ret, frame = cap.read() 7 | cv2.imshow("Webcam", frame) 8 | if cv2.waitKey(1) & 0xFF == 27: # use ESC to quit 9 | break 10 | 11 | cap.release() 12 | cv2.destroyAllWindows() 13 | 14 | 15 | -------------------------------------------------------------------------------- /csharp/Web/Web.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /csharp/Desktop/Desktop.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /javascript/desktop.js: -------------------------------------------------------------------------------- 1 | const cv = require('opencv4nodejs'); 2 | 3 | const vCap = new cv.VideoCapture(0); 4 | 5 | const delay = 10; 6 | while (true) { 7 | let frame = vCap.read(); 8 | // loop back to start on end of stream reached 9 | if (frame.empty) { 10 | vCap.reset(); 11 | frame = vCap.read(); 12 | } 13 | 14 | cv.imshow('OpenCV Node.js', frame); 15 | const key = cv.waitKey(delay); // Press ESC to quit 16 | if (key == 27) {break;} 17 | } -------------------------------------------------------------------------------- /go/desktop.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "gocv.io/x/gocv" 7 | ) 8 | 9 | func main() { 10 | webcam, _ := gocv.OpenVideoCapture(0) 11 | window := gocv.NewWindow("Webcam") 12 | img := gocv.NewMat() 13 | 14 | for { 15 | webcam.Read(&img) 16 | window.IMShow(img) 17 | key := window.WaitKey(10) 18 | if key == 27 { // ESC 19 | break 20 | } 21 | } 22 | 23 | fmt.Println("Exit") 24 | 25 | // must call close() to terminate the program 26 | webcam.Close() 27 | } 28 | -------------------------------------------------------------------------------- /javascript/index.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Webcam 6 | 7 | 8 | 9 | 10 | 11 | 12 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /javascript/double-click.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Webcam 6 | 7 | 8 | 9 | 10 | 11 | 12 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /csharp/Desktop/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using OpenCvSharp; 3 | 4 | namespace Desktop 5 | { 6 | class Program 7 | { 8 | static void Main(string[] args) 9 | { 10 | VideoCapture capture = new VideoCapture(0); 11 | using (Window window = new Window("Webcam")) 12 | { 13 | using (Mat image = new Mat()) 14 | { 15 | while (true) 16 | { 17 | capture.Read(image); 18 | if (image.Empty()) break; 19 | window.ShowImage(image); 20 | int key = Cv2.WaitKey(30); 21 | if (key == 27) break; 22 | } 23 | } 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Xiao Ling 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /javascript/web.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | const cv = require('opencv4nodejs') 3 | const wCap = new cv.VideoCapture(0); 4 | wCap.set(cv.CAP_PROP_FRAME_WIDTH, 640); 5 | wCap.set(cv.CAP_PROP_FRAME_HEIGHT, 480); 6 | 7 | var img = null; 8 | function capture() { 9 | var frame = wCap.read() 10 | if (frame.empty) { 11 | wCap.reset(); 12 | frame = wCap.read(); 13 | } 14 | 15 | img = cv.imencode('.jpg', frame); 16 | setTimeout(capture, 30); 17 | } 18 | 19 | capture(); 20 | 21 | var fs=require("fs"); 22 | var html = fs.readFileSync("index.htm", "utf8"); 23 | 24 | var server = http.createServer(function (req, res) { 25 | if (req.url === '/' || req.url === '/index.htm') { 26 | 27 | res.writeHead(200, { 'Content-Type': 'text/html' }); 28 | res.write(html); 29 | res.end(); 30 | 31 | } 32 | else if (req.url.startsWith("/image")) { 33 | 34 | res.writeHead(200, { 'Content-Type': 'image/jpeg' }); 35 | res.write(img); 36 | res.end(); 37 | 38 | } 39 | else 40 | res.end('Invalid Request!'); 41 | 42 | }); 43 | 44 | server.listen(2020); 45 | 46 | console.log('Node.js web server is running on port 2020...') -------------------------------------------------------------------------------- /go/web.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | "strings" 8 | 9 | "gocv.io/x/gocv" 10 | ) 11 | 12 | var webcam *gocv.VideoCapture 13 | var img gocv.Mat 14 | 15 | func handler(w http.ResponseWriter, r *http.Request) { 16 | if r.URL.Path == "/" { 17 | pageData := "" + 18 | "" + 19 | " " + 20 | " HttpListener Example" + 21 | " " + 22 | " " + 23 | "" + 24 | " " + 25 | " " + 26 | "" 27 | 28 | w.Header().Set("Content-Type", "text/html") 29 | w.Write([]byte(pageData)) 30 | } else if strings.HasPrefix(r.URL.Path, "/image") { 31 | webcam.Read(&img) 32 | jpg, _ := gocv.IMEncode(".jpg", img) 33 | w.Write(jpg) 34 | } else { 35 | fmt.Fprintf(w, "Page Not Found") 36 | } 37 | 38 | } 39 | 40 | func main() { 41 | fmt.Println("Running at port 2020...") 42 | webcam, _ = gocv.OpenVideoCapture(0) 43 | img = gocv.NewMat() 44 | http.HandleFunc("/", handler) 45 | log.Fatal(http.ListenAndServe(":2020", nil)) 46 | webcam.Close() 47 | } 48 | -------------------------------------------------------------------------------- /python/web.py: -------------------------------------------------------------------------------- 1 | import http.server 2 | import socketserver 3 | from time import sleep 4 | import cv2 5 | import threading 6 | 7 | PORT = 2020 8 | 9 | pageData = "" + \ 10 | "" + \ 11 | " " + \ 12 | " HttpListener Example" + \ 13 | " " + \ 14 | " " + \ 15 | ""+ \ 16 | " "+ \ 17 | " " + \ 18 | "" 19 | 20 | class MyHandler(http.server.BaseHTTPRequestHandler): 21 | def do_GET(self): 22 | 23 | if self.path == '/': 24 | self.send_response(200) 25 | self.send_header("Content-type", "text/html") 26 | self.end_headers() 27 | self.wfile.write(bytes(pageData, "utf8")) 28 | elif self.path.startswith('/image'): 29 | self.send_response(200) 30 | self.send_header("Content-type", "image/jpeg") 31 | self.end_headers() 32 | 33 | # ret, frame = cap.read() 34 | # _, jpg = cv2.imencode(".jpg", frame) 35 | 36 | self.wfile.write(jpg) 37 | else: 38 | self.send_response(404) 39 | 40 | # continuously get frames from webcam 41 | cap = cv2.VideoCapture(0) 42 | ret, frame = cap.read() 43 | _, jpg = cv2.imencode(".jpg", frame) 44 | 45 | class FrameThread (threading.Thread): 46 | def __init__(self): 47 | threading.Thread.__init__(self) 48 | self.isRunning = True 49 | 50 | def run(self): 51 | global jpg, cap 52 | 53 | while self.isRunning: 54 | ret, frame = cap.read() 55 | _, jpg = cv2.imencode(".jpg", frame) 56 | sleep(0.03) 57 | 58 | print("Quit thread") 59 | 60 | frame_thread = FrameThread() 61 | frame_thread.start() 62 | 63 | # run the server 64 | with socketserver.TCPServer(("", PORT), MyHandler) as httpd: 65 | print("Serving at port ", PORT) 66 | try: 67 | httpd.serve_forever() 68 | except: 69 | pass 70 | 71 | # quit the app 72 | print('Server is stopped') 73 | frame_thread.isRunning = False 74 | frame_thread.join() 75 | cap.release() 76 | 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Webcam Video Display for Desktop and Web 2 | 3 | The repository contains some simple webcam display programs implemented by using different programming languages and OpenCV. The web camera apps are available for both local and remote access via any web browser. 4 | 5 | ## Programming Language List 6 | - [JavaScript](#JavaScript) 7 | - [C#](#CSharp) 8 | - [Python](#Python) 9 | - [Golang](#Golang) 10 | 11 | 12 | ## JavaScript 13 | 14 | 15 | Install [opencv4nodejs](https://www.npmjs.com/package/opencv4nodejs): 16 | 17 | ``` 18 | npm i opencv4nodejs 19 | ``` 20 | 21 | #### Desktop 22 | 23 | Run: 24 | 25 | ``` 26 | node desktop.js 27 | ``` 28 | 29 | #### Web 30 | 31 | 1. Run: 32 | 33 | ``` 34 | node web.js 35 | ``` 36 | 2. Open `double-click.htm` directly or visit `locahost:2020` in any web browser. 37 | 38 | ## CSharp 39 | 40 | Install [OpenCvSharp](https://github.com/shimat/opencvsharp): 41 | 42 | **For Windows** 43 | 44 | ``` 45 | dotnet add package OpenCvSharp4 46 | dotnet add package OpenCvSharp4.runtime.win 47 | ``` 48 | 49 | ### Desktop 50 | Run: 51 | 52 | ``` 53 | dotnet restore 54 | dotnet run 55 | ``` 56 | 57 | ### Web 58 | 59 | 1. Run: 60 | 61 | ``` 62 | dotnet restore 63 | dotnet run 64 | ``` 65 | 66 | 2. Visit `locahost:2020` in any web browser. 67 | 68 | ## Python 69 | 70 | Install [OpenCV Python](https://pypi.org/project/opencv-python/): 71 | 72 | ``` 73 | pip install opencv-python 74 | ``` 75 | 76 | ### Desktop 77 | Run: 78 | 79 | ``` 80 | python desktop.py 81 | ``` 82 | 83 | ### Web 84 | 1. Run: 85 | 86 | ``` 87 | python web.py 88 | ``` 89 | 90 | 2. Visit `locahost:2020` in any web browser. 91 | 92 | ## Golang 93 | Install [gocv](https://gocv.io/getting-started): 94 | 95 | ``` 96 | go get -u -d gocv.io/x/gocv 97 | ``` 98 | 99 | **For Windows** 100 | 101 | 1. Install MinGW-W64 [x86_64-7.3.0-posix-seh-rt_v5-rev2](https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/7.3.0/) 102 | 2. Install [CMake](https://cmake.org/download/) 103 | 3. Build the OpenCV module: 104 | 105 | ``` 106 | chdir %GOPATH%\src\gocv.io\x\gocv 107 | win_build_opencv.cmd 108 | ``` 109 | 4. Add `C:\opencv\build\install\x64\mingw\bin` to your system path. 110 | 111 | 112 | 113 | ### Desktop 114 | Run: 115 | 116 | ``` 117 | go run desktop.go 118 | ``` 119 | 120 | ### Web 121 | 1. Run: 122 | 123 | ``` 124 | go run web.go 125 | ``` 126 | 127 | 2. Visit `locahost:2020` in any web browser. 128 | 129 | ## Blog 130 | [How to Use OpenCV to Build Simple Webcam Apps for Desktop and Web](https://www.dynamsoft.com/codepool/opencv-webcam-app-desktop-web.html) 131 | -------------------------------------------------------------------------------- /csharp/Web/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | using System.Net; 5 | using System.Threading.Tasks; 6 | using OpenCvSharp; 7 | 8 | namespace Web 9 | { 10 | class Program 11 | { 12 | public static HttpListener listener; 13 | public static string url = "http://localhost:2020/"; 14 | public static string pageData = 15 | "" + 16 | "" + 17 | " " + 18 | " HttpListener Example" + 19 | " " + 20 | " " + 21 | ""+ 22 | " "+ 23 | " " + 24 | ""; 25 | 26 | public static VideoCapture capture = new VideoCapture(0); 27 | 28 | // https://gist.github.com/define-private-public/d05bc52dd0bed1c4699d49e2737e80e7 29 | 30 | public static async Task HandleIncomingConnections() 31 | { 32 | while (true) 33 | { 34 | // Will wait here until we hear from a connection 35 | HttpListenerContext ctx = await listener.GetContextAsync(); 36 | 37 | // Peel out the requests and response objects 38 | HttpListenerRequest req = ctx.Request; 39 | HttpListenerResponse resp = ctx.Response; 40 | 41 | // Print out some info about the request 42 | // Console.WriteLine(req.Url.ToString()); 43 | // Console.WriteLine(req.HttpMethod); 44 | // Console.WriteLine(req.UserHostName); 45 | // Console.WriteLine(req.UserAgent); 46 | // Console.WriteLine(); 47 | 48 | if ((req.HttpMethod == "GET") && (req.Url.AbsolutePath.StartsWith("/image"))) { 49 | 50 | resp.ContentType = "image/jpeg"; 51 | using (Mat image = new Mat()) 52 | { 53 | capture.Read(image); 54 | Cv2.ImEncode(".jpg", image, out var imageData); 55 | await resp.OutputStream.WriteAsync(imageData, 0, imageData.Length); 56 | resp.Close(); 57 | } 58 | } 59 | else { 60 | 61 | // Write the response info 62 | byte[] data = Encoding.UTF8.GetBytes(pageData); 63 | resp.ContentType = "text/html"; 64 | resp.ContentEncoding = Encoding.UTF8; 65 | resp.ContentLength64 = data.LongLength; 66 | 67 | // Write out to the response stream (asynchronously), then close it 68 | await resp.OutputStream.WriteAsync(data, 0, data.Length); 69 | resp.Close(); 70 | } 71 | } 72 | } 73 | 74 | static void Main(string[] args) 75 | { 76 | // Create a Http server and start listening for incoming connections 77 | listener = new HttpListener(); 78 | listener.Prefixes.Add(url); 79 | listener.Start(); 80 | Console.WriteLine("Listening for connections on {0}", url); 81 | 82 | // Handle requests 83 | Task listenTask = HandleIncomingConnections(); 84 | listenTask.GetAwaiter().GetResult(); 85 | 86 | // Close the listener 87 | listener.Close(); 88 | } 89 | } 90 | } 91 | --------------------------------------------------------------------------------