-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Color Palette Extraction #1509
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
Merged
Merged
Color Palette Extraction #1509
Changes from 60 commits
Commits
Show all changes
99 commits
Select commit
Hold shift + click to select a range
3cd33df
Implement color palette extraction
olivervogel e22d05c
Merge branch 'develop' into feat/color-palette-lazy
olivervogel 3db0b5e
Add ColorCountAnalyzer
olivervogel d45e416
Merge branch 'feat/color-count-analyzer' into feat/color-palette-lazy
olivervogel 54e8718
Implement color palette
olivervogel 288dcbf
Implement ColorPaletteModifiedMedianCutAnalyzer
olivervogel 5653bc4
Adjust sample coordinate grid
olivervogel d2f4054
Remove ColorPaletteModifiedMedianCutAnalyzer::class
olivervogel f2d3bbd
Rename classes
olivervogel 4d94255
Refactor
olivervogel aa99578
Refactor
olivervogel ae35763
Add tests
olivervogel 148c88a
Refactor
olivervogel d2b6c00
Add tests
olivervogel 6b95279
Add tests
olivervogel eb392ec
Add tests
olivervogel bc9a661
Add tests
olivervogel 1fffde4
Rename PaletteColor to QuantizedColor
olivervogel 10a291b
Fix analyzer
olivervogel 2d07028
Update maxColors default value
olivervogel 806976b
Rename method
olivervogel 2c63321
Redesign color palette API
olivervogel 261470c
Add tests
olivervogel bc9806c
Add comments
olivervogel 1c500f4
Remove unused PaletteException
olivervogel 012fa25
Add tests
olivervogel bfb500e
Refactor Quantizer logic
olivervogel 080cec7
Fix Analyzers
olivervogel b102ce2
Refactor
olivervogel 2ab0f81
Fix bug
olivervogel 277b7c7
Refactor
olivervogel c3d7ae4
Edit description
olivervogel 677b7bb
Refactor swatches
olivervogel f1090e4
Refactor
olivervogel 56e1e64
Fix reseeding PHP's global RNG
olivervogel 8a15a28
Add condition for Rng::getInt call
olivervogel f337d72
Fix bug
olivervogel 3d3e2fe
Fix palette sort order
olivervogel 5136371
Add gates
olivervogel 7ebd0b3
Fix bug
olivervogel af19f81
Fix
olivervogel ef8b529
Add Test
olivervogel c4eda2c
Fix sorting palettes loosing color details
olivervogel 9a57742
Fix
olivervogel 535bd6d
Remove try/catch
olivervogel 9bfef2f
Refactor
olivervogel 2399dec
Remove redundant code
olivervogel fd95c10
Improve code
olivervogel a8d3d28
Add @template to phpcs config
olivervogel 13a384e
Add type hints
olivervogel eba9403
Edit comments
olivervogel af3d4ac
Merge Histogram into Palette
olivervogel 280aedb
Update Swatches
olivervogel 51941ef
Make SwatchesInterface more flexible
olivervogel 344d492
Add region limiter to ColorExtractor
olivervogel 6a5360f
Add tests
olivervogel bccf420
Fix bug
olivervogel ee2824e
Remove redundant code
olivervogel d313a61
Rename variables
olivervogel d7064e9
Remove unused code
olivervogel 8c14e76
Implement PaletteInterface::sortByPresence()
olivervogel 9210a9b
Fix bug
olivervogel bb66791
Add assertions
olivervogel 5a2b468
Fix bug
olivervogel 8f91ddf
chore: remove unrelated whitespace change in Collection
deluxetom 64af127
fix(colors): correct palette colorspace conversion, mixed sorting and…
deluxetom 5bd670c
fix(colors): extract actual image colors with driver-independent quan…
deluxetom 5eac98f
fix(analyzers): make dominant color analysis deterministic and fast
deluxetom fc261f6
fix(analyzers): validate sample region position within image bounds
deluxetom fb6b5cb
fix(colors): validate swatches classname before instantiation
deluxetom 6f0d931
fix(swatches): keep object usable after array offset unset
deluxetom 15ee1e2
feat(image): declare colors() in ImageInterface
deluxetom 2fb1245
Remove colors() from ImageInterface to prevent BC
olivervogel 4c582d0
Remove unused ColorCountAnalyzers
olivervogel c21583d
Lower verbosity
olivervogel 7383917
Create DominantPaletteAnalyzer::randomize()
olivervogel 7bd2571
Refactor
olivervogel 4479985
Add secondary order for sorting colors with same presence
olivervogel 71b40ff
Add test for Quantizer bug reproduction
olivervogel 19043c6
Rename classes
olivervogel 42074d5
A little bit of everything
olivervogel ee1e65f
Add PaletteInterface::quantize() and PaletteInterface::reduce()
olivervogel 650934f
Implement PaletteInterface:hasColor()
olivervogel ae4137e
Add assertion
olivervogel 16ede3d
Edit descriptions
olivervogel 77ba9e8
Add PHPDocs generics for ColorExtractor::swatches()
olivervogel 0c6f868
Edit description
olivervogel cda2f5c
Fix bug in ArrayAccess implementation of AbstractSwatches
olivervogel 2c3d015
Update PaletteInterface::reduce() logic
olivervogel 8daf4a9
Optimize performance of AbstractPaletteAnalyzer::collectColors()
olivervogel ef38cef
Add PHPDoc generics
olivervogel 0351034
Refactor array destructuring in AbstractPaletteAnalyzer
olivervogel 80e088d
Edit description
olivervogel 321c0d5
Refactoring and renaming
olivervogel b569602
Implement PaletteInterface::map() and filter()
olivervogel 7b5eed9
Rename argument
olivervogel 6120929
Rename method
olivervogel ad7de84
Add Theme enum
olivervogel b872a10
Edit default value
olivervogel File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -124,6 +124,7 @@ | |
| @uses, | ||
| "/> | ||
| <element value=" | ||
| @template, | ||
| @param, | ||
| @throws, | ||
| @return, | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Intervention\Image\Analyzers; | ||
|
|
||
| use Generator; | ||
| use Intervention\Image\Exceptions\InvalidArgumentException; | ||
| use Intervention\Image\Interfaces\AnalyzerInterface; | ||
| use Intervention\Image\Interfaces\ColorInterface; | ||
| use Intervention\Image\Interfaces\ImageInterface; | ||
| use Intervention\Image\Interfaces\SizeInterface; | ||
|
|
||
| abstract class AbstractPaletteAnalyzer implements AnalyzerInterface | ||
| { | ||
| /** | ||
| * Collect pixel colors from the image. | ||
| * | ||
| * @throws InvalidArgumentException | ||
| * @return Generator<ColorInterface> | ||
| */ | ||
| protected function collectColors(ImageInterface $image, ?SizeInterface $region = null): Generator | ||
| { | ||
| foreach ($this->sampleCoordinates($image->size(), $region) as $coordinate) { | ||
| $color = $image->colorAt(...$coordinate); | ||
| if ($color->isClear()) { | ||
| continue; | ||
| } | ||
|
|
||
| yield $color; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Get dynamic grid of pixel sample coordinates according to current image size. | ||
| * | ||
| * @throws InvalidArgumentException | ||
| * @return Generator<array{x: int, y: int}> | ||
| */ | ||
| protected function sampleCoordinates(SizeInterface $size, ?SizeInterface $region = null): Generator | ||
| { | ||
| $region = $region === null ? $size : $region; | ||
|
|
||
| if (!$region->fitsWithin($size)) { | ||
| throw new InvalidArgumentException('The region must not be larger than the actual image size'); | ||
| } | ||
|
|
||
| $startX = $region->pivot()->x(); | ||
| $startY = $region->pivot()->y(); | ||
| $width = $region->width(); | ||
| $height = $region->height(); | ||
| $totalPixels = $width * $height; | ||
|
|
||
| $sampleRate = match (true) { | ||
| $totalPixels <= 10000 => 1, // <= 10k pixels: sample all | ||
| $totalPixels <= 100000 => 5, // 10k-100k: every 5th pixel | ||
| $totalPixels <= 500000 => 10, // 100k-500k: every 10th pixel | ||
| $totalPixels <= 2000000 => 20, // 500k-2m: every 20th pixel | ||
| default => 30, // > 2m: every 30th pixel | ||
| }; | ||
|
|
||
| $endX = $startX + $width; | ||
| $endY = $startY + $height; | ||
|
|
||
| for ($y = $startY; $y < $endY; $y += $sampleRate) { | ||
| for ($x = $startX; $x < $endX; $x += $sampleRate) { | ||
| yield ['x' => $x, 'y' => $y]; | ||
| } | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Intervention\Image\Analyzers; | ||
|
|
||
| use Intervention\Image\Drivers\SpecializableAnalyzer; | ||
|
|
||
| class ColorCountAnalyzer extends SpecializableAnalyzer | ||
| { | ||
| // | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fitsWithin()ignores the pivot, so an offset region passes and then samples out of bounds.fitsWithin()compares width and height only. A small region positioned near the edge passes this guard and then blows up further down inPixelColorAnalyzer:The error the user sees is also misleading, since it points at a pixel coordinate rather than at the region they passed.
Suggested fix: validate the region including its origin, for example
pivot()->x() + width() <= $size->width()and the same fory, or clamp$endX/$endYto the image bounds.