Coder Social home page Coder Social logo

biigle / largo Goto Github PK

View Code? Open in Web Editor NEW
0.0 4.0 1.0 3.56 MB

:m: BIIGLE module to review image annotations in a regular grid

License: GNU General Public License v3.0

PHP 77.91% JavaScript 2.23% Vue 10.74% SCSS 0.65% Blade 8.47%
biigle biigle-module

largo's Introduction

BIIGLE

Admin docs

The Bio-Image Indexing and Graphical Labelling Environment (BIIGLE) is a web service for the efficient and rapid annotation of still images and videos. Read the paper or take a look at the manual.

BIIGLE is available at biigle.de.

This is the production setup of a BIIGLE instance. You can fork this repository to customize your own production instance.

Installation

Head over to the admin documentation for installation instructions and more.

Support

If you have a question or request that is not a bug report or feature request, please start a new discussion about it.

Contributions and bug reports

Contributions to BIIGLE are always welcome. Check out biigle/core to get started.

Branches

  • master: The default setup of a BIIGLE instance.

  • gpu: A setup of BIIGLE with GPU computing resources (see the docs).

  • arm32v6: A setup for ARM 32bit platforms (e.g. Raspberry Pi).

  • arm64v8: A setup for ARM 64bit platforms.

References

Reference publications that you should cite if you use BIIGLE for one of your studies.

Security Vulnerabilities

If you discover a security vulnerability within BIIGLE, please send an email to the BIIGLE team via [email protected]. All security vulnerabilities will be promptly addressed.

largo's People

Contributors

dependabot[bot] avatar dlangenk avatar lehecht avatar mzur avatar

Watchers

 avatar  avatar  avatar  avatar

largo's Issues

Duplicate key value

So this error occurred:

[2019-03-25 10:44:28] production.ERROR: SQLSTATE[23505]: Unique violation: 7 ERROR:  duplicate key value violates unique constraint "annotation_labels_annotation_id_label_id_user_id_unique"
DETAIL:  Key (annotation_id, label_id, user_id)=(1086935, 14251, 207) already exists. (SQL: insert into "annotation_labels" ("annotation_id", "confidence", "created_at", "label_id", "updated_at", "user_id") values (1135413, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (2660758, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (1023975, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (2681123, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (1022286, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (1086935, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (1018191, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (1019854, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (1020476, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (1022275, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (1022883, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (1056033, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (1086935, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (2658955, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (2659266, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (1016507, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (4160857, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (4304307, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (4309540, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (1018237, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (1083756, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207)) {"userId":207,"email":"xxx","exception":"[object] (Illuminate\\Database\\QueryException(code: 23505): SQLSTATE[23505]: Unique violation: 7 ERROR:  duplicate key value violates unique constraint \"annotation_labels_annotation_id_label_id_user_id_unique\"
DETAIL:  Key (annotation_id, label_id, user_id)=(1086935, 14251, 207) already exists. (SQL: insert into \"annotation_labels\" (\"annotation_id\", \"confidence\", \"created_at\", \"label_id\", \"updated_at\", \"user_id\") values (1135413, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (2660758, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (1023975, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (2681123, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (1022286, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (1086935, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (1018191, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (1019854, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (1020476, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (1022275, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (1022883, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (1056033, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (1086935, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (2658955, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (2659266, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (1016507, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (4160857, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (4304307, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (4309540, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (1018237, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207), (1083756, 1, 2019-03-25 10:44:28, 14251, 2019-03-25 10:44:28, 207)) at /var/www/vendor/laravel/framework/src/Illuminate/Database/Connection.php:664, Doctrine\\DBAL\\Driver\\PDOException(code: 23505): SQLSTATE[23505]: Unique violation: 7 ERROR:  duplicate key value violates unique constraint \"annotation_labels_annotation_id_label_id_user_id_unique\"
DETAIL:  Key (annotation_id, label_id, user_id)=(1086935, 14251, 207) already exists. at /var/www/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php:107, PDOException(code: 23505): SQLSTATE[23505]: Unique violation: 7 ERROR:  duplicate key value violates unique constraint \"annotation_labels_annotation_id_label_id_user_id_unique\"
DETAIL:  Key (annotation_id, label_id, user_id)=(1086935, 14251, 207) already exists. at /var/www/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php:105)
[stacktrace]
#0 /var/www/vendor/laravel/framework/src/Illuminate/Database/Connection.php(624): Illuminate\\Database\\Connection->runQueryCallback('insert into \"an...', Array, Object(Closure))
#1 /var/www/vendor/laravel/framework/src/Illuminate/Database/Connection.php(459): Illuminate\\Database\\Connection->run('insert into \"an...', Array, Object(Closure))
#2 /var/www/vendor/laravel/framework/src/Illuminate/Database/Connection.php(411): Illuminate\\Database\\Connection->statement('insert into \"an...', Array)
#3 /var/www/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php(2142): Illuminate\\Database\\Connection->insert('insert into \"an...', Array)
#4 /var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php(1283): Illuminate\\Database\\Query\\Builder->insert(Array)
#5 /var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(1477): Illuminate\\Database\\Eloquent\\Builder->__call('insert', Array)
#6 /var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(1489): Illuminate\\Database\\Eloquent\\Model->__call('insert', Array)
#7 /var/www/vendor/biigle/largo/src/Http/Controllers/Api/LargoController.php(217): Illuminate\\Database\\Eloquent\\Model::__callStatic('insert', Array)
#8 /var/www/vendor/biigle/largo/src/Http/Controllers/Api/LargoController.php(135): Biigle\\Modules\\Largo\\Http\\Controllers\\Api\\LargoController->applyChangedLabels(Object(Biigle\\User), Array)
#9 /var/www/vendor/laravel/framework/src/Illuminate/Database/Concerns/ManagesTransactions.php(29): Biigle\\Modules\\Largo\\Http\\Controllers\\Api\\LargoController->Biigle\\Modules\\Largo\\Http\\Controllers\\Api\\{closure}(Object(Illuminate\\Database\\PostgresConnection))
#10 /var/www/vendor/laravel/framework/src/Illuminate/Database/DatabaseManager.php(327): Illuminate\\Database\\Connection->transaction(Object(Closure))
#11 /var/www/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php(221): Illuminate\\Database\\DatabaseManager->__call('transaction', Array)
#12 /var/www/vendor/biigle/largo/src/Http/Controllers/Api/LargoController.php(136): Illuminate\\Support\\Facades\\Facade::__callStatic('transaction', Array)
#13 /var/www/vendor/biigle/largo/src/Http/Controllers/Api/Projects/LargoController.php(67): Biigle\\Modules\\Largo\\Http\\Controllers\\Api\\LargoController->applySave(Object(Biigle\\User), Array, Array, false)
#14 [internal function]: Biigle\\Modules\\Largo\\Http\\Controllers\\Api\\Projects\\LargoController->save(Object(Illuminate\\Http\\Request), '104')
#15 /var/www/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): call_user_func_array(Array, Array)
#16 /var/www/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(45): Illuminate\\Routing\\Controller->callAction('save', Array)
#17 /var/www/vendor/laravel/framework/src/Illuminate/Routing/Route.php(212): Illuminate\\Routing\\ControllerDispatcher->dispatch(Object(Illuminate\\Routing\\Route), Object(Biigle\\Modules\\Largo\\Http\\Controllers\\Api\\Projects\\LargoController), 'save')
#18 /var/www/vendor/laravel/framework/src/Illuminate/Routing/Route.php(169): Illuminate\\Routing\\Route->runController()
#19 /var/www/vendor/laravel/framework/src/Illuminate/Routing/Router.php(658): Illuminate\\Routing\\Route->run()
#20 /var/www/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(30): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#21 /var/www/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(41): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#22 /var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(149): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#23 /var/www/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#24 /var/www/vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php(43): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#25 /var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(149): Illuminate\\Auth\\Middleware\\Authenticate->handle(Object(Illuminate\\Http\\Request), Object(Closure), 'web', 'api')
#26 /var/www/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#27 /var/www/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(68): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#28 /var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(149): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#29 /var/www/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#30 /var/www/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(49): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#31 /var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(149): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#32 /var/www/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#33 /var/www/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(63): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#34 /var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(149): Illuminate\\Session\\Middleware\\StartSession->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#35 /var/www/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#36 /var/www/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#37 /var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(149): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#38 /var/www/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#39 /var/www/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(66): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#40 /var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(149): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#41 /var/www/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#42 /var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(102): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#43 /var/www/vendor/laravel/framework/src/Illuminate/Routing/Router.php(660): Illuminate\\Pipeline\\Pipeline->then(Object(Closure))
#44 /var/www/vendor/laravel/framework/src/Illuminate/Routing/Router.php(635): Illuminate\\Routing\\Router->runRouteWithinStack(Object(Illuminate\\Routing\\Route), Object(Illuminate\\Http\\Request))
#45 /var/www/vendor/laravel/framework/src/Illuminate/Routing/Router.php(601): Illuminate\\Routing\\Router->runRoute(Object(Illuminate\\Http\\Request), Object(Illuminate\\Routing\\Route))
#46 /var/www/vendor/laravel/framework/src/Illuminate/Routing/Router.php(590): Illuminate\\Routing\\Router->dispatchToRoute(Object(Illuminate\\Http\\Request))
#47 /var/www/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(176): Illuminate\\Routing\\Router->dispatch(Object(Illuminate\\Http\\Request))
#48 /var/www/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(30): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}(Object(Illuminate\\Http\\Request))
#49 /var/www/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(30): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#50 /var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(149): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#51 /var/www/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#52 /var/www/app/Http/Middleware/UpdateUserActivity.php(28): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#53 /var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(149): Biigle\\Http\\Middleware\\UpdateUserActivity->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#54 /var/www/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#55 /var/www/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(30): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#56 /var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(149): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#57 /var/www/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#58 /var/www/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#59 /var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(149): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#60 /var/www/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#61 /var/www/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/CheckForMaintenanceMode.php(46): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#62 /var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(149): Illuminate\\Foundation\\Http\\Middleware\\CheckForMaintenanceMode->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#63 /var/www/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#64 /var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(102): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#65 /var/www/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(151): Illuminate\\Pipeline\\Pipeline->then(Object(Closure))
#66 /var/www/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(116): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter(Object(Illuminate\\Http\\Request))
#67 /var/www/public/index.php(55): Illuminate\\Foundation\\Http\\Kernel->handle(Object(Illuminate\\Http\\Request))
#68 {main}
"} 

Investigate why this is possible and fix it.

Clear image cache

Images of remote volumes are cached to generate Largo patches. But the cached images are not automatically removed when the filesystem cache is used (which we do). This package may be a solution. Or we move to another cache driver like Redis (might be useful for other stuff, too).

Speed up rapid querying of annotation patches

When we still performed authorization on the image thumbnails, we put the DB query to permanent (30s) cache to speed things up:

https://github.com/BiodataMiningGroup/biigle-core/blob/189e7daf4e292dfe3fb68fe0c32fa3ddf47f7815/app/Policies/ImagePolicy.php#L41

Now image thumbnails are public and no authorization is performed any more. But in case of annotation patches it is. We can do a similar 30s caching here:

https://github.com/BiodataMiningGroup/biigle-core/blob/189e7daf4e292dfe3fb68fe0c32fa3ddf47f7815/app/Policies/AnnotationPolicy.php#L41

We have to wait for biigle/core#53 to be finished and then use the annotations.project_volume_id as identifier in the cache key.

Make GenerateAnnotationPatch job more robust

The GenerateAnnotationPatch job fails if the volume directory already exists here. But this can happen due to race conditions between workers. Ignore any exceptions there which are thrown because the directory already exists.

Patches are rotated in Firefox and Chrome

Related to biigle/annotations#141

The annotation patches are automatically rotated based on the EXIF information of the original image. This is a new behavior of Firefox and Chrome. To fix this, the EXIF information should be stripped from the patches.

This also affects biigle/maia.

Add documentation

Write a manual page for Largo. There still is the old Youtube video for Ate but it's unlikely that users will find it.

Handle "Impossible to create the root directory"

I don't know how this error can happen. The local filesystem adapter checks if the directory exists. If it does not, it attempts to create it and checks again. If it still does not exist, this error is thrown. Even if the directory is concurrently created by another worker this should not happen. It always occurs only once. Generating the annotation patch on the next try succeeds.

[2019-02-28 00:20:25] production.ERROR: Could not generate annotation patch for annotation 4582823: Impossible to create the root directory "/var/www/storage/app/public/largo-patches/d2/9b/d29bce86-9580-4c53-9a4a-3817e008ce4d".  {"exception":"[object] (Exception(code: 0): Could not generate annotation patch for annotation 4582823: Impossible to create the root directory \"/var/www/storage/app/public/largo-patches/d2/9b/d29bce86-9580-4c53-9a4a-3817e008ce4d\".  at /var/www/vendor/biigle/largo/src/Jobs/GenerateAnnotationPatch.php:88)
[stacktrace]
#0 [internal function]: Biigle\\Modules\\Largo\\Jobs\\GenerateAnnotationPatch->handle()
#1 /var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(29): call_user_func_array(Array, Array)
#2 /var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(87): Illuminate\\Container\\BoundMethod::Illuminate\\Container\\{closure}()
#3 /var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(31): Illuminate\\Container\\BoundMethod::callBoundMethod(Object(Illuminate\\Foundation\\Application), Array, Object(Closure))
#4 /var/www/vendor/laravel/framework/src/Illuminate/Container/Container.php(549): Illuminate\\Container\\BoundMethod::call(Object(Illuminate\\Foundation\\Application), Array, Array, NULL)
#5 /var/www/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php(94): Illuminate\\Container\\Container->call(Array)
#6 /var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(114): Illuminate\\Bus\\Dispatcher->Illuminate\\Bus\\{closure}(Object(Biigle\\Modules\\Largo\\Jobs\\GenerateAnnotationPatch))
#7 /var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(102): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Biigle\\Modules\\Largo\\Jobs\\GenerateAnnotationPatch))
#8 /var/www/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php(98): Illuminate\\Pipeline\\Pipeline->then(Object(Closure))
#9 /var/www/vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php(49): Illuminate\\Bus\\Dispatcher->dispatchNow(Object(Biigle\\Modules\\Largo\\Jobs\\GenerateAnnotationPatch), false)
#10 /var/www/vendor/laravel/framework/src/Illuminate/Queue/Jobs/Job.php(76): Illuminate\\Queue\\CallQueuedHandler->call(Object(Illuminate\\Queue\\Jobs\\RedisJob), Array)
#11 /var/www/vendor/laravel/framework/src/Illuminate/Queue/Worker.php(320): Illuminate\\Queue\\Jobs\\Job->fire()
#12 /var/www/vendor/laravel/framework/src/Illuminate/Queue/Worker.php(270): Illuminate\\Queue\\Worker->process('redis', Object(Illuminate\\Queue\\Jobs\\RedisJob), Object(Illuminate\\Queue\\WorkerOptions))
#13 /var/www/vendor/laravel/framework/src/Illuminate/Queue/Worker.php(114): Illuminate\\Queue\\Worker->runJob(Object(Illuminate\\Queue\\Jobs\\RedisJob), 'redis', Object(Illuminate\\Queue\\WorkerOptions))
#14 /var/www/vendor/laravel/framework/src/Illuminate/Queue/Console/WorkCommand.php(101): Illuminate\\Queue\\Worker->daemon('redis', 'high,default', Object(Illuminate\\Queue\\WorkerOptions))
#15 /var/www/vendor/laravel/framework/src/Illuminate/Queue/Console/WorkCommand.php(85): Illuminate\\Queue\\Console\\WorkCommand->runWorker('redis', 'high,default')
#16 [internal function]: Illuminate\\Queue\\Console\\WorkCommand->handle()
#17 /var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(29): call_user_func_array(Array, Array)
#18 /var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(87): Illuminate\\Container\\BoundMethod::Illuminate\\Container\\{closure}()
#19 /var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(31): Illuminate\\Container\\BoundMethod::callBoundMethod(Object(Illuminate\\Foundation\\Application), Array, Object(Closure))
#20 /var/www/vendor/laravel/framework/src/Illuminate/Container/Container.php(549): Illuminate\\Container\\BoundMethod::call(Object(Illuminate\\Foundation\\Application), Array, Array, NULL)
#21 /var/www/vendor/laravel/framework/src/Illuminate/Console/Command.php(183): Illuminate\\Container\\Container->call(Array)
#22 /var/www/vendor/symfony/console/Command/Command.php(255): Illuminate\\Console\\Command->execute(Object(Symfony\\Component\\Console\\Input\\ArgvInput), Object(Illuminate\\Console\\OutputStyle))
#23 /var/www/vendor/laravel/framework/src/Illuminate/Console/Command.php(170): Symfony\\Component\\Console\\Command\\Command->run(Object(Symfony\\Component\\Console\\Input\\ArgvInput), Object(Illuminate\\Console\\OutputStyle))
#24 /var/www/vendor/symfony/console/Application.php(953): Illuminate\\Console\\Command->run(Object(Symfony\\Component\\Console\\Input\\ArgvInput), Object(Symfony\\Component\\Console\\Output\\ConsoleOutput))
#25 /var/www/vendor/symfony/console/Application.php(248): Symfony\\Component\\Console\\Application->doRunCommand(Object(Illuminate\\Queue\\Console\\WorkCommand), Object(Symfony\\Component\\Console\\Input\\ArgvInput), Object(Symfony\\Component\\Console\\Output\\ConsoleOutput))
#26 /var/www/vendor/symfony/console/Application.php(148): Symfony\\Component\\Console\\Application->doRun(Object(Symfony\\Component\\Console\\Input\\ArgvInput), Object(Symfony\\Component\\Console\\Output\\ConsoleOutput))
#27 /var/www/vendor/laravel/framework/src/Illuminate/Console/Application.php(88): Symfony\\Component\\Console\\Application->run(Object(Symfony\\Component\\Console\\Input\\ArgvInput), Object(Symfony\\Component\\Console\\Output\\ConsoleOutput))
#28 /var/www/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php(121): Illuminate\\Console\\Application->run(Object(Symfony\\Component\\Console\\Input\\ArgvInput), Object(Symfony\\Component\\Console\\Output\\ConsoleOutput))
#29 /var/www/artisan(37): Illuminate\\Foundation\\Console\\Kernel->handle(Object(Symfony\\Component\\Console\\Input\\ArgvInput), Object(Symfony\\Component\\Console\\Output\\ConsoleOutput))
#30 {main}
"} 

Example annotations don't respect annotation sessions

The labels/:id/annotations endpoint returns all matching annotations, ignoring any active annotation session. This means that users might be able to see annotations of other users (or themselves) that should be hidden by an annotation session. Implementing this may be very complicated so I judged it not to be worth the effort. I've created this issue for the record.

Read only version for project guests

Implement a read only version of Largo for project guests. In this version the user can't select annotations for dismissal and can't advance to the second Largo step. They can only select labels and see the annotation patches. Update the documentation and enable the Largo button in the volume overview for guests once this is implemented.

Copria job to generate patches

Write an alternative to the GenerateAnnotationPatch job that submits a new Copria job instead. The Laravel job creates all directories and the Copria job takes care of extracting the patch(es). This must be robust in handling annotation coordinates that overflow the image boundaries.

Either write a new module for this or let the Largo module automatically detect whether Copria is available.

Annotation Catalog

This is an extended version of biigle/label-trees#18 (the "species guide") and will replace it.

Add a button to the label tree overview that redirects to the annotation catalog of the tree. The catalog looks similar to the Largo view: Annotation patches in a grid on the left and the single label tree on the right. When a user selects a label, all patches of annotations with this label are displayed, that the user is allowed to see. As with largo, the user can click a link that redirects to the annotation in the annotation tool.

Aspect ratio of annotation patches

Currently the annotation patches are squeezed into the aspect ration of the thumbnail images like so:

screenshot from 2016-06-22 14-35-01

This distorts the objects in the patches. Should the patches be cropped/expanded to fit into the thumbnails?

Error with unlink in GenerateAnnotationpatch job

Fix this:

[2018-09-19 03:40:05] production.ERROR: Could not generate annotation patch for annotation 3711704: unlink(/var/www/storage/framework/cache/images/222447): No such file or directory {"exception":"[object] (Exception(code: 0): Could not generate annotation patch for annotation 3711704: unlink(/var/www/storage/framework/cache/images/222447): No such file or directory at /var/www/vendor/biigle/largo/src/Jobs/GenerateAnnotationPatch.php:71)
[stacktrace]
#0 [internal function]: Biigle\\Modules\\Largo\\Jobs\\GenerateAnnotationPatch->handle()
#1 /var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(29): call_user_func_array(Array, Array)
#2 /var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(87): Illuminate\\Container\\BoundMethod::Illuminate\\Container\\{closure}()
#3 /var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(31): Illuminate\\Container\\BoundMethod::callBoundMethod(Object(Illuminate\\Foundation\\Application), Array, Object(Closure))
#4 /var/www/vendor/laravel/framework/src/Illuminate/Container/Container.php(549): Illuminate\\Container\\BoundMethod::call(Object(Illuminate\\Foundation\\Application), Array, Array, NULL)
#5 /var/www/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php(94): Illuminate\\Container\\Container->call(Array)
#6 /var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(114): Illuminate\\Bus\\Dispatcher->Illuminate\\Bus\\{closure}(Object(Biigle\\Modules\\Largo\\Jobs\\GenerateAnnotationPatch))
#7 /var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(102): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Biigle\\Modules\\Largo\\Jobs\\GenerateAnnotationPatch))
#8 /var/www/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php(98): Illuminate\\Pipeline\\Pipeline->then(Object(Closure))
#9 /var/www/vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php(49): Illuminate\\Bus\\Dispatcher->dispatchNow(Object(Biigle\\Modules\\Largo\\Jobs\\GenerateAnnotationPatch), false)
#10 /var/www/vendor/laravel/framework/src/Illuminate/Queue/Jobs/Job.php(76): Illuminate\\Queue\\CallQueuedHandler->call(Object(Illuminate\\Queue\\Jobs\\RedisJob), Array)
#11 /var/www/vendor/laravel/framework/src/Illuminate/Queue/Worker.php(320): Illuminate\\Queue\\Jobs\\Job->fire()
#12 /var/www/vendor/laravel/framework/src/Illuminate/Queue/Worker.php(270): Illuminate\\Queue\\Worker->process('redis', Object(Illuminate\\Queue\\Jobs\\RedisJob), Object(Illuminate\\Queue\\WorkerOptions))
#13 /var/www/vendor/laravel/framework/src/Illuminate/Queue/Worker.php(114): Illuminate\\Queue\\Worker->runJob(Object(Illuminate\\Queue\\Jobs\\RedisJob), 'redis', Object(Illuminate\\Queue\\WorkerOptions))
#14 /var/www/vendor/laravel/framework/src/Illuminate/Queue/Console/WorkCommand.php(101): Illuminate\\Queue\\Worker->daemon('redis', 'high,default', Object(Illuminate\\Queue\\WorkerOptions))
#15 /var/www/vendor/laravel/framework/src/Illuminate/Queue/Console/WorkCommand.php(85): Illuminate\\Queue\\Console\\WorkCommand->runWorker('redis', 'high,default')
#16 [internal function]: Illuminate\\Queue\\Console\\WorkCommand->handle()
#17 /var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(29): call_user_func_array(Array, Array)
#18 /var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(87): Illuminate\\Container\\BoundMethod::Illuminate\\Container\\{closure}()
#19 /var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(31): Illuminate\\Container\\BoundMethod::callBoundMethod(Object(Illuminate\\Foundation\\Application), Array, Object(Closure))
#20 /var/www/vendor/laravel/framework/src/Illuminate/Container/Container.php(549): Illuminate\\Container\\BoundMethod::call(Object(Illuminate\\Foundation\\Application), Array, Array, NULL)
#21 /var/www/vendor/laravel/framework/src/Illuminate/Console/Command.php(183): Illuminate\\Container\\Container->call(Array)
#22 /var/www/vendor/symfony/console/Command/Command.php(252): Illuminate\\Console\\Command->execute(Object(Symfony\\Component\\Console\\Input\\ArgvInput), Object(Illuminate\\Console\\OutputStyle))
#23 /var/www/vendor/laravel/framework/src/Illuminate/Console/Command.php(170): Symfony\\Component\\Console\\Command\\Command->run(Object(Symfony\\Component\\Console\\Input\\ArgvInput), Object(Illuminate\\Console\\OutputStyle))
#24 /var/www/vendor/symfony/console/Application.php(946): Illuminate\\Console\\Command->run(Object(Symfony\\Component\\Console\\Input\\ArgvInput), Object(Symfony\\Component\\Console\\Output\\ConsoleOutput))
#25 /var/www/vendor/symfony/console/Application.php(248): Symfony\\Component\\Console\\Application->doRunCommand(Object(Illuminate\\Queue\\Console\\WorkCommand), Object(Symfony\\Component\\Console\\Input\\ArgvInput), Object(Symfony\\Component\\Console\\Output\\ConsoleOutput))
#26 /var/www/vendor/symfony/console/Application.php(148): Symfony\\Component\\Console\\Application->doRun(Object(Symfony\\Component\\Console\\Input\\ArgvInput), Object(Symfony\\Component\\Console\\Output\\ConsoleOutput))
#27 /var/www/vendor/laravel/framework/src/Illuminate/Console/Application.php(88): Symfony\\Component\\Console\\Application->run(Object(Symfony\\Component\\Console\\Input\\ArgvInput), Object(Symfony\\Component\\Console\\Output\\ConsoleOutput))
#28 /var/www/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php(121): Illuminate\\Console\\Application->run(Object(Symfony\\Component\\Console\\Input\\ArgvInput), Object(Symfony\\Component\\Console\\Output\\ConsoleOutput))
#29 /var/www/artisan(37): Illuminate\\Foundation\\Console\\Kernel->handle(Object(Symfony\\Component\\Console\\Input\\ArgvInput), Object(Symfony\\Component\\Console\\Output\\ConsoleOutput))
#30 {main}
"} 

Use database transactions to roll back failed save

The Largo save endpoint does many complicated things. If anything goes wrong it could leave the database in an inconsistent state. With this in mind, I sometimes chose a more inefficient way to implement things to minimize possible corruption. We should use database transactions (see: Manually Using Transactions) instead. With transactions, we don't have to worry about an inconsistent state as all changes are rolled back automatically if an error occurs. This way we can probably implement the save endpoint more efficiently, too.

Limit thumbnail resolution

For all annotation shapes except the point, it is possible that the annotation thumbnails shows an image region that is smaller than the actual thumbnail (i.e. zoomed in). Instead of generating a zoomed in thumbnail, limit the thumbnail resolution to 1 so the annotated object is in the center but at its original size.

Respect annotation sessions in project Largo

The main Largo view does not respect annotation sessions too. Although the annotation patches are not loaded (a 403 is returned), they are shown as empty and can be modified in the Largo session. They should not show up at all as long as the annotation session is active.

Error when attempting to attach existing label

Say we have an annotation with label A and B, both from the same user. When the user attempts to detach label A in step 1 and attach label B in step 2, saving the changes will fail. Catch this case in the controller for the save andpoint.

Improved example annotations

The example annotation plugin should work as follows:

  1. If there are other annotations with the given label, display the 3 most recent ones as examples. Say "Examples with label xy".

  2. New If there are no other annotations of the currently selected label, display 3 example annotations of sibling labels (siblings in the label tree). Say "Similar to label xy".

  3. If there are neither annotations with the same nor with a similar label, say "There are no example annotations for this label".

Cache only remote images

Currently all images are cached in the image cache during generating the annotation patches. As the primary reason for this is to avoid an excessive amout of requests to servers of remote volumes we can restrict the caching to remote images to save storage space. In doing this we can also increase the lifetime of cached images because fewer images will be cached.

The cache volume of our production instance has only 2 GB of space and we repeatedly ran into problems with this.

Show who created an annotation label in dismiss step

Largo can display the same annotation multiple times if it has multiple labels attached. In particular, it can display an annotation multiple times for the same label if it has this label attached by different users. It would be helpful if Largo displayed the name of the user who attached an annoation label (similar to the display of a new label in the re-label step).

Filter annotations

Largo could be used (by an expert) to review the annotations of a single other user. To do this, the annotations need to be filterable by users. The reviewer/expert can select one (or more) user(s) to see only the annotations of the selected user(s) in Largo.

Summary: Filter by

  • user
  • file
  • date of the image
  • date of the annotation
  • date of the annotation label
  • annotation shape

Ideally, the filters could be combined just like in the volume overview and support less than/equals/more than rules (see biigle/core#359).

Scale annotation patches

The annotation patches currently are generated in their original size. They are only displayed in thumbnail size, however, so we can safely scale them to thumbnail size when they are generated to save memory.

Add "all" and "none" buttons

In the dismiss step: "Dismiss all/no annotations"
In the re-label step: "Re-label all annotations with the selected label"/"Re-label no annotations"

This can be useful in a two step annotation process. In the first step users only mark all interesting things with a single label ("Interesting"). In the second step they use Largo to sort the interesting things into their specific label categories. They select e.g. all starfishes in the dismiss step of Largo and then re-label all of them as star fish. With these buttons you don't have to re-label each annotation individually.

Scalebar / original size in grid overview

Bank said that it's sometimes helpful to get an impression on the original size of an object in a Largo patch. He suggested to implement a view of the image in original size on mouseover in Largo (similar to these product image views in Amazon etc.) In the ideal case, this view would have a scale bar as well. Bank acknowledged that this is possible if he opens the annotation in the annotation tool via the link but the integrated view described above would speed things up.

I think this might be quite cumbersome to implement. I'm also not sure if this feature might be of use for all users or if it's just a special thing for Bank's use case. Discuss this with the others.

Replacing multiple labels for same annotation does not work

Say we have an annotation with labels A and B attached to it from the same user. We can dismiss both of these labels in Largo and select a replacement for each of them (C and D). But only one of the replacements is actually applied. This means that the annotation ends up with A and B removed and with C but not D attached.

This is a flaw in the request payload format where only one entry per annotation is allowed.

Memory exhausted

There was an error where the allowed memory size was exhausted during saving of an Ate session:

[Thu Sep 08 15:22:57.133402 2016] [:error] [pid 23541:tid 15] [client xxx.xxx.xxx.xxx:57838] PHP Fatal error:  Allowed memory size of 134217728 bytes exhausted (tried to allocate 32 bytes) in /xxx/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php on line 132, referer: xxx/transects/205/ate

This possibly happened in the save method of the AteController. Find out if this can be reproduced and fixed.

Annotation patches are not generated

A lot of annotation patches are missing on our production system, e.g. here (label Porifera). Investigate what went wrong here. Patches for new annotations seem to be generated fine.

Annotation patches are grey

We've had a few annotation patches of volume 320 (a remote volume) that were completely grey images. All patches belong to annotations that were created on 2017-04-25 between 2 pm and 6 pm. The logfiles show some errors like these on this day:

[2017-04-25 14:33:46] production.ERROR: exception 'Intervention\Image\Exception\NotReadableException' with message 'Unable to init from given binary data.' in /vol/uwi/www/bias/vendor/intervention/image/src/Intervention/Image/Gd/Decoder.php:96
Stack trace:
#0 /vol/uwi/www/bias/vendor/intervention/image/src/Intervention/Image/AbstractDecoder.php(65): Intervention\Image\Gd\Decoder->initFromBinary('\x89PNG\r\n\x1A\n\x00\x00\x00\rIHD...')
#1 /vol/uwi/www/bias/vendor/intervention/image/src/Intervention/Image/AbstractDecoder.php(287): Intervention\Image\AbstractDecoder->initFromUrl('http://plym-mar...')
#2 /vol/uwi/www/bias/vendor/intervention/image/src/Intervention/Image/AbstractDriver.php(64): Intervention\Image\AbstractDecoder->init('http://plym-mar...')
#3 /vol/uwi/www/bias/vendor/intervention/image/src/Intervention/Image/ImageManager.php(50): Intervention\Image\AbstractDriver->init('http://plym-mar...')
#4 [internal function]: Intervention\Image\ImageManager->make('http://plym-mar...')
#5 /vol/uwi/www/bias/vendor/intervention/imagecache/src/Intervention/Image/ImageCache.php(255): call_user_func_array(Array, Array)
#6 /vol/uwi/www/bias/vendor/intervention/imagecache/src/Intervention/Image/ImageCache.php(270): Intervention\Image\ImageCache->processCall(Array)
#7 /vol/uwi/www/bias/vendor/intervention/imagecache/src/Intervention/Image/ImageCache.php(316): Intervention\Image\ImageCache->process()
#8 /vol/uwi/www/bias/vendor/biigle/largo/src/Jobs/GenerateAnnotationPatch.php(138): Intervention\Image\ImageCache->get(5, true)
#9 [internal function]: Biigle\Modules\Largo\Jobs\GenerateAnnotationPatch->handle()
#10 /vol/uwi/www/bias/bootstrap/cache/compiled.php(1375): call_user_func_array(Array, Array)
#11 /vol/uwi/www/bias/bootstrap/cache/compiled.php(9947): Illuminate\Container\Container->call(Array)
#12 /vol/uwi/www/bias/bootstrap/cache/compiled.php(10062): Illuminate\Bus\Dispatcher->Illuminate\Bus\{closure}(Object(Biigle\Modules\Largo\Jobs\GenerateAnnotationPatch))
#13 /vol/uwi/www/bias/bootstrap/cache/compiled.php(10040): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Biigle\Modules\Largo\Jobs\GenerateAnnotationPatch))
#14 /vol/uwi/www/bias/bootstrap/cache/compiled.php(9950): Illuminate\Pipeline\Pipeline->then(Object(Closure))
#15 /vol/uwi/www/bias/vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php(47): Illuminate\Bus\Dispatcher->dispatchNow(Object(Biigle\Modules\Largo\Jobs\GenerateAnnotationPatch), NULL)
#16 /vol/uwi/www/bias/vendor/laravel/framework/src/Illuminate/Queue/Jobs/Job.php(73): Illuminate\Queue\CallQueuedHandler->call(Object(Illuminate\Queue\Jobs\DatabaseJob), Array)
#17 /vol/uwi/www/bias/vendor/laravel/framework/src/Illuminate/Queue/Worker.php(203): Illuminate\Queue\Jobs\Job->fire()
#18 /vol/uwi/www/bias/vendor/laravel/framework/src/Illuminate/Queue/Worker.php(154): Illuminate\Queue\Worker->process('database', Object(Illuminate\Queue\Jobs\DatabaseJob), Object(Illuminate\Queue\WorkerOptions))
#19 /vol/uwi/www/bias/vendor/laravel/framework/src/Illuminate/Queue/Worker.php(75): Illuminate\Queue\Worker->runNextJob('database', 'default', Object(Illuminate\Queue\WorkerOptions))
#20 /vol/uwi/www/bias/vendor/laravel/framework/src/Illuminate/Queue/Console/WorkCommand.php(101): Illuminate\Queue\Worker->daemon('database', 'default', Object(Illuminate\Queue\WorkerOptions))
#21 /vol/uwi/www/bias/vendor/laravel/framework/src/Illuminate/Queue/Console/WorkCommand.php(85): Illuminate\Queue\Console\WorkCommand->runWorker('database', 'default')
#22 [internal function]: Illuminate\Queue\Console\WorkCommand->fire()
#23 /vol/uwi/www/bias/bootstrap/cache/compiled.php(1375): call_user_func_array(Array, Array)
#24 /vol/uwi/www/bias/vendor/laravel/framework/src/Illuminate/Console/Command.php(169): Illuminate\Container\Container->call(Array)
#25 /vol/uwi/www/bias/vendor/symfony/console/Command/Command.php(254): Illuminate\Console\Command->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#26 /vol/uwi/www/bias/vendor/laravel/framework/src/Illuminate/Console/Command.php(155): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#27 /vol/uwi/www/bias/vendor/symfony/console/Application.php(821): Illuminate\Console\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#28 /vol/uwi/www/bias/vendor/symfony/console/Application.php(187): Symfony\Component\Console\Application->doRunCommand(Object(Illuminate\Queue\Console\WorkCommand), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#29 /vol/uwi/www/bias/vendor/symfony/console/Application.php(118): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#30 /vol/uwi/www/bias/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php(121): Symfony\Component\Console\Application->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#31 /vol/uwi/www/bias/artisan(36): Illuminate\Foundation\Console\Kernel->handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#32 {main}

Our best guess is that the remote location of the volume (AWS) temporarily served malformed PNGs. Sometimes the GD decoder detected the image as malformed and threw the error, which should result in a new attempt to generate the annotation patch. But sometimes GD may not have detected the image as malformed, put it to the image cache and then generate only grey annotation patches because the image could not be read properly. All attempts to re-generate the grey annotation patches were successful so this was definitely a temporary issue.

If this problem may ever occur again, it can most probably be identified by similar error messages in the log file. Grey annotation patches can be identified by this command (executed in the largo_patches directory):

find . -type f -exec bash -c 'identify -verbose {} | grep -q "#808080 gray(128)" && echo {}' \;

(note that this command also identifies patches that rightfully contain some pixels with this color)

Offer multiple zoom levels for Largo patches

Just a quick idea I had since Erik asked for a different "padding" of Largo patches for point annotations. We could compute Largo patches at 3 different zoom levels (one zoomed out, one normal, one zoomed in). In Largo, users could cycle through the zoom levels by moving the mouse up and down in the thumbnail (similar to the left/right preview of video and volume thumbnails). This would greatly increase the required storage for Largo patches, though. Or maybe this could be made optional and additional zoom levels are only computed on demand.

Handle missing label on save

Recently we had a case where a label seems to have been deleted while a user did a Largo session. The user chose the deleted label in Largo and it crashed here while saving the changes. This is done after deleting the dismissed annotation labels, leaving the annotations in an incorrect state.

Improve this behavior by:

  • Checking if the changed labels still exist. Ignore all changes (detached labels, new labels) for annotations that should be changed to a nonexistant label.
  • Implement a rollback mechanism. If there is any error during deleting annotation labels or inserting new ones, restore all old annotation labels that have already been deleted (using a new array that is filled with the properties of the deleted annotation labels).

Show "cross-hair" in annotation patch

Sometimes it is hard to see what object an annotation patch should represent (e.g. if multiple objects are close). Show a cross-hair at the center of the annotation patch to highlight the correct object.

Add example annotations in Largo re-label step

The example annotations ( #20 ) for the annotation tool would be helpful in the Largo re-label step, too. Add them to the sidebar. This would need a new API endpoint for project level example annotations.

Caveat: The example annotations may show exactly the annotations that the user decided to dismiss/re-label. But there is nothing we can do about it.

Object detection workflow

We want to support what I'll call the "object detection workflow" with Largo. It goes like this: Some algorithm or people detect interesting objects in the images. They don't know the correct label for these objects. After that, an expert reviews these annotations in Largo and attaches the correct labels to them.

What users can do right now is to create an "interesting object" label and annotate the objects with it. Then the expert reviews all these annotations and attaches the additional correct label with Largo. There are the following issues with this approach:

  1. The "interesting object" label is not detached by the expert.
  2. Since the label is not detached, the expert is not able to track which annotations they still have to review.
  3. The "interesting object" labels clutter the reports (which is why we implemented this).

This all boils down to the fact that labels of other users cannot be detached with Largo. So how could we implement this? Daniel suggested a global label with special meaning. Any user should be allowed to detach this label in Largo.

In any case we would have the problem that Largo requests are not unambiguous any more. If a user is allowed to detach a label of another user, which one (as there may be multiple same labels of different users)?

New requirements

Apparently Largo does not statisfy the original requirements for a re-annotation task. @dlangenk please formulare a detailed description of what users would want to do with Largo. We can then discuss if and how Largo has to be changed.

Prevent window close

If there are dismissed annotations, ask if the window should be closed to prevent loss of work.

Lables in Re-label phase

It is not possible to see which label is attached to which patch. Resolve by adding a text label + color dot to the patches.

duplicate key value violates unique constraint "annotation_labels_annotation_id_label_id_user_id_unique"

Recently I observed the following error:

[2018-03-23 14:14:19] production.ERROR: PDOException: SQLSTATE[23505]: Unique violation: 7 ERROR:  duplicate key value violates unique constraint "annotation_labels_annotation_id_label_id_user_id_unique"
DETAIL:  Key (annotation_id, label_id, user_id)=(2122477, 6513, 133) already exists. in /var/www/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php:105
Stack trace:
#0 /var/www/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php(105): PDOStatement->execute(NULL)
#1 /var/www/vendor/laravel/framework/src/Illuminate/Database/Connection.php(449): Doctrine\DBAL\Driver\PDOStatement->execute()
#2 /var/www/vendor/laravel/framework/src/Illuminate/Database/Connection.php(640): Illuminate\Database\Connection->Illuminate\Database\{closure}('insert into "an...', Array)
#3 /var/www/vendor/laravel/framework/src/Illuminate/Database/Connection.php(607): Illuminate\Database\Connection->runQueryCallback('insert into "an...', Array, Object(Closure))
#4 /var/www/vendor/laravel/framework/src/Illuminate/Database/Connection.php(450): Illuminate\Database\Connection->run('insert into "an...', Array, Object(Closure))
#5 /var/www/vendor/laravel/framework/src/Illuminate/Database/Connection.php(404): Illuminate\Database\Connection->statement('insert into "an...', Array)
#6 /var/www/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php(2122): Illuminate\Database\Connection->insert('insert into "an...', Array)
#7 /var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php(1251): Illuminate\Database\Query\Builder->insert(Array)
#8 /var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(1372): Illuminate\Database\Eloquent\Builder->__call('insert', Array)
#9 /var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(1384): Illuminate\Database\Eloquent\Model->__call('insert', Array)
#10 /var/www/vendor/biigle/largo/src/Http/Controllers/Api/LargoController.php(152): Illuminate\Database\Eloquent\Model::__callStatic('insert', Array)
#11 /var/www/vendor/biigle/largo/src/Http/Controllers/Api/Volumes/LargoController.php(98): Biigle\Modules\Largo\Http\Controllers\Api\LargoController->applySave(Object(Biigle\User), Array, Array)
#12 [internal function]: Biigle\Modules\Largo\Http\Controllers\Api\Volumes\LargoController->save(Object(Illuminate\Http\Request), Object(Illuminate\Auth\SessionGuard), '450')
[...]

This might be an error in the implementation of Largo. Maybe it doesn't properly check for already existing annotation labels (although I think it does).

The same error happened twice yesterday, but for the regular "attach label to annotation" endpoint:

[2018-03-25 18:21:26] production.ERROR: PDOException: SQLSTATE[23505]: Unique violation: 7 ERROR:  duplicate key value violates unique constraint "annotation_labels_annotation_id_label_id_user_id_unique"
DETAIL:  Key (annotation_id, label_id, user_id)=(2216721, 8342, 59) already exists. in /var/www/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php:105
Stack trace:
#0 /var/www/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php(105): PDOStatement->execute(NULL)
#1 /var/www/vendor/laravel/framework/src/Illuminate/Database/Connection.php(323): Doctrine\DBAL\Driver\PDOStatement->execute()
#2 /var/www/vendor/laravel/framework/src/Illuminate/Database/Connection.php(640): Illuminate\Database\Connection->Illuminate\Database\{closure}('insert into "an...', Array)
#3 /var/www/vendor/laravel/framework/src/Illuminate/Database/Connection.php(607): Illuminate\Database\Connection->runQueryCallback('insert into "an...', Array, Object(Closure))
#4 /var/www/vendor/laravel/framework/src/Illuminate/Database/Connection.php(326): Illuminate\Database\Connection->run('insert into "an...', Array, Object(Closure))
#5 /var/www/vendor/laravel/framework/src/Illuminate/Database/Connection.php(297): Illuminate\Database\Connection->select('insert into "an...', Array, false)
#6 /var/www/vendor/laravel/framework/src/Illuminate/Database/Query/Processors/PostgresProcessor.php(20): Illuminate\Database\Connection->selectFromWriteConnection('insert into "an...', Array)
#7 /var/www/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php(2139): Illuminate\Database\Query\Processors\PostgresProcessor->processInsertGetId(Object(Illuminate\Database\Query\Builder), 'insert into "an...', Array, 'id')
#8 /var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php(1251): Illuminate\Database\Query\Builder->insertGetId(Array, 'id')
#9 /var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(688): Illuminate\Database\Eloquent\Builder->__call('insertGetId', Array)
#10 /var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(653): Illuminate\Database\Eloquent\Model->insertAndSetId(Object(Illuminate\Database\Eloquent\Builder), Array)
#11 /var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(522): Illuminate\Database\Eloquent\Model->performInsert(Object(Illuminate\Database\Eloquent\Builder))
#12 /var/www/app/Http/Controllers/Api/AnnotationLabelController.php(115): Illuminate\Database\Eloquent\Model->save()
#13 [internal function]: Biigle\Http\Controllers\Api\AnnotationLabelController->store(Object(Illuminate\Http\Request), Object(Illuminate\Auth\SessionGuard), '2216721')
[...]
[2018-03-25 23:09:29] production.ERROR: PDOException: SQLSTATE[23505]: Unique violation: 7 ERROR:  duplicate key value violates unique constraint "annotation_labels_annotation_id_label_id_user_id_unique"
DETAIL:  Key (annotation_id, label_id, user_id)=(2209345, 6530, 59) already exists. in /var/www/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php:105
Stack trace:
#0 /var/www/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php(105): PDOStatement->execute(NULL)
#1 /var/www/vendor/laravel/framework/src/Illuminate/Database/Connection.php(323): Doctrine\DBAL\Driver\PDOStatement->execute()
#2 /var/www/vendor/laravel/framework/src/Illuminate/Database/Connection.php(640): Illuminate\Database\Connection->Illuminate\Database\{closure}('insert into "an...', Array)
#3 /var/www/vendor/laravel/framework/src/Illuminate/Database/Connection.php(607): Illuminate\Database\Connection->runQueryCallback('insert into "an...', Array, Object(Closure))
#4 /var/www/vendor/laravel/framework/src/Illuminate/Database/Connection.php(326): Illuminate\Database\Connection->run('insert into "an...', Array, Object(Closure))
#5 /var/www/vendor/laravel/framework/src/Illuminate/Database/Connection.php(297): Illuminate\Database\Connection->select('insert into "an...', Array, false)
#6 /var/www/vendor/laravel/framework/src/Illuminate/Database/Query/Processors/PostgresProcessor.php(20): Illuminate\Database\Connection->selectFromWriteConnection('insert into "an...', Array)
#7 /var/www/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php(2139): Illuminate\Database\Query\Processors\PostgresProcessor->processInsertGetId(Object(Illuminate\Database\Query\Builder), 'insert into "an...', Array, 'id')
#8 /var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php(1251): Illuminate\Database\Query\Builder->insertGetId(Array, 'id')
#9 /var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(688): Illuminate\Database\Eloquent\Builder->__call('insertGetId', Array)
#10 /var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(653): Illuminate\Database\Eloquent\Model->insertAndSetId(Object(Illuminate\Database\Eloquent\Builder), Array)
#11 /var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(522): Illuminate\Database\Eloquent\Model->performInsert(Object(Illuminate\Database\Eloquent\Builder))
#12 /var/www/app/Http/Controllers/Api/AnnotationLabelController.php(115): Illuminate\Database\Eloquent\Model->save()
#13 [internal function]: Biigle\Http\Controllers\Api\AnnotationLabelController->store(Object(Illuminate\Http\Request), Object(Illuminate\Auth\SessionGuard), '2209345')
[...]

This might or might not be related.

No BMP support

We use Intervention image with the GD driver to create the ATE patches for each annotation. GD lacks support for BMP images.

  1. We should implement a check in dias/core if the format of images of a new transect is supported (jpg, png, ?). This can check if the images are readable, too.
  2. If BMP should supported, we have to switch to the Imagick driver (although it recently had some security vulnerabilities?) and make ImageMagick a requirement of dias/core.
  3. Finally we need to clean up the failed jobs in the worker queue of the dias.cebitec instance.

@dlangenk What image formats do we want to support?

@tmoeller4 The ATE feature is broken for all of your transects with BMP images.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.