Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support local files #4

Merged
merged 4 commits into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/action/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ var StartOption struct {
Port int
Readonly bool
OnlineSegmentation string
DataDir string
}

var ImportOption struct {
Expand Down
7 changes: 7 additions & 0 deletions app/action/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ func Start(ctx context.Context) error {
}
e.Use(middlewares...)

// local data
if StartOption.DataDir != "" {
zap.L().Info("serving local data", zap.String("dir", StartOption.DataDir))
e.Static("/local", StartOption.DataDir)
}

// backend
s, teardown, err := createServer()
if err != nil {
Expand Down Expand Up @@ -105,6 +111,7 @@ func createServer() (nutshapi.ServerInterface, func(), error) {
backend.WithPublicStorage(localfs.NewPublic(publicDir(), publicUrlPrefix)),
backend.WithSampleStorage(localfs.NewSample(sampleDir())),
backend.WithOnlineSegmentationServerAddr(StartOption.OnlineSegmentation),
backend.WithDataDir(StartOption.DataDir),
backend.WithConfig(&nutshapi.Config{
Readonly: StartOption.Readonly,
OnlineSegmentationEnabled: StartOption.OnlineSegmentation != "",
Expand Down
7 changes: 7 additions & 0 deletions app/backend/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type Options struct {
config *nutshapi.Config

onlineSegmentationServerAddr string
dataDir string
}

func (o *Options) Validate() error {
Expand Down Expand Up @@ -73,3 +74,9 @@ func WithOnlineSegmentationServerAddr(addr string) Option {
o.onlineSegmentationServerAddr = addr
}
}

func WithDataDir(dir string) Option {
return func(o *Options) {
o.dataDir = dir
}
}
24 changes: 23 additions & 1 deletion app/backend/segmentation.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import (
"nutsh/openapi/gen/nutshapi"
schemav1 "nutsh/proto/gen/schema/v1"
servicev1 "nutsh/proto/gen/service/v1"
"os"
"path"
"path/filepath"
"strings"

"github.com/labstack/echo/v4"
"github.com/pkg/errors"
Expand Down Expand Up @@ -136,7 +139,7 @@ func (s *mServer) onlineSegmentationEmbed(ctx context.Context, request nutshapi.
hasCrop := (w > 0 && h > 0)

imUrl := request.Params.ImageUrl
im, err := downloadImage(ctx, imUrl)
im, err := s.loadImage(ctx, imUrl)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -202,6 +205,25 @@ func (s *mServer) onlineSegmentationEmbed(ctx context.Context, request nutshapi.
}, nil
}

func (s *mServer) loadImage(ctx context.Context, url string) ([]byte, error) {
localPrefix := "file://"
if strings.HasPrefix(url, localPrefix) {
// the image should be loaded from data dir
relPath := strings.TrimPrefix(url, localPrefix)
dir := s.options.dataDir
if dir == "" {
return nil, errors.Errorf("missing data dir to load local image [%s]", relPath)
}
fpath := filepath.Join(dir, relPath)
data, err := os.ReadFile(fpath)
if err != nil {
return nil, errors.WithStack(err)
}
return data, nil
}
return downloadImage(ctx, url)
}

func downloadImage(ctx context.Context, url string) ([]byte, error) {
zap.L().Info("downloading image", zap.String("url", url))

Expand Down
9 changes: 8 additions & 1 deletion app/frontend/src/state/image/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ import {useQuery, QueryClient} from '@tanstack/react-query';
const client = new QueryClient();
const context = createContext<QueryClient | undefined>(client);

const query = (url: string) => {
const query = (u: string) => {
let url = u;

const localPrefix = 'file://';
if (u.startsWith(localPrefix)) {
url = `/local/${u.substring(localPrefix.length)}`;
}

return {
queryKey: ['downloadImage', url],
queryFn: async () => await (await fetch(url)).blob(),
Expand Down
44 changes: 44 additions & 0 deletions docs/docs/03-Usage/02-Resource.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,47 @@ Resources refer to objects that are to be annotated, such as images, videos, poi
Through the user interface, you can effortlessly create, search for, update, and delete videos.

<VideoPlayer url="https://nutsh-public.s3.eu-central-1.amazonaws.com/doc/video/manage_videos.mp4" />

## Loading Local Files

Resource assets are specified by their URLs, e.g. a video is represented by a list of urls of its frames.
By default, the URL should use the HTTP protocol, which isn't convenient for directly loading local files.
You can take the following steps to work with local files.

1. Start nutsh with an additional flag `--data-dir [data_dir]` pointing to some directory where your data is located.
2. Use `file://[rel_path]` as the URL for your resources, which will point to the file at `[data_dir]/[rel_path]` on your local machine.

For instance, if you're working with videos stored in

```
/var/data/abc
- video0001
- frame0001.jpg
- frame0002.jpg
- ...
- video0002
- frame0001.jpg
- frame0002.jpg
- ...
- ...
```

Start nutsh with the flag `--data-dir /var/data/abc`, and create your videos with urls like

```
file://video0001/frame0001.jpg
file://video0001/frame0002.jpg
...
```

:::caution

Always use relative paths. Namely, a local url should never start with `file:///` but only two slashes `file://`.

:::

:::danger

You may use `--data-dir /` to serve files with absolute paths, like `file://var/data/abc/video0001/frame0001.jpg`, but do note that it exposes security risks for your host machine.

:::
6 changes: 6 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ func main() {
EnvVars: []string{"NUTSH_ONLINE_SEGMENTATION"},
Destination: &action.StartOption.OnlineSegmentation,
},
&cli.StringFlag{
Name: "data-dir",
Usage: "data directory to serve local files under `file://`",
EnvVars: []string{"NUTSH_DATA_DIR"},
Destination: &action.StartOption.DataDir,
},
},
Commands: []*cli.Command{
{
Expand Down
Loading