First implementation
This commit is contained in:
		
							
								
								
									
										81
									
								
								camera/camera.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								camera/camera.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,81 @@
 | 
			
		||||
package camera
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/cyrilix/robocar-base/mqttdevice"
 | 
			
		||||
	"gocv.io/x/gocv"
 | 
			
		||||
	"io"
 | 
			
		||||
	"log"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type VideoSource interface {
 | 
			
		||||
	Read(m *gocv.Mat) bool
 | 
			
		||||
	io.Closer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type OpencvCameraPart struct {
 | 
			
		||||
	vc               VideoSource
 | 
			
		||||
	pub              mqttdevice.Publisher
 | 
			
		||||
	topic            string
 | 
			
		||||
	publishFrequency int
 | 
			
		||||
	muImgBuffered    sync.Mutex
 | 
			
		||||
	imgBuffered      *gocv.Mat
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(topic string, publisher mqttdevice.Publisher, publishFrequency int, videoProperties map[gocv.VideoCaptureProperties]float64) *OpencvCameraPart {
 | 
			
		||||
	log.Printf("Run camera part")
 | 
			
		||||
 | 
			
		||||
	vc, err := gocv.OpenVideoCapture(0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("unable to open video device: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	for k, v := range videoProperties {
 | 
			
		||||
		vc.Set(k, v)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	img := gocv.NewMat()
 | 
			
		||||
	o := OpencvCameraPart{
 | 
			
		||||
		vc:               vc,
 | 
			
		||||
		pub:              publisher,
 | 
			
		||||
		topic:            topic,
 | 
			
		||||
		publishFrequency: publishFrequency,
 | 
			
		||||
		imgBuffered:      &img,
 | 
			
		||||
	}
 | 
			
		||||
	return &o
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *OpencvCameraPart) Start() error {
 | 
			
		||||
	log.Printf("start camera")
 | 
			
		||||
	ticker := time.NewTicker(1 * time.Second / time.Duration(o.publishFrequency))
 | 
			
		||||
	defer ticker.Stop()
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		go o.publishFrame()
 | 
			
		||||
		<-ticker.C
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *OpencvCameraPart) Stop() {
 | 
			
		||||
	log.Print("close video device")
 | 
			
		||||
	if err := o.vc.Close(); err != nil {
 | 
			
		||||
		log.Printf("unexpected error while VideoCapture is closed: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if err := o.imgBuffered.Close(); err != nil {
 | 
			
		||||
		log.Printf("unexpected error while VideoCapture is closed: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *OpencvCameraPart) publishFrame() {
 | 
			
		||||
	o.muImgBuffered.Lock()
 | 
			
		||||
	defer o.muImgBuffered.Unlock()
 | 
			
		||||
 | 
			
		||||
	o.vc.Read(o.imgBuffered)
 | 
			
		||||
	img, err := gocv.IMEncode(gocv.JPEGFileExt, *o.imgBuffered)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Printf("unable to convert image to jpeg: %v", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	o.pub.Publish(o.topic, img)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										57
									
								
								camera/camera_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								camera/camera_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
package camera
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"github.com/cyrilix/robocar-base/testtools"
 | 
			
		||||
	"gocv.io/x/gocv"
 | 
			
		||||
	"image/jpeg"
 | 
			
		||||
	"io"
 | 
			
		||||
	"log"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type fakeVideoSource struct {
 | 
			
		||||
	io.Closer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f fakeVideoSource) Read(dest *gocv.Mat) bool {
 | 
			
		||||
	img := gocv.IMRead("testdata/img.jpg", gocv.IMReadUnchanged)
 | 
			
		||||
	if img.Total() == 0 {
 | 
			
		||||
		log.Print("image read is empty")
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	img.CopyTo(dest)
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestOpencvCameraPart(t *testing.T) {
 | 
			
		||||
	p := testtools.NewFakePublisher()
 | 
			
		||||
	const topic = "topic/test/camera"
 | 
			
		||||
	imgBuffer := gocv.NewMat()
 | 
			
		||||
 | 
			
		||||
	part := OpencvCameraPart{
 | 
			
		||||
		vc:               fakeVideoSource{},
 | 
			
		||||
		pub:              p,
 | 
			
		||||
		topic:            topic,
 | 
			
		||||
		publishFrequency: 1000,
 | 
			
		||||
		imgBuffered:      &imgBuffer,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	go part.Start()
 | 
			
		||||
	time.Sleep(1 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
	img := p.PublishedEvent(topic)
 | 
			
		||||
	if img == nil {
 | 
			
		||||
		t.Fatalf("event %s has not been published", topic)
 | 
			
		||||
	}
 | 
			
		||||
	content, err := img.ByteSliceValue()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("unexpected error: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err = jpeg.Decode(bytes.NewReader(content))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("image published can't be decoded: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								camera/testdata/img.jpg
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								camera/testdata/img.jpg
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 2.1 KiB  | 
		Reference in New Issue
	
	Block a user