在对象中放大MJPEG

huangapple go评论126阅读模式
英文:

Zooming mjpeg in object

问题

使用gocv库,我正在将图像流传输到我的HTML5页面上的object元素。

页面代码如下:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <title>使用gocv进行摄像头流传输</title>
  5. <meta charset="UTF-8">
  6. <meta name="viewport" content="width=device-width, initial-scale=1">
  7. <link href="css/style.css" rel="stylesheet">
  8. </head>
  9. <body>
  10. <!--<div id ="content"></div>-->
  11. <object data="http://localhost:8080/camera" width="300" height="200" alt="摄像头流传输"></object>
  12. </body>
  13. <<script>
  14. /* (function(){
  15. document.getElementById("content").innerHTML='<object type="text/html" data="http://localhost:8080/cam" ></object>';
  16. })();
  17. */
  18. </script>
  19. </html>

我的Go代码如下:

  1. // 此示例打开一个视频捕获设备,然后从中流式传输MJPEG。
  2. // 运行后,将浏览器指向命令行中传入的主机名/端口号(例如http://localhost:8080),您应该能够看到实时视频流。
  3. //
  4. // 如何运行:
  5. //
  6. // mjpeg-streamer [camera ID] [host:port]
  7. //
  8. // go get -u github.com/hybridgroup/mjpeg
  9. // go run ./cmd/mjpeg-streamer/main.go 1 0.0.0.0:8080
  10. //
  11. package main
  12. import (
  13. "fmt"
  14. "log"
  15. "net/http"
  16. _ "net/http/pprof"
  17. "opencv/mjpeg"
  18. "gocv.io/x/gocv"
  19. )
  20. var (
  21. deviceID int
  22. err error
  23. webcam *gocv.VideoCapture
  24. stream *mjpeg.Stream
  25. )
  26. func main() {
  27. /* if len(os.Args) < 3 {
  28. fmt.Println("How to run:\n\tmjpeg-streamer [camera ID] [host:port]")
  29. return
  30. }
  31. */
  32. // 解析参数
  33. deviceID := 0 // os.Args[1]
  34. host := ":8080" //os.Args[2]
  35. // 打开摄像头
  36. webcam, err = gocv.OpenVideoCapture(deviceID)
  37. if err != nil {
  38. fmt.Printf("打开捕获设备时出错:%v\n", deviceID)
  39. return
  40. }
  41. defer webcam.Close()
  42. // 创建MJPEG流
  43. stream = mjpeg.NewStream()
  44. // 开始捕获
  45. go mjpegCapture()
  46. fmt.Println("正在捕获。请将浏览器指向" + host)
  47. // 启动HTTP服务器
  48. http.Handle("/camera", stream)
  49. log.Fatal(http.ListenAndServe(host, nil))
  50. }
  51. func mjpegCapture() {
  52. img := gocv.NewMat()
  53. defer img.Close()
  54. for {
  55. if ok := webcam.Read(&img); !ok {
  56. fmt.Printf("设备已关闭:%v\n", deviceID)
  57. return
  58. }
  59. if img.Empty() {
  60. continue
  61. }
  62. buf, _ := gocv.IMEncode(".jpg", img)
  63. stream.UpdateJPEG(buf.GetBytes())
  64. buf.Close()
  65. }
  66. }

opencv/mjpeg中的流式传输函数如下:

  1. // 包mjpeg实现了一个简单的MJPEG流传输器。
  2. //
  3. // Stream对象实现了http.Handler接口,可以像下面这样与net/http包一起使用:
  4. // stream = mjpeg.NewStream()
  5. // http.Handle("/camera", stream)
  6. // 然后使用stream.UpdateJPEG()将新的JPEG帧推送到连接的客户端。
  7. package mjpeg
  8. import (
  9. "fmt"
  10. "log"
  11. "net/http"
  12. "sync"
  13. "time"
  14. )
  15. // Stream表示单个视频源。
  16. type Stream struct {
  17. m map[chan []byte]bool
  18. frame []byte
  19. lock sync.Mutex
  20. FrameInterval time.Duration
  21. }
  22. const boundaryWord = "MJPEGBOUNDARY"
  23. const headerf = "\r\n" +
  24. "--" + boundaryWord + "\r\n" +
  25. "Content-Type: image/jpeg\r\n" +
  26. "Content-Length: %d\r\n" +
  27. "X-Timestamp: 0.000000\r\n" +
  28. "\r\n"
  29. // ServeHTTP使用MJPEG流响应HTTP请求,实现了http.Handler接口。
  30. func (s *Stream) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  31. log.Println("流:", r.RemoteAddr, "已连接")
  32. w.Header().Add("Content-Type", "multipart/x-mixed-replace;boundary="+boundaryWord)
  33. c := make(chan []byte)
  34. s.lock.Lock()
  35. s.m[c] = true
  36. s.lock.Unlock()
  37. for {
  38. time.Sleep(s.FrameInterval)
  39. b := <-c
  40. _, err := w.Write(b)
  41. if err != nil {
  42. break
  43. }
  44. }
  45. s.lock.Lock()
  46. delete(s.m, c)
  47. s.lock.Unlock()
  48. log.Println("流:", r.RemoteAddr, "已断开连接")
  49. }
  50. // UpdateJPEG将新的JPEG帧推送到客户端。
  51. func (s *Stream) UpdateJPEG(jpeg []byte) {
  52. header := fmt.Sprintf(headerf, len(jpeg))
  53. if len(s.frame) < len(jpeg)+len(header) {
  54. s.frame = make([]byte, (len(jpeg)+len(header))*2)
  55. }
  56. copy(s.frame, header)
  57. copy(s.frame[len(header):], jpeg)
  58. s.lock.Lock()
  59. for c := range s.m {
  60. // 选择跳过正在休眠以丢弃帧的流。
  61. // 这可能需要更多的思考。
  62. select {
  63. case c <- s.frame:
  64. default:
  65. }
  66. }
  67. s.lock.Unlock()
  68. }
  69. // NewStream初始化并返回一个新的Stream。
  70. func NewStream() *Stream {
  71. return &Stream{
  72. m: make(map[chan []byte]bool),
  73. frame: make([]byte, len(headerf)),
  74. FrameInterval: 50 * time.Millisecond,
  75. }
  76. }

我的输出如下:

在对象中放大MJPEG

我的问题是:

  1. 如何将流式传输的图像适应所选的object元素尺寸?
  2. 是否有办法将其流式传输到video元素?我尝试过但失败了。
英文:

Using gocv I'm streaming an image to an object element at my html5 page.

The page is:

  1. &lt;!DOCTYPE html&gt;
  2. &lt;html lang=&quot;en&quot;&gt;
  3. &lt;head&gt;
  4. &lt;title&gt;Cam Streaming with gocv&lt;/title&gt;
  5. &lt;meta charset=&quot;UTF-8&quot;&gt;
  6. &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;
  7. &lt;link href=&quot;css/style.css&quot; rel=&quot;stylesheet&quot;&gt;
  8. &lt;/head&gt;
  9. &lt;body&gt;
  10. &lt;!-- &lt;div id =&quot;content&quot;&gt;&lt;/div&gt; --&gt;
  11. &lt;object data=&quot;http://localhost:8080/camera&quot; width=&quot;300&quot; height=&quot;200&quot; alt=&quot;Cam streaming&quot;&gt;&lt;/object&gt;
  12. &lt;/body&gt;
  13. &lt;&lt;script&gt;
  14. /* (function(){
  15. document.getElementById(&quot;content&quot;).innerHTML=&#39;&lt;object type=&quot;text/html&quot; data=&quot;http://localhost:8080/cam&quot; &gt;&lt;/object&gt;&#39;;
  16. })();
  17. */
  18. &lt;/script&gt;
  19. &lt;/html&gt;

Ang my go code is:

  1. // This example opens a video capture device, then streams MJPEG from it.
  2. // Once running point your browser to the hostname/port you passed in the
  3. // command line (for example http://localhost:8080) and you should see
  4. // the live video stream.
  5. //
  6. // How to run:
  7. //
  8. // mjpeg-streamer [camera ID] [host:port]
  9. //
  10. // go get -u github.com/hybridgroup/mjpeg
  11. // go run ./cmd/mjpeg-streamer/main.go 1 0.0.0.0:8080
  12. //
  13. package main
  14. import (
  15. &quot;fmt&quot;
  16. &quot;log&quot;
  17. &quot;net/http&quot;
  18. _ &quot;net/http/pprof&quot;
  19. &quot;opencv/mjpeg&quot;
  20. &quot;gocv.io/x/gocv&quot;
  21. )
  22. var (
  23. deviceID int
  24. err error
  25. webcam *gocv.VideoCapture
  26. stream *mjpeg.Stream
  27. )
  28. func main() {
  29. /* if len(os.Args) &lt; 3 {
  30. fmt.Println(&quot;How to run:\n\tmjpeg-streamer [camera ID] [host:port]&quot;)
  31. return
  32. }
  33. */
  34. // parse args
  35. deviceID := 0 // os.Args[1]
  36. host := &quot;:8080&quot; //os.Args[2]
  37. // open webcam
  38. webcam, err = gocv.OpenVideoCapture(deviceID)
  39. if err != nil {
  40. fmt.Printf(&quot;Error opening capture device: %v\n&quot;, deviceID)
  41. return
  42. }
  43. defer webcam.Close()
  44. // create the mjpeg stream
  45. stream = mjpeg.NewStream()
  46. // start capturing
  47. go mjpegCapture()
  48. fmt.Println(&quot;Capturing. Point your browser to &quot; + host)
  49. // start http server
  50. http.Handle(&quot;/camera&quot;, stream)
  51. log.Fatal(http.ListenAndServe(host, nil))
  52. }
  53. func mjpegCapture() {
  54. img := gocv.NewMat()
  55. defer img.Close()
  56. for {
  57. if ok := webcam.Read(&amp;img); !ok {
  58. fmt.Printf(&quot;Device closed: %v\n&quot;, deviceID)
  59. return
  60. }
  61. if img.Empty() {
  62. continue
  63. }
  64. buf, _ := gocv.IMEncode(&quot;.jpg&quot;, img)
  65. stream.UpdateJPEG(buf.GetBytes())
  66. buf.Close()
  67. }
  68. }

The streaming function at opencv/mjpeg is:

  1. // Package mjpeg implements a simple MJPEG streamer.
  2. //
  3. // Stream objects implement the http.Handler interface, allowing to use them with the net/http package like so:
  4. // stream = mjpeg.NewStream()
  5. // http.Handle(&quot;/camera&quot;, stream)
  6. // Then push new JPEG frames to the connected clients using stream.UpdateJPEG().
  7. package mjpeg
  8. import (
  9. &quot;fmt&quot;
  10. &quot;log&quot;
  11. &quot;net/http&quot;
  12. &quot;sync&quot;
  13. &quot;time&quot;
  14. )
  15. // Stream represents a single video feed.
  16. type Stream struct {
  17. m map[chan []byte]bool
  18. frame []byte
  19. lock sync.Mutex
  20. FrameInterval time.Duration
  21. }
  22. const boundaryWord = &quot;MJPEGBOUNDARY&quot;
  23. const headerf = &quot;\r\n&quot; +
  24. &quot;--&quot; + boundaryWord + &quot;\r\n&quot; +
  25. &quot;Content-Type: image/jpeg\r\n&quot; +
  26. &quot;Content-Length: %d\r\n&quot; +
  27. &quot;X-Timestamp: 0.000000\r\n&quot; +
  28. &quot;\r\n&quot;
  29. // ServeHTTP responds to HTTP requests with the MJPEG stream, implementing the http.Handler interface.
  30. func (s *Stream) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  31. log.Println(&quot;Stream:&quot;, r.RemoteAddr, &quot;connected&quot;)
  32. w.Header().Add(&quot;Content-Type&quot;, &quot;multipart/x-mixed-replace;boundary=&quot;+boundaryWord)
  33. c := make(chan []byte)
  34. s.lock.Lock()
  35. s.m[c] = true
  36. s.lock.Unlock()
  37. for {
  38. time.Sleep(s.FrameInterval)
  39. b := &lt;-c
  40. _, err := w.Write(b)
  41. if err != nil {
  42. break
  43. }
  44. }
  45. s.lock.Lock()
  46. delete(s.m, c)
  47. s.lock.Unlock()
  48. log.Println(&quot;Stream:&quot;, r.RemoteAddr, &quot;disconnected&quot;)
  49. }
  50. // UpdateJPEG pushes a new JPEG frame onto the clients.
  51. func (s *Stream) UpdateJPEG(jpeg []byte) {
  52. header := fmt.Sprintf(headerf, len(jpeg))
  53. if len(s.frame) &lt; len(jpeg)+len(header) {
  54. s.frame = make([]byte, (len(jpeg)+len(header))*2)
  55. }
  56. copy(s.frame, header)
  57. copy(s.frame[len(header):], jpeg)
  58. s.lock.Lock()
  59. for c := range s.m {
  60. // Select to skip streams which are sleeping to drop frames.
  61. // This might need more thought.
  62. select {
  63. case c &lt;- s.frame:
  64. default:
  65. }
  66. }
  67. s.lock.Unlock()
  68. }
  69. // NewStream initializes and returns a new Stream.
  70. func NewStream() *Stream {
  71. return &amp;Stream{
  72. m: make(map[chan []byte]bool),
  73. frame: make([]byte, len(headerf)),
  74. FrameInterval: 50 * time.Millisecond,
  75. }
  76. }

My output is as below:

在对象中放大MJPEG

My question is:

  1. How can I fit the streamed image into the selected object dimensions
  2. Is there a way to stream it to video element, I tried but failed.

答案1

得分: 0

我在go代码中找到了一个答案,它使用以下方式进行图像调整大小:

  1. "image/jpeg"
  2. "golang.org/x/image/draw"

代码如下:

  1. // 解码图像(从PNG到image.Image):
  2. src, _ := j.Decode(bytes.NewReader(jpeg))
  3. // 设置所需的大小:
  4. dst := image.NewRGBA(image.Rect(0, 0, src.Bounds().Max.X/3, src.Bounds().Max.Y/3))
  5. // 调整大小:
  6. draw.NearestNeighbor.Scale(dst, dst.Rect, src, src.Bounds(), draw.Over, nil)
  7. buf := new(bytes.Buffer)
  8. // 编码到`buf`:
  9. j.Encode(buf, dst, nil)
  10. copy(s.frame, header)
  11. // copy(s.frame[len(header):], jpeg)
  12. copy(s.frame[len(header):], buf.Bytes())

因此,我的完整流式代码如下:

  1. // Package mjpeg implements a simple MJPEG streamer.
  2. //
  3. // Stream objects implement the http.Handler interface, allowing to use them with the net/http package like so:
  4. // stream = mjpeg.NewStream()
  5. // http.Handle("/camera", stream)
  6. // Then push new JPEG frames to the connected clients using stream.UpdateJPEG().
  7. package mjpeg
  8. import (
  9. "bytes"
  10. "fmt"
  11. "image"
  12. j "image/jpeg"
  13. "log"
  14. "net/http"
  15. "sync"
  16. "time"
  17. "golang.org/x/image/draw"
  18. )
  19. // Stream represents a single video feed.
  20. type Stream struct {
  21. m map[chan []byte]bool
  22. frame []byte
  23. lock sync.Mutex
  24. FrameInterval time.Duration
  25. }
  26. const boundaryWord = "MJPEGBOUNDARY"
  27. const headerf = "\r\n" +
  28. "--" + boundaryWord + "\r\n" +
  29. "Content-Type: image/jpeg\r\n" +
  30. "Content-Length: %d\r\n" +
  31. "X-Timestamp: 0.000000\r\n" +
  32. "\r\n"
  33. // ServeHTTP responds to HTTP requests with the MJPEG stream, implementing the http.Handler interface.
  34. func (s *Stream) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  35. log.Println("Stream:", r.RemoteAddr, "connected")
  36. w.Header().Add("Content-Type", "multipart/x-mixed-replace;boundary="+boundaryWord)
  37. c := make(chan []byte)
  38. s.lock.Lock()
  39. s.m[c] = true
  40. s.lock.Unlock()
  41. for {
  42. time.Sleep(s.FrameInterval)
  43. b := <-c
  44. _, err := w.Write(b)
  45. if err != nil {
  46. break
  47. }
  48. }
  49. s.lock.Lock()
  50. delete(s.m, c)
  51. s.lock.Unlock()
  52. log.Println("Stream:", r.RemoteAddr, "disconnected")
  53. }
  54. // UpdateJPEG pushes a new JPEG frame onto the clients.
  55. func (s *Stream) UpdateJPEG(jpeg []byte) {
  56. header := fmt.Sprintf(headerf, len(jpeg))
  57. if len(s.frame) < len(jpeg)+len(header) {
  58. s.frame = make([]byte, (len(jpeg)+len(header))*2)
  59. }
  60. // 解码图像(从PNG到image.Image):
  61. src, _ := j.Decode(bytes.NewReader(jpeg))
  62. // 设置所需的大小:
  63. dst := image.NewRGBA(image.Rect(0, 0, src.Bounds().Max.X/3, src.Bounds().Max.Y/3))
  64. // 调整大小:
  65. draw.NearestNeighbor.Scale(dst, dst.Rect, src, src.Bounds(), draw.Over, nil)
  66. buf := new(bytes.Buffer)
  67. // 编码到`buf`:
  68. j.Encode(buf, dst, nil)
  69. copy(s.frame, header)
  70. // copy(s.frame[len(header):], jpeg)
  71. copy(s.frame[len(header):], buf.Bytes())
  72. s.lock.Lock()
  73. for c := range s.m {
  74. // 选择跳过正在休眠以丢弃帧的流。
  75. // 这可能需要更多的思考。
  76. select {
  77. case c <- s.frame:
  78. default:
  79. }
  80. }
  81. s.lock.Unlock()
  82. }
  83. // NewStream initializes and returns a new Stream.
  84. func NewStream() *Stream {
  85. return &Stream{
  86. m: make(map[chan []byte]bool),
  87. frame: make([]byte, len(headerf)),
  88. FrameInterval: 50 * time.Millisecond,
  89. }
  90. }

我的输出如下:

在对象中放大MJPEG

英文:

I found the an answer in the go code, that is doing image resizing using:

  1. &quot;image/jpeg&quot;
  2. &quot;golang.org/x/image/draw&quot;

As:

  1. // Decode the image (from PNG to image.Image):
  2. src, _ := j.Decode(bytes.NewReader(jpeg))
  3. // Set the expected size that you want:
  4. dst := image.NewRGBA(image.Rect(0, 0, src.Bounds().Max.X/3, src.Bounds().Max.Y/3))
  5. // Resize:
  6. draw.NearestNeighbor.Scale(dst, dst.Rect, src, src.Bounds(), draw.Over, nil)
  7. buf := new(bytes.Buffer)
  8. // Encode to `buf`:
  9. j.Encode(buf, dst, nil)
  10. copy(s.frame, header)
  11. // copy(s.frame[len(header):], jpeg)
  12. copy(s.frame[len(header):], buf.Bytes())

So, my streaming full code became:

  1. // Package mjpeg implements a simple MJPEG streamer.
  2. //
  3. // Stream objects implement the http.Handler interface, allowing to use them with the net/http package like so:
  4. // stream = mjpeg.NewStream()
  5. // http.Handle(&quot;/camera&quot;, stream)
  6. // Then push new JPEG frames to the connected clients using stream.UpdateJPEG().
  7. package mjpeg
  8. import (
  9. &quot;bytes&quot;
  10. &quot;fmt&quot;
  11. &quot;image&quot;
  12. j &quot;image/jpeg&quot;
  13. &quot;log&quot;
  14. &quot;net/http&quot;
  15. &quot;sync&quot;
  16. &quot;time&quot;
  17. &quot;golang.org/x/image/draw&quot;
  18. )
  19. // Stream represents a single video feed.
  20. type Stream struct {
  21. m map[chan []byte]bool
  22. frame []byte
  23. lock sync.Mutex
  24. FrameInterval time.Duration
  25. }
  26. const boundaryWord = &quot;MJPEGBOUNDARY&quot;
  27. const headerf = &quot;\r\n&quot; +
  28. &quot;--&quot; + boundaryWord + &quot;\r\n&quot; +
  29. &quot;Content-Type: image/jpeg\r\n&quot; +
  30. &quot;Content-Length: %d\r\n&quot; +
  31. &quot;X-Timestamp: 0.000000\r\n&quot; +
  32. &quot;\r\n&quot;
  33. // ServeHTTP responds to HTTP requests with the MJPEG stream, implementing the http.Handler interface.
  34. func (s *Stream) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  35. log.Println(&quot;Stream:&quot;, r.RemoteAddr, &quot;connected&quot;)
  36. w.Header().Add(&quot;Content-Type&quot;, &quot;multipart/x-mixed-replace;boundary=&quot;+boundaryWord)
  37. c := make(chan []byte)
  38. s.lock.Lock()
  39. s.m[c] = true
  40. s.lock.Unlock()
  41. for {
  42. time.Sleep(s.FrameInterval)
  43. b := &lt;-c
  44. _, err := w.Write(b)
  45. if err != nil {
  46. break
  47. }
  48. }
  49. s.lock.Lock()
  50. delete(s.m, c)
  51. s.lock.Unlock()
  52. log.Println(&quot;Stream:&quot;, r.RemoteAddr, &quot;disconnected&quot;)
  53. }
  54. // UpdateJPEG pushes a new JPEG frame onto the clients.
  55. func (s *Stream) UpdateJPEG(jpeg []byte) {
  56. header := fmt.Sprintf(headerf, len(jpeg))
  57. if len(s.frame) &lt; len(jpeg)+len(header) {
  58. s.frame = make([]byte, (len(jpeg)+len(header))*2)
  59. }
  60. // Decode the image (from PNG to image.Image):
  61. src, _ := j.Decode(bytes.NewReader(jpeg))
  62. // Set the expected size that you want:
  63. dst := image.NewRGBA(image.Rect(0, 0, src.Bounds().Max.X/3, src.Bounds().Max.Y/3))
  64. // Resize:
  65. draw.NearestNeighbor.Scale(dst, dst.Rect, src, src.Bounds(), draw.Over, nil)
  66. buf := new(bytes.Buffer)
  67. // Encode to `buf`:
  68. j.Encode(buf, dst, nil)
  69. copy(s.frame, header)
  70. // copy(s.frame[len(header):], jpeg)
  71. copy(s.frame[len(header):], buf.Bytes())
  72. s.lock.Lock()
  73. for c := range s.m {
  74. // Select to skip streams which are sleeping to drop frames.
  75. // This might need more thought.
  76. select {
  77. case c &lt;- s.frame:
  78. default:
  79. }
  80. }
  81. s.lock.Unlock()
  82. }
  83. // NewStream initializes and returns a new Stream.
  84. func NewStream() *Stream {
  85. return &amp;Stream{
  86. m: make(map[chan []byte]bool),
  87. frame: make([]byte, len(headerf)),
  88. FrameInterval: 50 * time.Millisecond,
  89. }
  90. }

Any my output became:

在对象中放大MJPEG

huangapple
  • 本文由 发表于 2022年6月11日 18:42:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/72583642.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定