[Algoritme] Berekenen kleuren en alpha-kanaal

Pagina: 1
Acties:

Vraag


Acties:
  • 0 Henk 'm!

  • Jurgle
  • Registratie: Februari 2003
  • Laatst online: 24-06 00:27

Jurgle

100% Compatible

Topicstarter
Beste Tweakers,

Om kleuren voor pixels te berekenen in een vierkante afbeelding heb ik een methode nodig, waar ik wellicht door een denkkronkel maar niet uit kom. Graag zou ik jullie hulp krijgen om die kronkel recht te maken.

Er zijn verschillende manieren om een kleur te definieren. We kennen allemaal wel het RGB model wel denk ik. Het geeft aan hoever respectievelijk het kanaal voor rood ( R ), groen ( G ) en blauw ( B ) open staat. Alle drie op 100% geeft wit, alle drie op 0% geeft zwart. Zo is 100%, 0%, 0% een volledig rood. Er zijn ook een modellen die meer aansluiten bij de manier waarop mensen kleuren waarnemen. Een van die modellen gebruikt als een van de dimensies de HUE. Dit kun je zien als een cirkel waarbij het waarneembare licht uit het spectrum van rood (0 graden) via oranje, geel, groen (120 graden), cyaan, blauw (240 graden) weer terug gaat naar rood (360 graden, of 0 graden).

Daarna zijn er verschillende manieren om met twee andere dimensies bij de kleur te komen die je zoekt. Ik gebruik een model waarbij je de verzadigdheid (saturation) aanduidt als een waarde van de HUE (100%) naar zwart (0%). Een andere dimensie is lightness/lumination, die van HUE (0%) naar wit loopt (100%). Je kunt discussieren of 0% lightness de HUE-kleur aanduidt of zwart. Ik kies voor de HUE-kleur. Daarnaast kun je discussieren of een een kleur waarbij de HUE irrelevant is (als saturation en lightness allebei 100% zijn) of dat zwart of wit is. Ik kies voor wit.

Dit kun je voor een willekeurige HUE weergeven als een vierkant met assen saturation (horizontale as) en lightness (verticale as). Dit vierkant toont links boven de betreffende HUE-kleur, rechts boven is zwart en beide hoeken in de onderkant zijn wit.

Er is een eenvoudige oplossing, waar ik niet naar op zoek ben maar wel het gewenste resultaat geeft: We hebben drie vierkanten die we over elkaar leggen, van onder naar boven noemen we die 1, 2 en 3. Vierkant 1 heeft elke pixel op de kleur van de HUE staan, met de andere kanalen 'uit'. Indien de HUE 0 is, kleur je dus een rood vierkant rgb(100%, 0%, 0%). Vierkanten 2 en 3 bevatten een alpha-kanaal: 100% alpha is volledig zichtbaar, 0% alpha is volledig doorzichtig. Over 1 leggen we vierkant 2, die volledig zwart is met een horizontale, lineaire gradient heeft in het alpha-kanaal van links 0% (doorzichtig) naar 100% (zwart). Vierkant 3 vervult vergelijkbaar die rol voor wil, maar dan met een verticale, lineaire gradient van boven 0% naar onder 100% (wit). Dit levert het figuur op dat ik zoek, echter niet op de manier die ik zoek. Zie deze afbeelding:
Zoals het zou moeten

Ik wil 'vierkant' 2 en 3 in een enkele afbeelding vatten, met een onbekende HUE. Dit generieke vierkant zou je kunnen tekenen met wit-naar-zwart-waarden, als je een alpha-kanaal toevoegt. Door 'achter' dit vierkant een vierkant te plaatsen die volledig de kleur van de HUE heeft zou je het bedoelde vierkant moeten zien verschijnen op een scherm.

Laten we de horizontale as x noemen, die van 0 naar 1 loopt, de verticale as noemen we y, die ook van 0 naar 1 loopt.

De grijstint voor een pixel in het vierkant weet je door de richtingscoefficient rc = y / x te nemen van (0, 0) (links boven) naar het betreffende punt P (x, y). Doordat de onderkant van het vierkant volledig wit is, weet je dat als rc >= 1, P(x, y) wit is.
Doordat je weet dat aan de rechter zijde van het vierkant de tint verloopt van zwart (boven) naar wit (onder) neem je de grijswaarde van coordinaat P(1, z), waarbij z de lineaire extrapolatie is van y met de rc (en met een rc die kleiner is dan 1, geeft dit een z van 0 naar 1). Dit levert onderstaande figuur op.
zwart wit resultaat

Nu het alpha-kanaal. Die verkrijg je volgens mij door y te nemen en daarbij de x-waarde op te tellen van het gedeelte dat y nog 'doorlaat'. Met ander woorden de alpha van P(x, y) = y + ((1 - y) * x). Dit levert de volgende figuur op.
Wat ik nu krijg

Merk op dat er onbedoeld een zichtbare 'witte' lijn-achtig loopt van linksboven naar rechtsonder. Hoe kan dit? Wat zit er mis in mijn methode? Gaat het bij de berekening van grijswaarde fout of zit het in het alpha kanaal? Of is mijn methode sowieso een verkeerde denkwijze?

Als we 100% op waarde 255 stellen en 0% op 0 (het model in 8 bits per kanaal in RGB voor HTML), dan zou ik in het midden 191, 128, 128 verwachten. In de afbeelding hierboven is dat duidelijk niet zo.

Bedankt voor het lezen van deze lange post en nog meer dank voor het meedenken.

[ Voor 0% gewijzigd door Jurgle op 20-07-2017 13:57 . Reden: B met een ) wordt een smiley, ik heb maar spaties toegevoegd... ]

My opinions may have changed but not the fact that I am right ― Ashleigh Brilliant

Beste antwoord (via Jurgle op 26-07-2017 15:35)


  • Klaasvaak
  • Registratie: Maart 2010
  • Laatst online: 05-10 13:10
Aangezien je alleen een zwart en een wit vlak wil samenvoegen, kan je volgens mij het makkelijkst het HSLA formaat gebruiken. Dan kan je de hue op 0 laten staan, saturation op 0% en hoef je enkel de lightness en de alpha te berekenen.
code:
1
2
3
4
5
pixelHSLA(x, y, totalWidth, totalHeight){
  alphaA = x / totalWidth
  alphaB = y / totalHeight

  h = 0

s = 0%
l = ( alphaB / (alphaA + alphaB * (1 - alphaA)) ) * 100%
a = alphaA + alphaB * (1 - alphaA)
}[/code]

Alle reacties


Acties:
  • 0 Henk 'm!

Verwijderd

Je laat bij de witte zo te zien de waarde afhangen van zowel X als Y, terwijl het alleen van Y afhangt - da's de enige manier warom je een 45 graden diagonaal kunt krijgen.

Acties:
  • 0 Henk 'm!

  • Jurgle
  • Registratie: Februari 2003
  • Laatst online: 24-06 00:27

Jurgle

100% Compatible

Topicstarter
Dank voor je antwoord. Voor de volledigheid heb ik het tweede plaatje uit de post hierboven met een zwarte achtergrond hieronder toegevoegd. Hierin is te zien dat wit alleen afhankelijk is van y:
Greyscale met zwarte achtergrond

My opinions may have changed but not the fact that I am right ― Ashleigh Brilliant


Acties:
  • 0 Henk 'm!

Verwijderd

Jurgle schreef op donderdag 20 juli 2017 @ 13:46:
Nu het alpha-kanaal. Die verkrijg je volgens mij door y te nemen en daarbij de x-waarde op te tellen van het gedeelte dat y nog 'doorlaat'.
Hier gaat het mis - alpha is gelijk aan (x / width). Zie dit plaatje:
Afbeeldingslocatie: http://blender.zaph.nl/canvas-gradient.png

De code waarmee dat gemaakt is:
JavaScript:
1
2
3
4
5
6
7
8
9
for (var y = 0; y < 256; y++) {
    for (var x = 0; x < 256; x++) {
        var alpha = x;
        var saturation = 1 - y / 256;
    /*
    hsv -> rgb omzetten, en pixels waarde geven.
    */
    }
}

Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 08-10 20:31

.oisyn

Moderator Devschuur®

Demotivational Speaker

Jurgle schreef op donderdag 20 juli 2017 @ 13:46:
Er is een eenvoudige oplossing
Misschien moet je eerst omschrijven voor welk probleem je dan een oplossing zoekt? Want nu zitten we je oplossing te debuggen zonder het probleem te kennen dat die oplossing op moet leveren.

Als het je gewoon te doen is om die ene vierkant te repliceren, dan is de kleur op pixel (x, y) (met x en y tussen 0 en 1) gewoonweg:

blend(basis_kleur * (1 - x), wit, 1 - y)
met blend(a, b, t) = a + (b-a)*t

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Jurgle
  • Registratie: Februari 2003
  • Laatst online: 24-06 00:27

Jurgle

100% Compatible

Topicstarter
Allereerst dank voor jullie replies.
Verwijderd schreef op donderdag 20 juli 2017 @ 20:12:
[...]

Hier gaat het mis - alpha is gelijk aan (x / width). Zie dit plaatje:
Hierbij is de hoek linksonder ook volledig transparant, dat is niet de bedoeling.
.oisyn schreef op donderdag 20 juli 2017 @ 21:12:
[...]

Misschien moet je eerst omschrijven voor welk probleem je dan een oplossing zoekt? Want nu zitten we je oplossing te debuggen zonder het probleem te kennen dat die oplossing op moet leveren.
Ik had het idee dat ik de probleemstelling juist best uitvoerig beschreven had. Ter verduidelijking: in de vraagstelling staan drie plaatjes. Mijn doel is het eerste plaatje als resultaat te hebben, door een willekeurige kleur als achtergrond te hebben en een plaatje met zwart-grijs-wit-alpha er overheen te leggen. Mijn probleem is het renderen van die zwart-grijs-wit-alpha-afbeelding.
.oisyn schreef op donderdag 20 juli 2017 @ 21:12:
[...]

Als het je gewoon te doen is om die ene vierkant te repliceren, dan is de kleur op pixel (x, y) (met x en y tussen 0 en 1) gewoonweg:

blend(basis_kleur * (1 - x), wit, 1 - y)
met blend(a, b, t) = a + (b-a)*t
Juist, met een bekende basis_kleur lukt het me ook wel, het gaat erom een 'generieke' overlay te renderen.

My opinions may have changed but not the fact that I am right ― Ashleigh Brilliant


Acties:
  • 0 Henk 'm!

Verwijderd

Dat komt door de lineaire benadering - daarmee krijg je altijd dat soort scherpe overgangen.

Even opsplitsend: je wilt een verticale gradient van zwart naar wit, en je wilt dat de transparantie afhangt van de afstand tot de linkerbovenhoek - respectievelijk rgb = [y, y, y] en alpha = Math.max(Math.sqrt(x*x+y*y), 255) dus.

Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 08-10 20:31

.oisyn

Moderator Devschuur®

Demotivational Speaker

Jurgle schreef op vrijdag 21 juli 2017 @ 10:52:
Ik had het idee dat ik de probleemstelling juist best uitvoerig beschreven had. Ter verduidelijking: in de vraagstelling staan drie plaatjes. Mijn doel is het eerste plaatje als resultaat te hebben, door een willekeurige kleur als achtergrond te hebben en een plaatje met zwart-grijs-wit-alpha er overheen te leggen. Mijn probleem is het renderen van die zwart-grijs-wit-alpha-afbeelding.

[..]

Juist, met een bekende basis_kleur lukt het me ook wel, het gaat erom een 'generieke' overlay te renderen.
Kijk, dat is essentiele informatie die uit je TS mist :). Je hebt gekozen voor een bepaalde oplossing, maar je hebt problemen met die oplossing, terwijl we helemaal niet wisten waar die oplossing een oplossing voor was.

Maar wil je echt vierkanten renderen? Of wil je eigenlijk gewoon een plaatje transformeren aan de hand van saturation en lightness waardes?

Anyway, als het dan echt in het vierkant moet, mijn eerdere functie is op te schrijven als:

blend(p * s, 1, l)
met p de basis kleur, s de saturation en l de lightness

Dat kun je omschrijven naar p * s * (1-l) + l
Oftewel: p * (s-s*l) + l

En laat dit nu exact overeenkomen met de blending die je doet bij een premultiplied alpha texture :)
Je alpha is dus s-s*l, en de kleur in je texture is (l, l, l)

Let wel dat je hier dus premultiplied alpha blending gebruikt. Dus one_minus_src_alpha als dest multiplier en one als source multiplier. Je kunt ook src_alpha gebruiken, maar dan moet je de kleur even delen door de alpha, wat zonde is want dan heb je precision loss.

[ Voor 8% gewijzigd door .oisyn op 21-07-2017 12:33 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • Beste antwoord
  • 0 Henk 'm!

  • Klaasvaak
  • Registratie: Maart 2010
  • Laatst online: 05-10 13:10
Aangezien je alleen een zwart en een wit vlak wil samenvoegen, kan je volgens mij het makkelijkst het HSLA formaat gebruiken. Dan kan je de hue op 0 laten staan, saturation op 0% en hoef je enkel de lightness en de alpha te berekenen.
code:
1
2
3
4
5
pixelHSLA(x, y, totalWidth, totalHeight){
  alphaA = x / totalWidth
  alphaB = y / totalHeight

  h = 0

s = 0%
l = ( alphaB / (alphaA + alphaB * (1 - alphaA)) ) * 100%
a = alphaA + alphaB * (1 - alphaA)
}[/code]

Acties:
  • 0 Henk 'm!

  • Jurgle
  • Registratie: Februari 2003
  • Laatst online: 24-06 00:27

Jurgle

100% Compatible

Topicstarter
Klaasvaak schreef op vrijdag 21 juli 2017 @ 17:56:
Aangezien je alleen een zwart en een wit vlak wil samenvoegen, kan je volgens mij het makkelijkst het HSLA formaat gebruiken. Dan kan je de hue op 0 laten staan, saturation op 0% en hoef je enkel de lightness en de alpha te berekenen.
...
Dank. Uitwerkt in JSFiddle: https://jsfiddle.net/55cwjer2/


Code van Klaasvaak in geheel in code-block:

code:
1
2
3
4
5
6
7
8
9
10
pixelHSLA(x, y, totalWidth, totalHeight){
  alphaA = x / totalWidth
  alphaB = y / totalHeight

  h = 0

  s = 0%
  l = ( alphaB / (alphaA + alphaB * (1 - alphaA)) ) * 100%
  a = alphaA + alphaB * (1 - alphaA)
}

[ Voor 26% gewijzigd door Jurgle op 26-07-2017 17:24 ]

My opinions may have changed but not the fact that I am right ― Ashleigh Brilliant

Pagina: 1