Questa è davvero una bella domanda. Se guardi allo source code puoi vedere che i diversi campioni vengono scelti analizzando il profilo di colore HSL dei pixel sull'immagine, in base agli intervalli target di luminanza, saturazione e popolazione (quanti pixel nell'immagine sono rappresentati dal campione). Utilizza un calcolo della media ponderata con preferenza data a luminanza, quindi a saturazione, quindi a popolazione.
In generale, i colori vivaci sono più saturi rispetto ai colori tenui, i colori scuri sono più scuri e quelli chiari sono più chiari. Quale si utilizza dipende dall'effetto generale che si desidera.
Chris Banes wrote in his blog "Vibrant e Dark Vibrant sono quelli che gli sviluppatori useranno principalmente" ma in pratica non è così semplice.
Un esempio che ho trovato è il metodo in Romain Guy's sample app da Google IO 2014, anche se il codice presuppone che i vari campioni verranno trovati (probabilmente perché sta lavorando con immagini conosciute).
A seconda dell'immagine, è possibile che alcuni tipi di campioni non vengano trovati, quindi accertarsi di tenere conto di tale possibilità nel codice.
Ad esempio, è possibile provare a prelevare campioni dalla tavolozza in un ordine specifico, ad es. per un tema Dark potresti provare a ottenere Vibrant Dark, quindi Muted Dark, e quindi tornare su un colore predefinito.
Se si desidera qualcosa che è un po 'più prevedibile, è anche possibile afferrare il colore più rappresentato, in questo modo:
public static Palette.Swatch getDominantSwatch(Palette palette) {
// find most-represented swatch based on population
return Collections.max(palette.getSwatches(), new Comparator<Palette.Swatch>() {
@Override
public int compare(Palette.Swatch sw1, Palette.Swatch sw2) {
return Integer.compare(sw1.getPopulation(), sw2.getPopulation());
}
});
}
I valori HSL per ciascun campione sono anche accessibili, così si potrebbe scrivere una routine simile da scegliere, ad esempio, il campione più saturo che non sia il colore dominante.
Uso obiettivi personalizzati
Un'altra cosa che ho trovato utile è definire alcuni obiettivi personalizzati in aggiunta ai 6 obiettivi definiti nei Palette.Target
, con diversi pesi e indirizzare valori di luminosità e saturazione, in per aumentare la possibilità di trovare un colore utile.
Ad esempio, si può chiedere di quantizzazione di Palette per includere un campione per il colore più dominante che soddisfa i criteri di filtro attuali con un obiettivo come questo:
public static final Target DOMINANT;
static {
DOMINANT = new Target.Builder().setPopulationWeight(1f)
.setSaturationWeight(0f)
.setLightnessWeight(0f)
.setExclusive(false)
.build();
}
E si può ottenere alcuni campioni utili che mettono a maggiore priorità sulla leggerezza rispetto agli obiettivi stock come questo:
public static final Target DARK;
public static final Target LIGHT;
public static final Target NEUTRAL;
static {
DARK = new Target.Builder().setMinimumLightness(0f)
.setTargetLightness(0.26f)
.setMaximumLightness(0.5f)
.setMinimumSaturation(0.1f)
.setTargetSaturation(0.6f)
.setMaximumSaturation(1f)
.setPopulationWeight(0.18f)
.setSaturationWeight(0.22f)
.setLightnessWeight(0.60f)
.setExclusive(false)
.build();
LIGHT = new Target.Builder().setMinimumLightness(0.50f)
.setTargetLightness(0.74f)
.setMaximumLightness(1.0f)
.setMinimumSaturation(0.1f)
.setTargetSaturation(0.7f)
.setMaximumSaturation(1f)
.setPopulationWeight(0.18f)
.setSaturationWeight(0.22f)
.setLightnessWeight(0.60f)
.setExclusive(false)
.build();
NEUTRAL = new Target.Builder().setMinimumLightness(0.20f)
.setTargetLightness(0.5f)
.setMaximumLightness(0.8f)
.setMinimumSaturation(0.1f)
.setTargetSaturation(0.6f)
.setMaximumSaturation(1f)
.setPopulationWeight(0.18f)
.setSaturationWeight(0.22f)
.setLightnessWeight(0.60f)
.setExclusive(false)
.build();
}
è possibile accedere al campione trovato per un target personalizzato dopo che è stato generato utilizzando Palette.getSwatchForTarget, ad esempio:
Palette.Swatch neutral = Palette.getSwatchForTarget(NEUTRAL);
Siate consapevoli del filtro predefinito
Per impostazione predefinita Palette
ha un Palette.Filter
che rifiuta i colori molto vicino al bianco o nero, così come i colori "molto vicino alla linea I rossa" che credo si riferisca a colori isocroni che sono difficili per le persone con cecità ai daltonici.
La mia teoria è che rifiuta i colori quasi bianco e quasi nero al fine di evitare che vengano scelti come colori "saturi" quando la saturazione è fortemente ponderata.
Il risultato di questo filtro tuttavia è che non verrà trovato alcun campione per le immagini che consistono interamente di pixel quasi bianchi e/o quasi neri, nonché una tendenza ad evitare di scegliere i colori con una tonalità rosata.
Tuttavia, è possibile rimuovere questo filtro utilizzando Palette.Builder.clearFilters()
e aggiungere il proprio filtro con Palette.Builder.addFilter()
.
Nel mio codice ho scelto di fare un secondo tentativo di generare una tavolozza se il primo tentativo non restituisce alcun campioni:
Palette palette = new Palette.Builder(bitmap).addTarget(DOMINANT)
.addTarget(DARK)
.addTarget(LIGHT)
.addTarget(NEUTRAL)
.generate();
if(palette.getSwatches().isEmpty()) {
Log.v(TAG, "Getting alternate (UNFILTERED) palette.");
palette = new Palette.Builder(bitmap).addTarget(DOMINANT)
.addTarget(DARK)
.addTarget(LIGHT)
.addTarget(NEUTRAL)
.clearFilters() /// allow isBlack(), isWhite(), isNearRedILine()
.generate();
}
concatenazione i tentativi conserva l'utilità del filtro di default nella maggior parte casi, ma consente comunque di trovare campioni per le immagini che l'impostazione predefinita rifiuterebbe completamente.
Ottima risposta, grazie! Apprezzo anche le idee per buoni fallback quando non si trovano campioni. :) –
A partire dalla palette-v7: 25.0.0, ora esiste un metodo di convenienza chiamato getDominantSwatch: l'implementazione di un metodo personalizzato non dovrebbe più essere necessaria. Il resto della risposta è ancora valida e buona però! –
@DavidDoyle Sì, è vero! Hanno aggiunto un metodo equivalente che seleziona il campione con la popolazione più alta. Tuttavia devo ammettere che nel mio codice ho trovato più efficace usare il bersaglio dominante (con peso di popolazione 1f e gli altri pesi 0f) perché trova un colore più dominante in generale, invece di scegliere il campione più popolato tra quelli che ha soddisfatto gli altri criteri di destinazione (VIBRANT, MUTED, ecc.). –