PHP code example of zielu92 / filament-image-labeler
1. Go to this page and download the library: Download zielu92/filament-image-labeler library . Choose the download type require .
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
zielu92 / filament-image-labeler example snippets
use Zielu92\FilamentImageLabeler\Concerns\HasAnnotations;
class Photo extends Model
{
use HasAnnotations;
}
use Zielu92\FilamentImageLabeler\Forms\Components\ImageLabel;
ImageLabel::make('annotations')
->image(fn ($record) => $record?->getFirstMediaUrl())
->enableSquare() // Enable rectangle drawing
->enablePolygon() // Enable polygon drawing
->enableClear() // Show "Clear All" button
->multiple() // Allow multiple annotations (default: true)
->live()
->columnSpanFull()
// In your CreateRecord page:
class CreatePhoto extends CreateRecord
{
private array $annotationData = [];
protected function mutateFormDataBeforeCreate(array $data): array
{
// Extract annotation data before Eloquent save
$this->annotationData = collect($data['annotation_repeater'] ?? [])
->map(fn ($item) => [
'annotation_id' => $item['annotation_id'],
'geometry' => $item['geometry'],
'metadata' => [
'title' => $item['title'] ?? null,
'category' => $item['category'] ?? null,
],
])->toArray();
unset($data['annotations'], $data['annotation_repeater']);
return $data;
}
protected function afterCreate(): void
{
$this->record->syncAnnotations($this->annotationData);
}
}
// In your EditRecord page:
class EditPhoto extends EditRecord
{
private array $annotationData = [];
protected function mutateFormDataBeforeFill(array $data): array
{
$annotations = $this->record->annotations()->orderBy('id')->get();
if ($annotations->isNotEmpty()) {
// Populate the repeater with your app's metadata fields
$data['annotation_repeater'] = $annotations->map(fn ($ann) => [
'annotation_id' => $ann->annotation_id,
'title' => $ann->metadata['title'] ?? '',
'category' => $ann->metadata['category'] ?? null,
'geometry' => json_encode($ann->geometry),
])->toArray();
// Populate the canvas with geometry
$data['annotations'] = $annotations->map(fn ($ann) => [
'id' => $ann->annotation_id,
'target' => $ann->geometry,
])->toArray();
}
return $data;
}
protected function mutateFormDataBeforeSave(array $data): array
{
$this->annotationData = collect($data['annotation_repeater'] ?? [])
->map(fn ($item) => [
'annotation_id' => $item['annotation_id'],
'geometry' => $item['geometry'],
'metadata' => [
'title' => $item['title'] ?? null,
'category' => $item['category'] ?? null,
],
])->toArray();
unset($data['annotations'], $data['annotation_repeater']);
return $data;
}
protected function afterSave(): void
{
$this->record->syncAnnotations($this->annotationData);
}
}
use Filament\Forms\Components\Hidden;
use Filament\Forms\Components\Placeholder;
use Filament\Forms\Components\Repeater;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Components\Utilities\Get;
use Filament\Schemas\Components\Utilities\Set;
use Illuminate\Support\HtmlString;
use Zielu92\FilamentImageLabeler\Forms\Components\ImageLabel;
use Zielu92\FilamentImageLabeler\Support\AnnotationColor;
// Define your palette once — pass the same array to both the component and AnnotationColor
$palette = [
'#ef4444', '#3b82f6', '#10b981', '#f59e0b',
'#8b5cf6', '#ec4899', '#06b6d4', '#84cc16',
'#f97316', '#6366f1', '#14b8a6', '#e11d48',
];
// The annotation canvas with colored annotations
ImageLabel::make('annotations')
->image(fn ($record) => $record?->getFirstMediaUrl())
->coloredAnnotations($palette)
->enableSquare()
->enablePolygon()
->enableClear()
->multiple()
->live()
->columnSpanFull()
->afterStateUpdated(function (?array $state, Set $set, Get $get) {
$currentRepeater = $get('annotation_repeater') ?? [];
$existingById = collect($currentRepeater)->keyBy('annotation_id');
$newRepeater = collect($state ?? [])->map(function ($annotation) use ($existingById) {
$id = $annotation['id'];
$existing = $existingById->get($id);
return [
'annotation_id' => $id,
'title' => $existing['title'] ?? '',
'category' => $existing['category'] ?? null,
'geometry' => json_encode($annotation['target'] ?? []),
];
})->toArray();
$set('annotation_repeater', $newRepeater);
}),
// The metadata repeater with color swatch
Repeater::make('annotation_repeater')
->schema([
Placeholder::make('color_swatch')
->hiddenLabel()
->content(function (Get $get) use ($palette): HtmlString {
$id = $get('annotation_id') ?? '';
$color = AnnotationColor::forId($id, $palette);
return new HtmlString(
'<div style="width: 24px; height: 24px; border-radius: 4px; '
. 'background-color: ' . $color . '; '
. 'border: 1px solid rgba(0,0,0,0.2);"></div>'
);
})
->columnSpan(1),
TextInput::make('title')
->label('Title')
->columnSpan(3),
Select::make('category')
->options([
'person' => 'Person',
'object' => 'Object',
'location' => 'Location',
])
->columnSpan(3),
Hidden::make('annotation_id'),
Hidden::make('geometry'),
])
->addable(false)
->deletable(true)
->reorderable(false)
->columns(7)
->columnSpanFull()
->live()
->afterStateUpdated(function (?array $state, Set $set) {
$canvasState = collect($state ?? [])->map(fn ($item) => [
'id' => $item['annotation_id'],
'target' => json_decode($item['geometry'] ?? '[]', true),
])->toArray();
$set('annotations', $canvasState);
}),
$model->syncAnnotations([
[
'annotation_id' => 'uuid-from-annotorious',
'geometry' => ['selector' => ['type' => 'SvgSelector', 'value' => '<svg>...</svg>']],
'metadata' => ['title' => 'My Label', 'score' => 0.95], // optional
],
]);
// With colors — each annotation gets a unique color from the palette
ImageLabel::make('annotations')
->image(fn ($record) => $record?->getFirstMediaUrl())
->coloredAnnotations([
'#ef4444', '#3b82f6', '#10b981', '#f59e0b',
'#8b5cf6', '#ec4899', '#06b6d4', '#84cc16',
'#f97316', '#6366f1', '#14b8a6', '#e11d48',
])
->enableSquare()
->enablePolygon()
->live()
// Without colors — uses Annotorious default white/light styling
ImageLabel::make('annotations')
->image(fn ($record) => $record?->getFirstMediaUrl())
->enableSquare()
->enablePolygon()
->live()
use Zielu92\FilamentImageLabeler\Support\AnnotationColor;
Placeholder::make('color_swatch')
->hiddenLabel()
->content(function (Get $get) use ($palette): HtmlString {
$id = $get('annotation_id') ?? '';
$color = AnnotationColor::forId($id, $palette);
return new HtmlString(
'<div style="width: 24px; height: 24px; border-radius: 4px; '
. 'background-color: ' . $color . ';"></div>'
);
})
ImageLabel::make('annotations')
->image(function ($record, Get $get) {
if ($record && $record->getFirstMedia()) {
return $record->getFirstTemporaryUrl(now()->addMinutes(30));
}
// Handle temporary upload during create...
return null;
})
public function test_sync_creates_annotations(): void
{
$photo = Photo::factory()->create();
$photo->syncAnnotations([
[
'annotation_id' => 'ann-1',
'geometry' => ['selector' => ['type' => 'FragmentSelector', 'value' => 'xywh=pixel:10,20,100,50']],
'metadata' => ['label' => 'Person'],
],
]);
$this->assertCount(1, $photo->annotations);
$this->assertEquals('Person', $photo->annotations->first()->metadata['label']);
}
bash
php artisan migrate