Skip to content

Commit

Permalink
Update code to use the new ProcessAnnotatedImage job of biigle/largo
Browse files Browse the repository at this point in the history
This also fixes #148 because the new job generates feature vectors, too.
  • Loading branch information
mzur committed Jan 19, 2024
1 parent 0c9aa78 commit 82e375f
Show file tree
Hide file tree
Showing 15 changed files with 456 additions and 126 deletions.
16 changes: 10 additions & 6 deletions src/Http/Controllers/Api/AnnotationCandidateController.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
namespace Biigle\Modules\Maia\Http\Controllers\Api;

use Biigle\Http\Controllers\Api\Controller;
use Biigle\Modules\Largo\Jobs\GenerateImageAnnotationPatch;
use Biigle\Modules\Maia\AnnotationCandidateFeatureVector;
use Biigle\Modules\Maia\Http\Requests\SubmitAnnotationCandidates;
use Biigle\Modules\Maia\Http\Requests\UpdateAnnotationCandidate;
use Biigle\Modules\Maia\Jobs\ConvertAnnotationCandidates;
use Biigle\Modules\Maia\Jobs\ProcessObjectDetectedImage;
use Biigle\Modules\Maia\MaiaJob;
use Illuminate\Http\Response;
use Pgvector\Laravel\Distance;
Expand Down Expand Up @@ -150,17 +150,21 @@ public function submit(SubmitAnnotationCandidates $request)
*/
public function update(UpdateAnnotationCandidate $request)
{
$candidate = $request->candidate;
if ($request->filled('points')) {
$request->candidate->points = $request->input('points');
$disk = config('maia.annotation_candidate_storage_disk');
GenerateImageAnnotationPatch::dispatch($request->candidate, $disk)
$candidate->points = $request->input('points');
ProcessObjectDetectedImage::dispatch($candidate->image,
only: [$candidate->id],
maiaJob: $candidate->job,
targetDisk: config('maia.annotation_candidate_storage_disk')
)
->onQueue(config('largo.generate_annotation_patch_queue'));
}

if ($request->has('label_id')) {
$request->candidate->label_id = $request->input('label_id');
$candidate->label_id = $request->input('label_id');
}

$request->candidate->save();
$candidate->save();
}
}
17 changes: 11 additions & 6 deletions src/Http/Controllers/Api/TrainingProposalController.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
namespace Biigle\Modules\Maia\Http\Controllers\Api;

use Biigle\Http\Controllers\Api\Controller;
use Biigle\Modules\Largo\Jobs\GenerateImageAnnotationPatch;
use Biigle\Modules\Maia\Events\MaiaJobContinued;
use Biigle\Modules\Maia\Http\Requests\ContinueMaiaJob;
use Biigle\Modules\Maia\Http\Requests\UpdateTrainingProposal;
use Biigle\Modules\Maia\Jobs\ProcessNoveltyDetectedImage;
use Biigle\Modules\Maia\MaiaJob;
use Biigle\Modules\Maia\MaiaJobState as State;
use Biigle\Modules\Maia\TrainingProposalFeatureVector;
Expand Down Expand Up @@ -149,14 +149,19 @@ public function submit(ContinueMaiaJob $request)
*/
public function update(UpdateTrainingProposal $request)
{
$proposal = $request->proposal;
if ($request->filled('points')) {
$request->proposal->points = $request->input('points');
$disk = config('maia.training_proposal_storage_disk');
GenerateImageAnnotationPatch::dispatch($request->proposal, $disk)
$proposal->points = $request->input('points');
ProcessNoveltyDetectedImage::dispatch($proposal->image,
only: [$proposal->id],
maiaJob: $proposal->job,
targetDisk: config('maia.training_proposal_storage_disk')
)
->onQueue(config('largo.generate_annotation_patch_queue'));
}

$request->proposal->selected = $request->input('selected', $request->proposal->selected);
$request->proposal->save();
$proposal->selected = $request->input('selected', $proposal->selected);

$proposal->save();
}
}
21 changes: 14 additions & 7 deletions src/Jobs/ConvertAnnotationCandidates.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
use Biigle\ImageAnnotation;
use Biigle\ImageAnnotationLabel;
use Biigle\Jobs\Job;
use Biigle\Modules\Largo\Jobs\GenerateImageAnnotationPatch;
use Biigle\Modules\Largo\Jobs\ProcessAnnotatedImage;
use Biigle\Modules\Maia\MaiaJob;
use Biigle\User;
use Carbon\Carbon;
use DB;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Collection;

class ConvertAnnotationCandidates extends Job
{
Expand Down Expand Up @@ -47,9 +48,9 @@ class ConvertAnnotationCandidates extends Job
/**
* Newly created annotations.
*
* @var array
* @var Collection
*/
protected $newAnnotations = [];
protected $newAnnotations;

/**
* Create a new isntance.
Expand All @@ -62,6 +63,7 @@ public function __construct(MaiaJob $job, User $user)
$this->queue = config('maia.convert_annotations_queue');
$this->job = $job;
$this->user = $user;
$this->newAnnotations = collect([]);
}

/**
Expand All @@ -79,10 +81,15 @@ public function handle()
->chunkById(10000, [$this, 'processChunk']);
});

foreach ($this->newAnnotations as $annotation) {
GenerateImageAnnotationPatch::dispatch($annotation)
->onQueue(config('largo.generate_annotation_patch_queue'));
}
$this->newAnnotations
->groupBy('image_id')
->each(function ($group) {
$image = $group[0]->image;
$ids = $group->pluck('id')->all();
ProcessAnnotatedImage::dispatch($image, only: $ids)
->onQueue(config('largo.generate_annotation_patch_queue'));
});

} finally {
$this->job->convertingCandidates = false;
$this->job->save();
Expand Down
43 changes: 33 additions & 10 deletions src/Jobs/GenerateAnnotationCandidatePatches.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,46 @@

namespace Biigle\Modules\Maia\Jobs;

class GenerateAnnotationCandidatePatches extends GenerateAnnotationPatches
use Biigle\Jobs\Job;
use Biigle\Modules\Maia\MaiaJob;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\SerializesModels;

class GenerateAnnotationCandidatePatches extends Job implements ShouldQueue
{
use SerializesModels;

/**
* Get a query for the annotations that have been created by this job.
* Ignore this job if the MAIA job does not exist any more.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
* @var bool
*/
protected function getCreatedAnnotations()
{
return $this->job->annotationCandidates();
}
protected $deleteWhenMissingModels = true;

/**
* Get the storage disk to store the annotation patches to.
* Create a new isntance.
*/
protected function getPatchStorageDisk()
public function __construct(public MaiaJob $maiaJob)
{
//
}

public function handle(): void
{
return config('maia.annotation_candidate_storage_disk');
$this->maiaJob->volume->images()
->whereExists(fn ($q) =>
$q->select(\DB::raw(1))
->from('maia_annotation_candidates')
->where('maia_annotation_candidates.job_id', $this->maiaJob->id)
->whereColumn('maia_annotation_candidates.image_id', 'images.id')
)
->eachById(fn ($image) =>
ProcessObjectDetectedImage::dispatch($image, $this->maiaJob,
// Feature vectors are generated in a separate job on the GPU.
skipFeatureVectors: true,
targetDisk: config('maia.annotation_candidate_storage_disk')
)
->onQueue(config('largo.generate_annotation_patch_queue'))
);
}
}
65 changes: 0 additions & 65 deletions src/Jobs/GenerateAnnotationPatches.php

This file was deleted.

43 changes: 33 additions & 10 deletions src/Jobs/GenerateTrainingProposalPatches.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,46 @@

namespace Biigle\Modules\Maia\Jobs;

class GenerateTrainingProposalPatches extends GenerateAnnotationPatches
use Biigle\Jobs\Job;
use Biigle\Modules\Maia\MaiaJob;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\SerializesModels;

class GenerateTrainingProposalPatches extends Job implements ShouldQueue
{
use SerializesModels;

/**
* Get a query for the annotations that have been created by this job.
* Ignore this job if the MAIA job does not exist any more.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
* @var bool
*/
protected function getCreatedAnnotations()
{
return $this->job->trainingProposals();
}
protected $deleteWhenMissingModels = true;

/**
* Get the storage disk to store the annotation patches to.
* Create a new isntance.
*/
protected function getPatchStorageDisk()
public function __construct(public MaiaJob $maiaJob)
{
//
}

public function handle(): void
{
return config('maia.training_proposal_storage_disk');
$this->maiaJob->volume->images()
->whereExists(fn ($q) =>
$q->select(\DB::raw(1))
->from('maia_training_proposals')
->where('maia_training_proposals.job_id', $this->maiaJob->id)
->whereColumn('maia_training_proposals.image_id', 'images.id')
)
->eachById(fn ($image) =>
ProcessNoveltyDetectedImage::dispatch($image, $this->maiaJob,
// Feature vectors are generated in a separate job on the GPU.
skipFeatureVectors: true,
targetDisk: config('maia.training_proposal_storage_disk')
)
->onQueue(config('largo.generate_annotation_patch_queue'))
);
}
}
55 changes: 55 additions & 0 deletions src/Jobs/ProcessNoveltyDetectedImage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

namespace Biigle\Modules\Maia\Jobs;

use Biigle\Modules\Largo\Jobs\ProcessAnnotatedImage;
use Biigle\Modules\Maia\MaiaJob;
use Biigle\Modules\Maia\TrainingProposal;
use Biigle\Modules\Maia\TrainingProposalFeatureVector;
use Biigle\VolumeFile;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;

class ProcessNoveltyDetectedImage extends ProcessAnnotatedImage
{
public function __construct(
public VolumeFile $file,
public MaiaJob $maiaJob,
public array $only = [],
public bool $skipFeatureVectors = false,
public ?string $targetDisk = null
)
{
parent::__construct($file, $only,
skipFeatureVectors: $skipFeatureVectors,
targetDisk: $targetDisk
);
}

/**
* {@inheritdoc}
*/
protected function getAnnotationQuery(VolumeFile $file): Builder
{
return TrainingProposal::where('image_id', $file->id)
->where('job_id', $this->maiaJob->id);
}

/**
* Create the feature vectors based on the Python script output.
*/
protected function updateOrCreateFeatureVectors(Collection $annotations, \Generator $output): void
{
$annotations = $annotations->keyBy('id');
foreach ($output as $row) {
$annotation = $annotations->get($row[0]);
TrainingProposalFeatureVector::updateOrCreate(
['id' => $annotation->id],
[
'job_id' => $annotation->job_id,
'vector' => $row[1],
]
);
}
}
}
Loading

0 comments on commit 82e375f

Please sign in to comment.