diff --git a/README.md b/README.md index 9ff499a..ace35b4 100644 --- a/README.md +++ b/README.md @@ -19,10 +19,11 @@ or Binaries are compiled with static OpenCV/libjpeg-turbo libraries, they should just work: - - [Linux 64bit](https://github.com/gen2brain/cam2ip/releases/download/1.3/cam2ip-1.3-64bit.tar.gz) - - [RPi 32bit](https://github.com/gen2brain/cam2ip/releases/download/1.3/cam2ip-1.3-RPi.tar.gz) - - [RPi3 32bit](https://github.com/gen2brain/cam2ip/releases/download/1.3/cam2ip-1.3-RPi3.tar.gz) - - [Windows 32bit](https://github.com/gen2brain/cam2ip/releases/download/1.3/cam2ip-1.3.zip) + - [Linux 64bit](https://github.com/gen2brain/cam2ip/releases/download/1.4/cam2ip-1.4-64bit.tar.gz) + - [RPi 32bit](https://github.com/gen2brain/cam2ip/releases/download/1.4/cam2ip-1.4-RPi.tar.gz) + - [RPi3 32bit](https://github.com/gen2brain/cam2ip/releases/download/1.4/cam2ip-1.4-RPi3.tar.gz) + - [Windows 32bit](https://github.com/gen2brain/cam2ip/releases/download/1.4/cam2ip-1.4.zip) + - [Windows 64bit](https://github.com/gen2brain/cam2ip/releases/download/1.4/cam2ip-1.4-64bit.zip) ### Installation @@ -49,6 +50,8 @@ Usage of ./cam2ip: Camera index -nowebgl Disable WebGL drawing of images (html handler) + -rotate int + Rotate image, valid values are 90, 180, 270 -video-file string Use video file instead of camera ``` diff --git a/cam2ip.go b/cam2ip.go index 4c24a4b..3653c8a 100644 --- a/cam2ip.go +++ b/cam2ip.go @@ -12,7 +12,7 @@ import ( const ( name = "cam2ip" - version = "1.3" + version = "1.4" ) func main() { @@ -22,6 +22,7 @@ func main() { flag.IntVar(&srv.Delay, "delay", 10, "Delay between frames, in milliseconds") flag.Float64Var(&srv.FrameWidth, "width", 640, "Frame width") flag.Float64Var(&srv.FrameHeight, "height", 480, "Frame height") + flag.IntVar(&srv.Rotate, "rotate", 0, "Rotate image, valid values are 90, 180, 270") flag.BoolVar(&srv.NoWebGL, "nowebgl", false, "Disable WebGL drawing of images (html handler)") flag.StringVar(&srv.Bind, "bind-addr", ":56000", "Bind address") flag.StringVar(&srv.Htpasswd, "htpasswd-file", "", "Path to htpasswd file, if empty auth is disabled") @@ -48,7 +49,7 @@ func main() { } if srv.FileName != "" { - vid, err := video.New(srv.FileName) + vid, err := video.New(video.Options{srv.FileName, srv.Rotate}) if err != nil { fmt.Fprintf(os.Stderr, "%s\n", err.Error()) os.Exit(1) @@ -56,7 +57,7 @@ func main() { srv.Reader = vid } else { - cam, err := camera.New(srv.Index) + cam, err := camera.New(camera.Options{srv.Index, srv.Rotate}) if err != nil { fmt.Fprintf(os.Stderr, "%s\n", err.Error()) os.Exit(1) diff --git a/camera/camera.go b/camera/camera.go index 2804f67..532da20 100644 --- a/camera/camera.go +++ b/camera/camera.go @@ -7,22 +7,31 @@ import ( "fmt" "image" + "github.com/disintegration/imaging" "github.com/lazywei/go-opencv/opencv" ) +// Options. +type Options struct { + Index int + Rotate int +} + // Camera represents camera. type Camera struct { + opts Options camera *opencv.Capture frame *opencv.IplImage } // New returns new Camera for given camera index. -func New(index int) (camera *Camera, err error) { +func New(opts Options) (camera *Camera, err error) { camera = &Camera{} + camera.opts = opts - camera.camera = opencv.NewCameraCapture(index) + camera.camera = opencv.NewCameraCapture(opts.Index) if camera.camera == nil { - err = fmt.Errorf("camera: can not open camera %d", index) + err = fmt.Errorf("camera: can not open camera %d", opts.Index) } return @@ -32,7 +41,25 @@ func New(index int) (camera *Camera, err error) { func (c *Camera) Read() (img image.Image, err error) { if c.camera.GrabFrame() { c.frame = c.camera.RetrieveFrame(1) + + if c.frame == nil { + err = fmt.Errorf("camera: can not grab frame") + return + } + img = c.frame.ToImage() + if c.opts.Rotate == 0 { + return + } + + switch c.opts.Rotate { + case 90: + img = imaging.Rotate90(img) + case 180: + img = imaging.Rotate180(img) + case 270: + img = imaging.Rotate270(img) + } } else { err = fmt.Errorf("camera: can not grab frame") } diff --git a/camera/camera_cv3.go b/camera/camera_cv3.go index 8054ce7..238ab1f 100644 --- a/camera/camera_cv3.go +++ b/camera/camera_cv3.go @@ -7,25 +7,34 @@ import ( "fmt" "image" + "github.com/disintegration/imaging" "gocv.io/x/gocv" ) +// Options. +type Options struct { + Index int + Rotate int +} + // Camera represents camera. type Camera struct { + opts Options camera *gocv.VideoCapture frame *gocv.Mat } // New returns new Camera for given camera index. -func New(index int) (camera *Camera, err error) { +func New(opts Options) (camera *Camera, err error) { camera = &Camera{} + camera.opts = opts mat := gocv.NewMat() camera.frame = &mat - camera.camera, err = gocv.VideoCaptureDevice(index) + camera.camera, err = gocv.VideoCaptureDevice(opts.Index) if err != nil { - err = fmt.Errorf("camera: can not open camera %d: %s", index, err.Error()) + err = fmt.Errorf("camera: can not open camera %d: %s", opts.Index, err.Error()) } return @@ -45,6 +54,24 @@ func (c *Camera) Read() (img image.Image, err error) { return } + if c.frame == nil { + err = fmt.Errorf("camera: can not grab frame") + return + } + + if c.opts.Rotate == 0 { + return + } + + switch c.opts.Rotate { + case 90: + img = imaging.Rotate90(img) + case 180: + img = imaging.Rotate180(img) + case 270: + img = imaging.Rotate270(img) + } + return } diff --git a/make.bash b/make.bash index 8244294..ab9e0bf 100755 --- a/make.bash +++ b/make.bash @@ -2,6 +2,7 @@ CHROOT="/usr/x86_64-pc-linux-gnu-static" MINGW="/usr/i686-w64-mingw32" +MINGW64="/usr/x86_64-w64-mingw32" RPI="/usr/armv6j-hardfloat-linux-gnueabi" RPI3="/usr/armv7a-hardfloat-linux-gnueabi" @@ -14,7 +15,6 @@ CGO_LDFLAGS="-L$CHROOT/usr/lib -L$CHROOT/lib" \ CGO_CFLAGS="-I$CHROOT/usr/include" \ CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -v -x -o build/cam2ip.linux.amd64 -ldflags "-linkmode external -s -w" github.com/gen2brain/cam2ip - PKG_CONFIG="/usr/bin/i686-w64-mingw32-pkg-config" \ PKG_CONFIG_PATH="$MINGW/usr/lib/pkgconfig" \ PKG_CONFIG_LIBDIR="$MINGW/usr/lib/pkgconfig" \ @@ -23,6 +23,14 @@ CGO_CFLAGS="-I$MINGW/usr/include" \ CC="i686-w64-mingw32-gcc" CXX="i686-w64-mingw32-g++" \ CGO_ENABLED=1 GOOS=windows GOARCH=386 go build -v -x -o build/cam2ip.exe -ldflags "-linkmode external -s -w '-extldflags=-static'" github.com/gen2brain/cam2ip +PKG_CONFIG="/usr/bin/x86_64-w64-mingw32-pkg-config" \ +PKG_CONFIG_PATH="$MINGW64/usr/lib/pkgconfig" \ +PKG_CONFIG_LIBDIR="$MINGW64/usr/lib/pkgconfig" \ +CGO_LDFLAGS="-L$MINGW64/usr/lib" \ +CGO_CFLAGS="-I$MINGW64/usr/include" \ +CC="x86_64-w64-mingw32-gcc" CXX="x86_64-w64-mingw32-g++" \ +CGO_ENABLED=1 GOOS=windows GOARCH=amd64 go build -v -x -o build/cam2ip.exe.amd64 -ldflags "-linkmode external -s -w '-extldflags=-static'" github.com/gen2brain/cam2ip + PKG_CONFIG="/usr/bin/armv6j-hardfloat-linux-gnueabi-pkg-config" \ PKG_CONFIG_PATH="$RPI/usr/lib/pkgconfig" \ PKG_CONFIG_LIBDIR="$RPI/usr/lib/pkgconfig" \ diff --git a/server/server.go b/server/server.go index aa2721e..5444d8f 100644 --- a/server/server.go +++ b/server/server.go @@ -26,6 +26,8 @@ type Server struct { FrameWidth float64 FrameHeight float64 + Rotate int + NoWebGL bool FileName string diff --git a/video/video.go b/video/video.go index 5e28532..5ee31ce 100644 --- a/video/video.go +++ b/video/video.go @@ -7,22 +7,31 @@ import ( "fmt" "image" + "github.com/disintegration/imaging" "github.com/lazywei/go-opencv/opencv" ) +// Options. +type Options struct { + Filename string + Rotate int +} + // Video represents video. type Video struct { + opts Options video *opencv.Capture frame *opencv.IplImage } // New returns new Video for given path. -func New(filename string) (video *Video, err error) { +func New(opts Options) (video *Video, err error) { video = &Video{} + video.opts = opts - video.video = opencv.NewFileCapture(filename) + video.video = opencv.NewFileCapture(opts.Filename) if video.video == nil { - err = fmt.Errorf("video: can not open video %s", filename) + err = fmt.Errorf("video: can not open video %s", opts.Filename) } return @@ -32,7 +41,24 @@ func New(filename string) (video *Video, err error) { func (v *Video) Read() (img image.Image, err error) { if v.video.GrabFrame() { v.frame = v.video.RetrieveFrame(1) + if v.frame == nil { + err = fmt.Errorf("video: can not grab frame") + return + } + img = v.frame.ToImage() + if v.opts.Rotate == 0 { + return + } + + switch v.opts.Rotate { + case 90: + img = imaging.Rotate90(img) + case 180: + img = imaging.Rotate180(img) + case 270: + img = imaging.Rotate270(img) + } } else { err = fmt.Errorf("video: can not grab frame") } diff --git a/video/video_cv3.go b/video/video_cv3.go index 629c68e..30e2027 100644 --- a/video/video_cv3.go +++ b/video/video_cv3.go @@ -7,25 +7,34 @@ import ( "fmt" "image" + "github.com/disintegration/imaging" "gocv.io/x/gocv" ) +// Options. +type Options struct { + Filename string + Rotate int +} + // Video represents video. type Video struct { + opts Options video *gocv.VideoCapture frame *gocv.Mat } // New returns new Video for given path. -func New(filename string) (video *Video, err error) { +func New(opts Options) (video *Video, err error) { video = &Video{} + video.opts = opts mat := gocv.NewMat() video.frame = &mat - video.video, err = gocv.VideoCaptureFile(filename) + video.video, err = gocv.VideoCaptureFile(opts.Filename) if err != nil { - err = fmt.Errorf("video: can not open video %s: %s", filename, err.Error()) + err = fmt.Errorf("video: can not open video %s: %s", opts.Filename, err.Error()) } return @@ -39,12 +48,30 @@ func (v *Video) Read() (img image.Image, err error) { return } + if v.frame == nil { + err = fmt.Errorf("video: can not grab frame") + return + } + img, e := v.frame.ToImage() if e != nil { err = fmt.Errorf("video: %v", e) return } + if v.opts.Rotate == 0 { + return + } + + switch v.opts.Rotate { + case 90: + img = imaging.Rotate90(img) + case 180: + img = imaging.Rotate180(img) + case 270: + img = imaging.Rotate270(img) + } + return }