英文:
Zooming mjpeg in object
问题
使用gocv
库,我正在将图像流传输到我的HTML5页面上的object
元素。
页面代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<title>使用gocv进行摄像头流传输</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="css/style.css" rel="stylesheet">
</head>
<body>
<!--<div id ="content"></div>-->
<object data="http://localhost:8080/camera" width="300" height="200" alt="摄像头流传输"></object>
</body>
<<script>
/* (function(){
document.getElementById("content").innerHTML='<object type="text/html" data="http://localhost:8080/cam" ></object>';
})();
*/
</script>
</html>
我的Go代码如下:
// 此示例打开一个视频捕获设备,然后从中流式传输MJPEG。
// 运行后,将浏览器指向命令行中传入的主机名/端口号(例如http://localhost:8080),您应该能够看到实时视频流。
//
// 如何运行:
//
// mjpeg-streamer [camera ID] [host:port]
//
// go get -u github.com/hybridgroup/mjpeg
// go run ./cmd/mjpeg-streamer/main.go 1 0.0.0.0:8080
//
package main
import (
"fmt"
"log"
"net/http"
_ "net/http/pprof"
"opencv/mjpeg"
"gocv.io/x/gocv"
)
var (
deviceID int
err error
webcam *gocv.VideoCapture
stream *mjpeg.Stream
)
func main() {
/* if len(os.Args) < 3 {
fmt.Println("How to run:\n\tmjpeg-streamer [camera ID] [host:port]")
return
}
*/
// 解析参数
deviceID := 0 // os.Args[1]
host := ":8080" //os.Args[2]
// 打开摄像头
webcam, err = gocv.OpenVideoCapture(deviceID)
if err != nil {
fmt.Printf("打开捕获设备时出错:%v\n", deviceID)
return
}
defer webcam.Close()
// 创建MJPEG流
stream = mjpeg.NewStream()
// 开始捕获
go mjpegCapture()
fmt.Println("正在捕获。请将浏览器指向" + host)
// 启动HTTP服务器
http.Handle("/camera", stream)
log.Fatal(http.ListenAndServe(host, nil))
}
func mjpegCapture() {
img := gocv.NewMat()
defer img.Close()
for {
if ok := webcam.Read(&img); !ok {
fmt.Printf("设备已关闭:%v\n", deviceID)
return
}
if img.Empty() {
continue
}
buf, _ := gocv.IMEncode(".jpg", img)
stream.UpdateJPEG(buf.GetBytes())
buf.Close()
}
}
opencv/mjpeg
中的流式传输函数如下:
// 包mjpeg实现了一个简单的MJPEG流传输器。
//
// Stream对象实现了http.Handler接口,可以像下面这样与net/http包一起使用:
// stream = mjpeg.NewStream()
// http.Handle("/camera", stream)
// 然后使用stream.UpdateJPEG()将新的JPEG帧推送到连接的客户端。
package mjpeg
import (
"fmt"
"log"
"net/http"
"sync"
"time"
)
// Stream表示单个视频源。
type Stream struct {
m map[chan []byte]bool
frame []byte
lock sync.Mutex
FrameInterval time.Duration
}
const boundaryWord = "MJPEGBOUNDARY"
const headerf = "\r\n" +
"--" + boundaryWord + "\r\n" +
"Content-Type: image/jpeg\r\n" +
"Content-Length: %d\r\n" +
"X-Timestamp: 0.000000\r\n" +
"\r\n"
// ServeHTTP使用MJPEG流响应HTTP请求,实现了http.Handler接口。
func (s *Stream) ServeHTTP(w http.ResponseWriter, r *http.Request) {
log.Println("流:", r.RemoteAddr, "已连接")
w.Header().Add("Content-Type", "multipart/x-mixed-replace;boundary="+boundaryWord)
c := make(chan []byte)
s.lock.Lock()
s.m[c] = true
s.lock.Unlock()
for {
time.Sleep(s.FrameInterval)
b := <-c
_, err := w.Write(b)
if err != nil {
break
}
}
s.lock.Lock()
delete(s.m, c)
s.lock.Unlock()
log.Println("流:", r.RemoteAddr, "已断开连接")
}
// UpdateJPEG将新的JPEG帧推送到客户端。
func (s *Stream) UpdateJPEG(jpeg []byte) {
header := fmt.Sprintf(headerf, len(jpeg))
if len(s.frame) < len(jpeg)+len(header) {
s.frame = make([]byte, (len(jpeg)+len(header))*2)
}
copy(s.frame, header)
copy(s.frame[len(header):], jpeg)
s.lock.Lock()
for c := range s.m {
// 选择跳过正在休眠以丢弃帧的流。
// 这可能需要更多的思考。
select {
case c <- s.frame:
default:
}
}
s.lock.Unlock()
}
// NewStream初始化并返回一个新的Stream。
func NewStream() *Stream {
return &Stream{
m: make(map[chan []byte]bool),
frame: make([]byte, len(headerf)),
FrameInterval: 50 * time.Millisecond,
}
}
我的输出如下:
我的问题是:
- 如何将流式传输的图像适应所选的
object
元素尺寸? - 是否有办法将其流式传输到
video
元素?我尝试过但失败了。
英文:
Using gocv
I'm streaming an image to an object
element at my html5 page.
The page is:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Cam Streaming with gocv</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="css/style.css" rel="stylesheet">
</head>
<body>
<!-- <div id ="content"></div> -->
<object data="http://localhost:8080/camera" width="300" height="200" alt="Cam streaming"></object>
</body>
<<script>
/* (function(){
document.getElementById("content").innerHTML='<object type="text/html" data="http://localhost:8080/cam" ></object>';
})();
*/
</script>
</html>
Ang my go
code is:
// This example opens a video capture device, then streams MJPEG from it.
// Once running point your browser to the hostname/port you passed in the
// command line (for example http://localhost:8080) and you should see
// the live video stream.
//
// How to run:
//
// mjpeg-streamer [camera ID] [host:port]
//
// go get -u github.com/hybridgroup/mjpeg
// go run ./cmd/mjpeg-streamer/main.go 1 0.0.0.0:8080
//
package main
import (
"fmt"
"log"
"net/http"
_ "net/http/pprof"
"opencv/mjpeg"
"gocv.io/x/gocv"
)
var (
deviceID int
err error
webcam *gocv.VideoCapture
stream *mjpeg.Stream
)
func main() {
/* if len(os.Args) < 3 {
fmt.Println("How to run:\n\tmjpeg-streamer [camera ID] [host:port]")
return
}
*/
// parse args
deviceID := 0 // os.Args[1]
host := ":8080" //os.Args[2]
// open webcam
webcam, err = gocv.OpenVideoCapture(deviceID)
if err != nil {
fmt.Printf("Error opening capture device: %v\n", deviceID)
return
}
defer webcam.Close()
// create the mjpeg stream
stream = mjpeg.NewStream()
// start capturing
go mjpegCapture()
fmt.Println("Capturing. Point your browser to " + host)
// start http server
http.Handle("/camera", stream)
log.Fatal(http.ListenAndServe(host, nil))
}
func mjpegCapture() {
img := gocv.NewMat()
defer img.Close()
for {
if ok := webcam.Read(&img); !ok {
fmt.Printf("Device closed: %v\n", deviceID)
return
}
if img.Empty() {
continue
}
buf, _ := gocv.IMEncode(".jpg", img)
stream.UpdateJPEG(buf.GetBytes())
buf.Close()
}
}
The streaming function at opencv/mjpeg
is:
// Package mjpeg implements a simple MJPEG streamer.
//
// Stream objects implement the http.Handler interface, allowing to use them with the net/http package like so:
// stream = mjpeg.NewStream()
// http.Handle("/camera", stream)
// Then push new JPEG frames to the connected clients using stream.UpdateJPEG().
package mjpeg
import (
"fmt"
"log"
"net/http"
"sync"
"time"
)
// Stream represents a single video feed.
type Stream struct {
m map[chan []byte]bool
frame []byte
lock sync.Mutex
FrameInterval time.Duration
}
const boundaryWord = "MJPEGBOUNDARY"
const headerf = "\r\n" +
"--" + boundaryWord + "\r\n" +
"Content-Type: image/jpeg\r\n" +
"Content-Length: %d\r\n" +
"X-Timestamp: 0.000000\r\n" +
"\r\n"
// ServeHTTP responds to HTTP requests with the MJPEG stream, implementing the http.Handler interface.
func (s *Stream) ServeHTTP(w http.ResponseWriter, r *http.Request) {
log.Println("Stream:", r.RemoteAddr, "connected")
w.Header().Add("Content-Type", "multipart/x-mixed-replace;boundary="+boundaryWord)
c := make(chan []byte)
s.lock.Lock()
s.m[c] = true
s.lock.Unlock()
for {
time.Sleep(s.FrameInterval)
b := <-c
_, err := w.Write(b)
if err != nil {
break
}
}
s.lock.Lock()
delete(s.m, c)
s.lock.Unlock()
log.Println("Stream:", r.RemoteAddr, "disconnected")
}
// UpdateJPEG pushes a new JPEG frame onto the clients.
func (s *Stream) UpdateJPEG(jpeg []byte) {
header := fmt.Sprintf(headerf, len(jpeg))
if len(s.frame) < len(jpeg)+len(header) {
s.frame = make([]byte, (len(jpeg)+len(header))*2)
}
copy(s.frame, header)
copy(s.frame[len(header):], jpeg)
s.lock.Lock()
for c := range s.m {
// Select to skip streams which are sleeping to drop frames.
// This might need more thought.
select {
case c <- s.frame:
default:
}
}
s.lock.Unlock()
}
// NewStream initializes and returns a new Stream.
func NewStream() *Stream {
return &Stream{
m: make(map[chan []byte]bool),
frame: make([]byte, len(headerf)),
FrameInterval: 50 * time.Millisecond,
}
}
My output is as below:
My question is:
- How can I fit the streamed image into the selected
object
dimensions - Is there a way to stream it to
video
element, I tried but failed.
答案1
得分: 0
我在go
代码中找到了一个答案,它使用以下方式进行图像调整大小:
"image/jpeg"
"golang.org/x/image/draw"
代码如下:
// 解码图像(从PNG到image.Image):
src, _ := j.Decode(bytes.NewReader(jpeg))
// 设置所需的大小:
dst := image.NewRGBA(image.Rect(0, 0, src.Bounds().Max.X/3, src.Bounds().Max.Y/3))
// 调整大小:
draw.NearestNeighbor.Scale(dst, dst.Rect, src, src.Bounds(), draw.Over, nil)
buf := new(bytes.Buffer)
// 编码到`buf`:
j.Encode(buf, dst, nil)
copy(s.frame, header)
// copy(s.frame[len(header):], jpeg)
copy(s.frame[len(header):], buf.Bytes())
因此,我的完整流式代码如下:
// Package mjpeg implements a simple MJPEG streamer.
//
// Stream objects implement the http.Handler interface, allowing to use them with the net/http package like so:
// stream = mjpeg.NewStream()
// http.Handle("/camera", stream)
// Then push new JPEG frames to the connected clients using stream.UpdateJPEG().
package mjpeg
import (
"bytes"
"fmt"
"image"
j "image/jpeg"
"log"
"net/http"
"sync"
"time"
"golang.org/x/image/draw"
)
// Stream represents a single video feed.
type Stream struct {
m map[chan []byte]bool
frame []byte
lock sync.Mutex
FrameInterval time.Duration
}
const boundaryWord = "MJPEGBOUNDARY"
const headerf = "\r\n" +
"--" + boundaryWord + "\r\n" +
"Content-Type: image/jpeg\r\n" +
"Content-Length: %d\r\n" +
"X-Timestamp: 0.000000\r\n" +
"\r\n"
// ServeHTTP responds to HTTP requests with the MJPEG stream, implementing the http.Handler interface.
func (s *Stream) ServeHTTP(w http.ResponseWriter, r *http.Request) {
log.Println("Stream:", r.RemoteAddr, "connected")
w.Header().Add("Content-Type", "multipart/x-mixed-replace;boundary="+boundaryWord)
c := make(chan []byte)
s.lock.Lock()
s.m[c] = true
s.lock.Unlock()
for {
time.Sleep(s.FrameInterval)
b := <-c
_, err := w.Write(b)
if err != nil {
break
}
}
s.lock.Lock()
delete(s.m, c)
s.lock.Unlock()
log.Println("Stream:", r.RemoteAddr, "disconnected")
}
// UpdateJPEG pushes a new JPEG frame onto the clients.
func (s *Stream) UpdateJPEG(jpeg []byte) {
header := fmt.Sprintf(headerf, len(jpeg))
if len(s.frame) < len(jpeg)+len(header) {
s.frame = make([]byte, (len(jpeg)+len(header))*2)
}
// 解码图像(从PNG到image.Image):
src, _ := j.Decode(bytes.NewReader(jpeg))
// 设置所需的大小:
dst := image.NewRGBA(image.Rect(0, 0, src.Bounds().Max.X/3, src.Bounds().Max.Y/3))
// 调整大小:
draw.NearestNeighbor.Scale(dst, dst.Rect, src, src.Bounds(), draw.Over, nil)
buf := new(bytes.Buffer)
// 编码到`buf`:
j.Encode(buf, dst, nil)
copy(s.frame, header)
// copy(s.frame[len(header):], jpeg)
copy(s.frame[len(header):], buf.Bytes())
s.lock.Lock()
for c := range s.m {
// 选择跳过正在休眠以丢弃帧的流。
// 这可能需要更多的思考。
select {
case c <- s.frame:
default:
}
}
s.lock.Unlock()
}
// NewStream initializes and returns a new Stream.
func NewStream() *Stream {
return &Stream{
m: make(map[chan []byte]bool),
frame: make([]byte, len(headerf)),
FrameInterval: 50 * time.Millisecond,
}
}
我的输出如下:
英文:
I found the an answer in the go
code, that is doing image resizing using:
"image/jpeg"
"golang.org/x/image/draw"
As:
// Decode the image (from PNG to image.Image):
src, _ := j.Decode(bytes.NewReader(jpeg))
// Set the expected size that you want:
dst := image.NewRGBA(image.Rect(0, 0, src.Bounds().Max.X/3, src.Bounds().Max.Y/3))
// Resize:
draw.NearestNeighbor.Scale(dst, dst.Rect, src, src.Bounds(), draw.Over, nil)
buf := new(bytes.Buffer)
// Encode to `buf`:
j.Encode(buf, dst, nil)
copy(s.frame, header)
// copy(s.frame[len(header):], jpeg)
copy(s.frame[len(header):], buf.Bytes())
So, my streaming full code became:
// Package mjpeg implements a simple MJPEG streamer.
//
// Stream objects implement the http.Handler interface, allowing to use them with the net/http package like so:
// stream = mjpeg.NewStream()
// http.Handle("/camera", stream)
// Then push new JPEG frames to the connected clients using stream.UpdateJPEG().
package mjpeg
import (
"bytes"
"fmt"
"image"
j "image/jpeg"
"log"
"net/http"
"sync"
"time"
"golang.org/x/image/draw"
)
// Stream represents a single video feed.
type Stream struct {
m map[chan []byte]bool
frame []byte
lock sync.Mutex
FrameInterval time.Duration
}
const boundaryWord = "MJPEGBOUNDARY"
const headerf = "\r\n" +
"--" + boundaryWord + "\r\n" +
"Content-Type: image/jpeg\r\n" +
"Content-Length: %d\r\n" +
"X-Timestamp: 0.000000\r\n" +
"\r\n"
// ServeHTTP responds to HTTP requests with the MJPEG stream, implementing the http.Handler interface.
func (s *Stream) ServeHTTP(w http.ResponseWriter, r *http.Request) {
log.Println("Stream:", r.RemoteAddr, "connected")
w.Header().Add("Content-Type", "multipart/x-mixed-replace;boundary="+boundaryWord)
c := make(chan []byte)
s.lock.Lock()
s.m[c] = true
s.lock.Unlock()
for {
time.Sleep(s.FrameInterval)
b := <-c
_, err := w.Write(b)
if err != nil {
break
}
}
s.lock.Lock()
delete(s.m, c)
s.lock.Unlock()
log.Println("Stream:", r.RemoteAddr, "disconnected")
}
// UpdateJPEG pushes a new JPEG frame onto the clients.
func (s *Stream) UpdateJPEG(jpeg []byte) {
header := fmt.Sprintf(headerf, len(jpeg))
if len(s.frame) < len(jpeg)+len(header) {
s.frame = make([]byte, (len(jpeg)+len(header))*2)
}
// Decode the image (from PNG to image.Image):
src, _ := j.Decode(bytes.NewReader(jpeg))
// Set the expected size that you want:
dst := image.NewRGBA(image.Rect(0, 0, src.Bounds().Max.X/3, src.Bounds().Max.Y/3))
// Resize:
draw.NearestNeighbor.Scale(dst, dst.Rect, src, src.Bounds(), draw.Over, nil)
buf := new(bytes.Buffer)
// Encode to `buf`:
j.Encode(buf, dst, nil)
copy(s.frame, header)
// copy(s.frame[len(header):], jpeg)
copy(s.frame[len(header):], buf.Bytes())
s.lock.Lock()
for c := range s.m {
// Select to skip streams which are sleeping to drop frames.
// This might need more thought.
select {
case c <- s.frame:
default:
}
}
s.lock.Unlock()
}
// NewStream initializes and returns a new Stream.
func NewStream() *Stream {
return &Stream{
m: make(map[chan []byte]bool),
frame: make([]byte, len(headerf)),
FrameInterval: 50 * time.Millisecond,
}
}
Any my output became:
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论