diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..de651b9 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,31 @@ +name: Run lint & unit tests + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + name: "Run unit tests" + runs-on: [buildjet-2vcpu-ubuntu-2204-arm] + container: + image: ghcr.io/viamrobotics/ocean-prefilter:arm64 + options: --platform linux/arm64 + + steps: + - uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: '1.21' + + + + - name: Run lint + run: make lint + + - name: Run unit tests + run: make test \ No newline at end of file diff --git a/Makefile b/Makefile index ea8135c..2ccc85f 100644 --- a/Makefile +++ b/Makefile @@ -69,3 +69,16 @@ docker-amd64-ci: BUILD_PUSH = --push docker-amd64-ci: $(BUILD_CMD) +# Test target for running Go tests +test: + LD_LIBRARY_PATH=/usr/local/lib:/usr/lib go test ./oceanprefilter + + +# Docker image and container details +DOCKER_IMAGE := ghcr.io/viamrobotics/ocean-prefilter:arm64 + +# Lint rule +lint: + GOFLAGS="-buildvcs=false" golangci-lint run --timeout 10m ./ + + diff --git a/etc/Dockerfile.debian.bookworm b/etc/Dockerfile.debian.bookworm index 5a01f73..c2f21c5 100644 --- a/etc/Dockerfile.debian.bookworm +++ b/etc/Dockerfile.debian.bookworm @@ -125,4 +125,7 @@ RUN cd /root/opt/src && \ # install appimage-builder RUN pip3 install --break-system-packages git+https://github.com/AppImageCrafters/appimage-builder.git@61c8ddde9ef44b85d7444bbe79d80b44a6a5576d +# golangci-lint installation here +RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b /usr/local/bin v1.53.2 + ENV PATH="/usr/local/go/bin:${PATH}" diff --git a/go.mod b/go.mod index 0bf5989..0784476 100644 --- a/go.mod +++ b/go.mod @@ -2,11 +2,14 @@ module github.com/viamrobotics/ocean-prefilter go 1.21.7 +toolchain go1.21.12 + require ( github.com/pkg/errors v0.9.1 go.viam.com/rdk v0.28.0 + go.viam.com/test v1.1.1-0.20220913152726-5da9916c08a2 go.viam.com/utils v0.1.79 - gocv.io/x/gocv v0.36.1 + gocv.io/x/gocv v0.37.0 ) require ( @@ -45,6 +48,7 @@ require ( github.com/go-gl/mathgl v1.0.0 // indirect github.com/go-latex/latex v0.0.0-20230307184459-12ec69307ad9 // indirect github.com/go-pdf/fpdf v0.6.0 // indirect + github.com/go-playground/validator/v10 v10.14.0 // indirect github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect @@ -54,6 +58,7 @@ require ( github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/gonuts/binary v0.2.0 // indirect + github.com/google/flatbuffers v2.0.8+incompatible // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/s2a-go v0.1.4 // indirect github.com/google/uuid v1.6.0 // indirect @@ -65,8 +70,11 @@ require ( github.com/improbable-eng/grpc-web v0.15.0 // indirect github.com/jedib0t/go-pretty/v6 v6.4.6 // indirect github.com/jhump/protoreflect v1.15.1 // indirect + github.com/json-iterator/go v1.1.12 // indirect github.com/kellydunn/golang-geo v0.7.0 // indirect github.com/klauspost/compress v1.16.5 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/kylelemons/go-gypsy v1.0.0 // indirect github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect github.com/lestrrat-go/blackmagic v1.0.2 // indirect @@ -104,11 +112,13 @@ require ( github.com/pion/webrtc/v3 v3.2.36 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.4.4 // indirect + github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/rs/cors v1.9.0 // indirect github.com/sergi/go-diff v1.3.1 // indirect github.com/smartystreets/assertions v1.13.0 // indirect github.com/srikrsna/protoc-gen-gotag v0.6.2 // indirect github.com/stretchr/testify v1.9.0 // indirect + github.com/ugorji/go/codec v1.2.11 // indirect github.com/viam-labs/go-libjpeg v0.3.1 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect @@ -125,7 +135,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.24.0 // indirect go.viam.com/api v0.1.302 // indirect - go.viam.com/test v1.1.1-0.20220913152726-5da9916c08a2 // indirect + golang.org/x/arch v0.3.0 // indirect golang.org/x/crypto v0.23.0 // indirect golang.org/x/exp v0.0.0-20230725012225-302865e7556b // indirect golang.org/x/image v0.15.0 // indirect diff --git a/go.sum b/go.sum index c8c9a41..130906a 100644 --- a/go.sum +++ b/go.sum @@ -257,6 +257,8 @@ github.com/fullstorydev/grpcurl v1.6.0/go.mod h1:ZQ+ayqbKMJNhzLmbpCiurTVlaK2M/3n github.com/fullstorydev/grpcurl v1.8.6 h1:WylAwnPauJIofYSHqqMTC1eEfUIzqzevXyogBxnQquo= github.com/fullstorydev/grpcurl v1.8.6/go.mod h1:WhP7fRQdhxz2TkL97u+TCb505sxfH78W1usyoB3tepw= github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gen2brain/malgo v0.11.21 h1:qsS4Dh6zhZgmvAW5CtKRxDjQzHbc2NJlBG9eE0tgS8w= github.com/gen2brain/malgo v0.11.21/go.mod h1:f9TtuN7DVrXMiV/yIceMeWpvanyVzJQMlBecJFVMxww= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -303,12 +305,15 @@ github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNI github.com/go-pdf/fpdf v0.6.0 h1:MlgtGIfsdMEEQJr2le6b/HNr1ZlQwxyWr77r2aj2U/8= github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= +github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-restruct/restruct v1.2.0-alpha.0.20210525045353-983b86fa188e h1:PIFVUcdZ9OADg9XAsN0I8OzUzmYXHU+2msP2X7ST/fo= github.com/go-restruct/restruct v1.2.0-alpha.0.20210525045353-983b86fa188e/go.mod h1:KqrpKpn4M8OLznErihXTGLlsXFGeLxHUrLRRI/1YjGk= @@ -415,8 +420,8 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= github.com/google/certificate-transparency-go v1.1.1/go.mod h1:FDKqPvSXawb2ecErVRrD+nfy23RCzyl7eqVCEmlT1Zs= -github.com/google/flatbuffers v2.0.6+incompatible h1:XHFReMv7nFFusa+CEokzWbzaYocKXI6C7hdU5Kgh9Lw= -github.com/google/flatbuffers v2.0.6+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/flatbuffers v2.0.8+incompatible h1:ivUb1cGomAB101ZM1T0nOiWz9pSrTMoa9+EiY7igmkM= +github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -562,8 +567,9 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= @@ -588,16 +594,18 @@ github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -609,8 +617,9 @@ github.com/kylelemons/go-gypsy v1.0.0/go.mod h1:chkXM0zjdpXOiqkCW1XcCHDfjfk14PH2 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/kyoh86/exportloopref v0.1.8/go.mod h1:1tUcJeiioIs7VWe5gcOObrux3lb66+sBqGZrRkMwPgg= github.com/ldez/gomoddirectives v0.2.1/go.mod h1:sGicqkRgBOg//JfpXwkB9Hj0X5RyJ7mlACM5B9f6Me4= -github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A= github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k= @@ -707,8 +716,9 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/montanaflynn/stats v0.7.0 h1:r3y12KyNxj/Sb/iOE46ws+3mS1+MZca1wlHQFPsY/JU= @@ -837,6 +847,7 @@ github.com/pion/turn/v2 v2.1.3 h1:pYxTVWG2gpC97opdRc5IGsQ1lJ9O/IlNhkzj7MMrGAA= github.com/pion/turn/v2 v2.1.3/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY= github.com/pion/webrtc/v3 v3.2.36 h1:RM/miAv0M4TrhhS7h2mcZXt44K68WmpVDkUOgz2l2l8= github.com/pion/webrtc/v3 v3.2.36/go.mod h1:wWQz1PuKNSNK4VrJJNpPN3vZmKEi4zA6i2ynaQOlxIU= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -898,6 +909,9 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE= github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= @@ -1004,11 +1018,11 @@ github.com/u2takey/ffmpeg-go v0.4.1 h1:l5ClIwL3N2LaH1zF3xivb3kP2HW95eyG5xhHE1JdZ github.com/u2takey/ffmpeg-go v0.4.1/go.mod h1:ruZWkvC1FEiUNjmROowOAps3ZcWxEiOpFoHCvk97kGc= github.com/u2takey/go-utils v0.3.1 h1:TaQTgmEZZeDHQFYfd+AdUT1cT4QJgJn/XVPELhHw4ys= github.com/u2takey/go-utils v0.3.1/go.mod h1:6e+v5vEZ/6gu12w/DC2ixZdZtCrNokVxD0JUklcqdCs= -github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= @@ -1109,12 +1123,13 @@ go.viam.com/utils v0.1.79 h1:rLxMPuJifGaDqQgGsfgzy60dMz6P44n5oEBbLEdggaM= go.viam.com/utils v0.1.79/go.mod h1:3xcBBlLsRX2eWpWuQrFJ7UVxsgXwJg8Fx30Rt/kp52g= go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 h1:WJhcL4p+YeDxmZWg141nRm7XC8IDmhz7lk5GpadO1Sg= go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= -gocv.io/x/gocv v0.36.1 h1:6XkEaPOk7h/umjy+MXgSEtSeCIgcPJhccUjrJFhjdTY= -gocv.io/x/gocv v0.36.1/go.mod h1:lmS802zoQmnNvXETpmGriBqWrENPei2GxYx5KUxJsMA= +gocv.io/x/gocv v0.37.0 h1:sISHvnApErjoJodz1Dxb8UAkFdITOB3vXGslbVu6Knk= +gocv.io/x/gocv v0.37.0/go.mod h1:lmS802zoQmnNvXETpmGriBqWrENPei2GxYx5KUxJsMA= goji.io v2.0.2+incompatible h1:uIssv/elbKRLznFUy3Xj4+2Mz/qKhek/9aZQDUMae7c= goji.io v2.0.2+incompatible/go.mod h1:sbqFwrtqZACxLBTQcdgVjFh54yGVCvwq8+w49MVMMIk= -golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= +golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1330,6 +1345,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/oceanprefilter/histogram_compare.go b/oceanprefilter/histogram_compare.go index 46f6b47..7aedb94 100644 --- a/oceanprefilter/histogram_compare.go +++ b/oceanprefilter/histogram_compare.go @@ -3,7 +3,6 @@ package oceanprefilter import ( "fmt" "image" - "image/draw" "math" "github.com/pkg/errors" @@ -113,15 +112,6 @@ func histogramTrigger(oldHist, newHist Histogram, thresh float64) bool { return trigger } -func convertToFloat64(uint8Slice []uint8) []float64 { - float64Slice := make([]float64, len(uint8Slice)) - - for i, v := range uint8Slice { - float64Slice[i] = float64(v) - } - return float64Slice -} - func createGrayHistograms(pic image.Image) []Histogram { hists := make([]Histogram, 0, 1) img := toGray(pic) @@ -136,47 +126,6 @@ func createGrayHistograms(pic image.Image) []Histogram { return hists } -func createColorHistograms(pic image.Image) []Histogram { - hists := make([]Histogram, 0, 3) - img := image.NewRGBA(pic.Bounds()) - if rgbaImg, ok := pic.(*image.RGBA); ok { - img = rgbaImg - } else { - draw.Draw(img, pic.Bounds(), pic, pic.Bounds().Min, draw.Src) - } - rPix := []float64{} - gPix := []float64{} - bPix := []float64{} - for x := 0; x < img.Bounds().Dx(); x++ { - for y := 0; y < img.Bounds().Dy(); y++ { - c := img.RGBAAt(x, y) - rPix = append(rPix, float64(c.R)) - gPix = append(gPix, float64(c.G)) - bPix = append(bPix, float64(c.B)) - } - } - hists = append(hists, Hist(32, 0, 256, rPix)) // 32 bin, 8 values in each bin in 255 total - hists = append(hists, Hist(32, 0, 256, gPix)) // 32 bin, 8 values in each bin in 255 total - hists = append(hists, Hist(32, 0, 256, bPix)) // 32 bin, 8 values in each bin in 255 total - return hists -} - -// basicCompare compares two histograms and returns a measure of their difference -func basicCompare(hist1, hist2 Histogram) float64 { - var sum float64 - for i := range hist1.Buckets { - diff := hist1.Buckets[i].Count - hist2.Buckets[i].Count - if diff < 0 { - diff = -diff - } - sum += float64(diff) - } - // Normalize the difference based on the number of pixels - totalPixels := float64(hist1.Count + hist2.Count) // hist2 has the same number of pixels - score := sum / totalPixels - return score -} - // histogramToECDF converts a histogram to an empirical cumulative distribution function (ECDF) // Assumes histogram bins are evenly distributed over the data range func histogramToECDF(hist Histogram) []float64 { diff --git a/oceanprefilter/ml_filtering.go b/oceanprefilter/ml_filtering.go deleted file mode 100644 index dae99e2..0000000 --- a/oceanprefilter/ml_filtering.go +++ /dev/null @@ -1,31 +0,0 @@ -package oceanprefilter - -import ( - "context" - "image" -) - -func mlFilter(ctx context.Context, img image.Image, rc runConfig) (bool, error) { - // first try if the optional detection is present - if rc.detector == nil { - return false, nil - } - dets, _ := rc.detector.Detections(ctx, img, nil) - if dets == nil { - return false, nil - } - if rc.chosenLabels == nil { - if len(dets) != 0 { - return true, nil - } - return false, nil - } - for _, d := range dets { - if conf, ok := rc.chosenLabels[d.Label()]; ok { - if d.Score() >= conf { - return true, nil - } - } - } - return false, nil -} diff --git a/oceanprefilter/oceanprefilter.go b/oceanprefilter/oceanprefilter.go index 0f51756..05c4252 100644 --- a/oceanprefilter/oceanprefilter.go +++ b/oceanprefilter/oceanprefilter.go @@ -91,7 +91,6 @@ type runConfig struct { detector vision.Service chosenLabels map[string]float64 frequency float64 - minConfidence float64 threshold float64 excludedZone *image.Rectangle motionTrigger bool diff --git a/oceanprefilter/oceanprefilter_test.go b/oceanprefilter/oceanprefilter_test.go new file mode 100644 index 0000000..10e2861 --- /dev/null +++ b/oceanprefilter/oceanprefilter_test.go @@ -0,0 +1,248 @@ +package oceanprefilter + +import ( + "context" + "image" + "image/color" + "sync/atomic" + "testing" + "unsafe" + + "go.viam.com/rdk/services/vision" + "go.viam.com/rdk/vision/classification" + "go.viam.com/rdk/vision/viscapture" + "go.viam.com/test" +) + +// MockImage creates a mock RGBA image for testing purposes. +func MockImage(width, height int) *image.RGBA { + img := image.NewRGBA(image.Rect(0, 0, width, height)) + // Fill the image with a solid color (e.g., white) + drawColor := color.RGBA{R: 255, G: 255, B: 255, A: 255} + for y := 0; y < height; y++ { + for x := 0; x < width; x++ { + img.Set(x, y, drawColor) + } + } + return img +} + +func TestConfigValidate(t *testing.T) { + // Test case where camera name is empty + cfg := &Config{ + CameraName: "", + } + path := "test_path" + dependencies, err := cfg.Validate(path) + test.That(t, dependencies, test.ShouldBeNil) + test.That(t, err.Error(), test.ShouldEqual, `expected "camera_name" attribute for object tracker "test_path"`) + + // Test case where detector name is empty + cfg = &Config{ + CameraName: "camera1", + DetectorName: "", + } + path = "test_path" + dependencies, err = cfg.Validate(path) + test.That(t, dependencies, test.ShouldResemble, []string{"camera1"}) + test.That(t, err, test.ShouldBeNil) + + // Test case where both camera name and detector name are provided + cfg = &Config{ + CameraName: "camera1", + DetectorName: "detector1", + } + path = "test_path" + dependencies, err = cfg.Validate(path) + test.That(t, dependencies, test.ShouldResemble, []string{"camera1", "detector1"}) + test.That(t, err, test.ShouldBeNil) +} + +func TestClassificationsFromCamera(t *testing.T) { + pf := &prefilter{ + camName: "configuredCamera", + triggerFlag: &atomic.Bool{}, + cancelContext: context.Background(), + } + ctx := context.Background() + cameraName := "testCamera" + + // Test case where camera name does not match + classifications, err := pf.ClassificationsFromCamera(ctx, cameraName, 1, nil) + test.That(t, classifications, test.ShouldBeNil) + test.That(t, err.Error(), test.ShouldEqual, "camera name given to method, testCamera is not the same as configured camera configuredCamera") + + // Test case where context is done + cancelledCtx, cancel := context.WithCancel(ctx) + cancel() + classifications, err = pf.ClassificationsFromCamera(cancelledCtx, "configuredCamera", 1, nil) + test.That(t, classifications, test.ShouldBeNil) + test.That(t, err.Error(), test.ShouldEqual, "module might be configuring: context canceled") + + // Test case where internal context is done + cancelledInternalCtx, internalCancel := context.WithCancel(ctx) + pf.cancelContext = cancelledInternalCtx + internalCancel() + classifications, err = pf.ClassificationsFromCamera(ctx, "configuredCamera", 1, nil) + test.That(t, classifications, test.ShouldBeNil) + test.That(t, err.Error(), test.ShouldEqual, "lost connection with background camera stream loop: context canceled") + + // Test case where trigger flag is not set + pf.cancelContext = context.Background() + classifications, err = pf.ClassificationsFromCamera(ctx, "configuredCamera", 1, nil) + test.That(t, classifications, test.ShouldBeEmpty) + test.That(t, err, test.ShouldBeNil) + + // Test case where trigger flag is set + pf.triggerFlag.Store(true) + classifications, err = pf.ClassificationsFromCamera(ctx, "configuredCamera", 1, nil) + expectedClassifications := classification.Classifications{ + classification.NewClassification(1.0, "TRIGGER"), + } + test.That(t, classifications, test.ShouldResemble, expectedClassifications) + test.That(t, err, test.ShouldBeNil) +} + +func TestClassifications(t *testing.T) { + pf := &prefilter{ + triggerFlag: &atomic.Bool{}, + cancelContext: context.Background(), + } + + ctx := context.Background() + img := image.NewRGBA(image.Rect(0, 0, 100, 100)) + + // Test case where context is canceled + cancelledCtx, cancel := context.WithCancel(ctx) + cancel() + classifications, err := pf.Classifications(cancelledCtx, img, 1, nil) + test.That(t, classifications, test.ShouldBeNil) + test.That(t, err.Error(), test.ShouldEqual, "module might be configuring: context canceled") + + // Test case where internal context is canceled + cancelledInternalCtx, internalCancel := context.WithCancel(ctx) + pf.cancelContext = cancelledInternalCtx + internalCancel() + classifications, err = pf.Classifications(ctx, img, 1, nil) + test.That(t, classifications, test.ShouldBeNil) + test.That(t, err.Error(), test.ShouldEqual, "lost connection with background camera stream loop: context canceled") + + // Test case where trigger flag is not set + pf.cancelContext = context.Background() + classifications, err = pf.Classifications(ctx, img, 1, nil) + test.That(t, classifications, test.ShouldBeEmpty) + test.That(t, err, test.ShouldBeNil) + + // Test case where trigger flag is set + pf.triggerFlag.Store(true) + classifications, err = pf.Classifications(ctx, img, 1, nil) + expectedClassifications := classification.Classifications{ + classification.NewClassification(1.0, "TRIGGER"), + } + test.That(t, classifications, test.ShouldResemble, expectedClassifications) + test.That(t, err, test.ShouldBeNil) +} + +func TestGetProperties(t *testing.T) { + // Create a mock prefilter instance + pf := &prefilter{ + properties: vision.Properties{ + ClassificationSupported: true, + DetectionSupported: false, + ObjectPCDsSupported: false, + }, + } + + // Mock context and extra parameters + ctx := context.Background() + extra := map[string]interface{}{} + + // Call the GetProperties method + properties, err := pf.GetProperties(ctx, extra) + + // Assert the returned properties + test.That(t, properties, test.ShouldNotBeNil) + test.That(t, *properties, test.ShouldResemble, pf.properties) + + // Assert no error returned + test.That(t, err, test.ShouldBeNil) +} + + +func TestCaptureAllFromCamera(t *testing.T) { + stubImage := image.NewRGBA(image.Rect(0, 0, 100, 100)) + imgInterface := image.Image(stubImage) // Convert *image.RGBA to image.Image interface + imgPtr := unsafe.Pointer(&imgInterface) + + // Mock image dimensions + mockWidth := 100 + mockHeight := 100 + + // Create a mock image + mockImg := image.NewRGBA(image.Rect(0, 0, mockWidth, mockHeight)) + + pf := &prefilter{ + camName: "configuredCamera", + triggerFlag: &atomic.Bool{}, + cancelContext: context.Background(), + } + + ctx := context.Background() + + atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&pf.currImg)), unsafe.Pointer(&mockImg)) + + // Test case where context is canceled + cancelledCtx, cancel := context.WithCancel(ctx) + cancel() + capture, err := pf.CaptureAllFromCamera(cancelledCtx, "configuredCamera", viscapture.CaptureOptions{ReturnImage: true, ReturnClassifications: true}, nil) + test.That(t, capture.Image, test.ShouldBeEmpty) + test.That(t, capture.Classifications, test.ShouldBeEmpty) + test.That(t, err.Error(), test.ShouldEqual, "context canceled") + + // Test case where internal context is canceled + cancelledInternalCtx, internalCancel := context.WithCancel(ctx) + pf.cancelContext = cancelledInternalCtx + internalCancel() + capture, err = pf.CaptureAllFromCamera(ctx, "configuredCamera", viscapture.CaptureOptions{ReturnImage: true, ReturnClassifications: true}, nil) + test.That(t, capture.Image, test.ShouldBeEmpty) + test.That(t, capture.Classifications, test.ShouldBeEmpty) + test.That(t, err.Error(), test.ShouldEqual, "context canceled") + + // Test case where camera name does not match + pf.cancelContext = context.Background() + capture, err = pf.CaptureAllFromCamera(ctx, "incorrectCamera", viscapture.CaptureOptions{ReturnImage: true, ReturnClassifications: true}, nil) + test.That(t, capture.Image, test.ShouldBeEmpty) + test.That(t, capture.Classifications, test.ShouldBeEmpty) + test.That(t, err.Error(), test.ShouldEqual, "Camera name \"incorrectCamera\" given to CaptureAllFromCamera is not the same as configured camera \"configuredCamera\"") + + // Test case where no image or classifications are requested + capture, err = pf.CaptureAllFromCamera(ctx, "configuredCamera", viscapture.CaptureOptions{}, nil) + test.That(t, capture.Image, test.ShouldBeEmpty) + test.That(t, capture.Classifications, test.ShouldBeEmpty) + test.That(t, err, test.ShouldBeNil) + + // Test case where only image is requested + pf.triggerFlag.Store(true) + atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&pf.currImg)), imgPtr) + capture, err = pf.CaptureAllFromCamera(ctx, "configuredCamera", viscapture.CaptureOptions{ReturnImage: true}, nil) + test.That(t, capture.Image, test.ShouldResemble, stubImage) + test.That(t, capture.Classifications, test.ShouldBeEmpty) + test.That(t, err, test.ShouldBeNil) + + // Test case where only classifications are requested + capture, err = pf.CaptureAllFromCamera(ctx, "configuredCamera", viscapture.CaptureOptions{ReturnClassifications: true}, nil) + expectedClassifications := viscapture.VisCapture{ + Classifications: classification.Classifications{ + classification.NewClassification(1.0, triggerClassName), + }, + } + test.That(t, capture.Image, test.ShouldBeNil) + test.That(t, capture.Classifications, test.ShouldResemble, expectedClassifications.Classifications) + test.That(t, err, test.ShouldBeNil) + + // Test case where both image and classifications are requested + capture, err = pf.CaptureAllFromCamera(ctx, "configuredCamera", viscapture.CaptureOptions{ReturnImage: true, ReturnClassifications: true}, nil) + test.That(t, capture.Image, test.ShouldResemble, stubImage) + test.That(t, capture.Classifications, test.ShouldResemble, expectedClassifications.Classifications) + test.That(t, err, test.ShouldBeNil) +}