« 1 2 »
Autor Zpráva
Návštěvník
Profil *
Nedávno jsem tu psal vlákno, že si chci udělat program ve kterém mohu změnit některé barvy v obrázku (topic #155195). Tam to mám udělané tak, že se zaměňuje jedna konkrétní barva za jednu barvu nebo např. tři konkrétní barvy za tři konkrétní jiné barvy. Z praktického hlediska by ale bylo lepší udělat program tak, aby uživatel zadal pouze jednu barvu, kterou určí do kterého odstínu chce přesunout celý obrázek. Například obrázku (clipartu) rtů, kde je použito kolem sedmi červených odstínů + jejich vzájemné přechody, by bylo třeba určovat ručně všechny barvy (klikat pokaždé na color picker by bylo hodně nepraktické). A tak mě zajímá jestli víte o nějakém mechanismu, tedy výpočtu, který by vypočítal rozdíl mezi tou barvou, kterou se vybral uživatel a barvou která je použita jako dominující na obrázku. Nejlépe, kdyby bylo možné vypočítat barvu ve smyslu toho jak ji používá např. Photoshop, kde slovo hue znamená vlastně odstín, nikoliv míru světla nebo tmy (svítivost). Takže by mě zajímal ten přepočet z barvy rgb na nějaké číslo, které určuje odstín a pak matematická korelace a nakonec zase převod zpět.
juriad_
Profil *
Hledej HSV. A funkce na prevod RGB do HSV. Na StackOverflow jsem je zahlednul.
Dominantni barva = hue prumerne barvy vypocitane v RGB.
Jakmile zjistis rozdil hue, tak ho jen prictes k hue kazdeho pixelu a prevedes zpet na RBG.
juriad
Profil
Případně HSL, ty dva prostory se moc neliší. Hue (odstín) mají dokonce definovaný identicky.
Docela pěkně vypadá http://lux.googlecode.com/svn/trunk/Lux/Color.php, umí to všechny konverze a BSD licence ti umožňuje to bez jakýchkoli omezení použít.
peta
Profil
Návštěvník:
http://mlich.zam.slu.cz/js-color4.htm - druha tabulka, zadas do sedeho sloupce barvu, on k ni dopocita sedy odstin a propocita ostatni barvy.
Co se tyce obrazku, tak jsem experimentoval s svg a css
Tez to asi pujde delat s canvas, staci vygooglovat par prikladu.
<style>
img.grayscale {
filter: url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#grayscale"); /* Firefox 10+, Firefox on Android */
filter: gray; /* IE6-9 */
-webkit-filter: grayscale(100%); /* Chrome 19+, Safari 6+, Safari 6+ iOS */
}

img.grayscale2 {
filter: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><filter id='grayscale2'><feColorMatrix type='matrix' values='0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0 0 0 1 0'/></filter></svg>#grayscale2"); /* Firefox 10+, Firefox on Android */
filter: gray; /* IE6-9 */
-webkit-filter: grayscale(100%); /* Chrome 19+, Safari 6+, Safari 6+ iOS */
}

</style>
<img src="butt-spinavemodra.png" class="grayscale"><br>
<img src="butt-spinavemodra.png" class="grayscale2"><br>
<img src="butt-spinavemodra.png">
nebo
<!DOCTYPE html>
  <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
      <style>
    svg {
        height: 0;
        }
    p {
        background-image: url("butt-spinavemodra.png");
        margin: 10px auto;
        filter: url(#grayscale);
        display: block;
        width: 250px;
        height: 100px;
        }
      </style>
      <title>SVG Filters - Grayscale Effect</title>
    </head>

    <body>
      <p>
        Test box
      </p>

      <p>
        Test box
      </p>

      <svg height="0" xmlns="http://www.w3.org/2000/svg">
        <filter id="grayscale">
          <feColorMatrix values="0.3333 0.3333 0.3333 0 0
                                 0.3333 0.3333 0.3333 0 0
                                 0.3333 0.3333 0.3333 0 0
                                 0      0      0      1 0" />
        </filter>
      </svg>
    </body>
</html>
Návštěvník
Profil *
peta:
můžeš poslat to tvoje svg, abych se mohl podívat co to dělá?


Díky moc za odkazy, vypadá to slibně. Ten js-color4.htm je volně šiřitelný?
Návštěvník
Profil *
juriad:
Proč to píše chybu?
Unsupported operand types in P:\www\color_convertors\HSL.php on line 122
$var_R = ($rgb[0] / 255);

$mycolors = array(array(255,0,0),array(255,126,126),array(86,13,0));
$lux = new Lux_color();
$hsv = $lux->rgb2hsv($mycolors);
print_r($hsv);



sorry, blbá otázka, už vím


Takže výsledkem té metody rgb2hsv je hue? Pochopil jsem správně?


Asi ne. Vrací pole s třemi elementy
0 => Hue, 1 => Saturation, 2 => Lightness

Tak jsem napsal tenhle první kód:
http://paste.ofcode.org/rc4f3z38S2Vr8VxbX3SvNU

viz dole na konci souboru.

Zadávám tři barvy z obrázku standardní červená, světle červená a tmavě červená alias brown. Snažím se získat průměrnou barvu a výsledkem jsem dostal cyan #2BFFFF . Což je ale špatně, protože by mě měla vyjít červená nebo hnědá možná.
juriad
Profil
Průměrnou barvu musíš počítat v nějakém prostoru, ve kterém podobné barvy jsou kódované podobně.

Hue je kruh v rozsahu 0 -- 360 stupňů. Nestačí tedy aritmetický průměr, musíš provést průměr v geometrickém smyslu.
Barva je komplexní číslo saturation*( cos(hue) + i*sin(hue)), když je zprůměruješ přes všechny pixely v obrázku vyjde ti komplexní číslo kódující průměrnou „barvu“.
Průměrný odstín je úhel v geometrické reprezentaci tohoto komplexního čísla.
Násobení saturací je tam pro zvýšení váhy barvám, které jsou v obrázku syté; můžeš si tam klidně přidat jinou závislost na saturaci a světlosti, ale tato mi přišla jednoduchá a snad i použitelná (nezkoušel jsem).

Jinou možností je provést aritmetický průměr v prostoru RGB, po složkách. Bude to zase jen nějaká aproximace, ale ze zkušenosti funguje docela dobře.
Navíc je to triviální na implementaci.
Návštěvník
Profil *
juriad:
saturation*( cos(hue) + i*sin(hue))
Co je to i?
juriad
Profil
Návštěvník:
Aha, ty nemáš znalosti komplexních čísel, co? http://cs.wikipedia.org/wiki/Komplexn%C3%AD_jednotka
Neřeš to, lze to „emulovat“ i pomocí souřadnic x a y.

Zkus udělat ten průměr v RGB, pokud nebude kvalitativně stačit, tak ti vysvětlím ten první návrh a třeba ti s ním pomůžu.
peta
Profil
Návštěvník: Svg kod v obou prikladech je snad, ne? Zkus hledat slovo svg :)
A ten link, free, mno, to je normalni matematika a tabulka. Jak se pocita HSL, a pod, si najdes treba na wiki. To neni nic tajneho.
Bezny grayscale se pocita jako... http://en.wikipedia.org/wiki/Grayscale
Y' = 0.299 * R' + 0.587 * G' + 0.114 * B' nebo
Y' = 0.2126 * R' + 0.7152 * G' + 0.0722 * B'.
Aby z toho byla zas rgb seda barva, tak se to spoji jako YYY :) Kdyz to chces obarvit, tak musis zpetne napocitat z YYY opet RGB.
Návštěvník
Profil *
peta:
hledal, jenže dostávám jpeg obrázky
juriad
Profil
Vytvoří obrázek, který vyplní sytou průměrnou barvou vypočítanou na základě HSV:
<?php

include 'Color.php';

function average($im) {
    $w = imagesx($im); // image width
    $h = imagesy($im); // image height

    $avg = [0, 0];
    for ($x = 0; $x < $w; $x++) {
        for ($y = 0; $y < $h; $y++) {
            $rgb = imagecolorat($im, $x, $y);
            $r = ($rgb >> 16) & 0xFF;
            $g = ($rgb >> 8) & 0xFF;
            $b = $rgb & 0xFF;
            $rgb = [$r, $g, $b];

            $hsv = Lux_Color::rgb2hsv($rgb);

            $avg[0] += ($hsv[1] + $hsv[2]) / 2 * $hsv[1] * cos($hsv[0] * 2 * M_PI);
            $avg[1] += ($hsv[1] + $hsv[2]) / 2 * $hsv[1] * sin($hsv[0] * 2 * M_PI);
        }
    }
    var_dump($avg);
    return getAngle($avg[0], $avg[1]);
}

function getAngle($x, $y) {
    if ($x >= 0) {
        if ($y >= 0) {
            return atan2($y, $x) / (2 * M_PI);
        } else {
            return getAngle(-$y, $x) + 0.75;
        }
    } else {
        if ($y >= 0) {
            return getAngle($y, -$x) + 0.25;
        } else {
            return getAngle(-$x, -$y) + 0.5;
        }
    }
}

var_dump(Lux_Color::rgb2hsv([255, 255, 0]));

$im = imagecreatefrompng("php.png");
$w = imagesx($im); // image width
$h = imagesy($im); // image height

$ah = average($im);
var_dump($ah);
$rgb = Lux_Color::hsv2rgb([$ah, 1, 1]);
$c = imagecolorallocate ($im, $rgb[0], $rgb[1], $rgb[2]);
var_dump($c);
imagefilledrectangle ($im, 0, 0, $w, $h, $c);

imagepng($im, 'a.png');
imagedestroy($im);
Návštěvník
Profil *
juriad:
Díky za kód. Zítra se na to podívám. Docela blbé ale je že tomu nerozumím. To HSV se používá stejně jako HSL? Mohl bys mi ještě vysvětlit co znamená to i ve tvém vzorci? Je to intenzita světla?
juriad
Profil
To i je komplexní jednotka. Jde o rozšíření komplexních čísel. Jistě víš, že umocnit na druhou můžeš libovolné číslo, ale odmocnit už ne. Odmocňování selže u záporných čísel.
Pro další zápis matematiky budu používat TeXové značení, mělo by snad být zřejmé.
Jde však vybudovat teorie, kdy si \sqrt{-1} (odmocnina z mínus jedné) označíš jako i.
Je jasné, že i není reálné číslo. Je to něco úplně jiného.

Pak si verzmeš číslo 1+i a obecně a+b*i, kde a a b jsou reálná čísla. S tímto číslem můžeš normálně počítat - zvlášť reálnou část (ta bez i), zvlášť imaginární část (ta s i).

(a+b*i) + (c+d*i) = (a+c) + (b+d)*i // sčítání
(a+b*i) * (c+d*i) = a*c + a*d*i + b*i*c + b*i*d*i = a*c + a*d*i + b*c*i - b*d = (a*c-b*d) + (a*d+b*c)*i // násobení
Zvýrazněné znaméno plyne z i*i = \sqrt{-1} * \sqrt{-1} = \sqrt{-1}^2 = -1
S komplexními čísly tedy můžeš provádět všechny běžné operace. (Nemůžeš je ale porovnávat, které je větší.)

Uvědom si, že starý známý vzoreček pro řešení kvadratických rovnic x_{1,2} = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a} (po vykreslení vypadá vzorec takto: pro některé hodnoty nefunguje a říkáme, že kvadratická rovnice nemá řešení. Příkladem je x^2-10+29 = 0. Tam vychází odmocnina z -16.
V reálných číslech tato rovnice opravdu řešení nemá. Ale, co kdybych si dovolili malý trik.
\sqrt{-16} = \sqrt{16} * \sqrt{-1} = 4*i
A můžeme pokračovat v řešení; vychází x_{1,2} = \frac{10 \pm 4*i}{2} = 5 \pm 2*i. Hurá máme řešení.
Zkusíme zkoušku jednoho z řešení. (5+2*i)^2 - 10*(5+2*i) + 29 = 21+20*i - (50+20*i) + 29 = -29 + 29 = 0 Ono to funguje; druhé řešení dopadne stejně.

A teď si představ, že bys komplexní číslo zakreslil do roviny - na vodorovnou osu budeš zanášet reálnou část a na vertikání osu budeš zanášet imaginární část komplexního čísla.
Stará známá reálná čísla budou odpovídat horizontální ose. Všechna ostatní komplexní čísla budou mimo tuto osu.
Toto nakreslení se nazývá komplexní rovina.

Tato rovina má počátek bod 0+0*i, doprava jsou kladná reálná čísla.
Každý bod roviny lze vyjádřit dvojicí: vzdáleností od počátku a orientovaným úhlem od kladné reálné polosy.
Zápis čísla a+b*i ve formátu d * (cos(phi) + i*sin(phi)) se nazývá geometrický.
Platí pro něj a = d*cos(phi); b = d*sin(phi)*i.
Naopak d = \sqrt{a^2+b^2}; phi = \arctg{b/a}. U phi je navíc třeba si trochu pohrát s periodicitou, protože arctg má obor hodnot jen -PI/2 až PI/2.
Bohužel se kompexní čísla v geometrickém tvaru nechutně špatně sčítají, ale o to nádherněji se násobí a dokonce umocňují.

A teď souvislost s barvami. Odstín je kruh 0 -- 360 stupňů.
Pokud hledáš průměrný odstín, můžeš si na tom kruhu vyznačit odstín každého pixelu. A pak všechny ty body na tom kruhu sečteš v geometrickém významu. Tedy opačné odstíny (třeba zelená - fialová) se sečtou nic (střed kruhu). Čím více bude pixelů daného odstínu, tím více bude táhnout průměr dále od středu kruhu svým směrem.
Tyto součty a průměry lze počítat zvlášť v x-ové a zvlášť v y-ové složce.

Pozn. obyčejný aritmetický průměr odstínů nefunguje proto, že některé odstíny jsou daleho aritmeticky (například odstíny 10 a 350 jsou vzdálené 340), ale jsou blízko přes roh (jen 20). Navíc existují opačné odstíny (třeba 20 a 200), takové se požerou navzájem.

A hele, sčítání zvlášť v každé složce umí kompexní čísla. Pomocí jejich geometického zápisu lze snadno zapsat konkrétní odstín na kruhu. Vzdálenost od středu u každého pixelu značí, jak moc je ten odstín v tom pixelu výrazný.
Takže jsem původní problém přeformuloval na počítání s kompexními čísly.

Bohužel PHP neumí pracovat s komplexními čísly přímo (narozdíl od jiných jazyků: C, Python) a tak jsem si je nahradil pomocí složek x a y.
Jakmile se naučíš pracovat s kompexními čísly, je v nich přirozené přemýšlet a využívat jejich vlastností.
Kompexní čísla nejsou jen hříčkou, ale opravdu se používají skoro všude. Pomocí funkcí s komplexními proměnnými lze řešit úlohy, které jsou jinak neřešitelné.

Mimochodem, to ses na střední nepotkal z komplexními čísly? Nebo jste k nim ještě nedošli?
Fuj, to byla dřina.
Návštěvník
Profil *
Člověče, diskriminant jsem používal naposled když jsem byl o polovinu svého věku mladší. To abych si oprášil znalosti shlédnutím nějakého výukového videa. Ale zhruba, myslím že chápu o čem mluvíš. S tím texem mám problém, tohle já vůbec nepoužívám. To geometrické znázornění se sinem a cosinem už mi je trochu bližší. Škoda, o komplexních číslech jsem se nikdy neučil. Ta část v souvislosti komplexních čísel s tím barevným prostorem se mi špatně chápe. Těžko představitelné. Není na netu nějaké online udělátko, ve kterém by se ilustroval onen problém, něco jako třírozměrný color picker (nějaká třírozměrná krychle nebo válec) na kterém by si to člověk mohl vyzkoušet, co by to udělalo, když by se to použilo špatně / správně nebo nějaká další ilustrační pomůcka pro pochopení té třetí souvislosti?
juriad
Profil
Návštěvník:
Jelikož ti při výpočtu jde o průměrný odstín, zakreslíš si všechny odstíny do kruku.
Řešíš tedy dvě úlohy:
1) Jak promítnout každou barvu do kruhu odstínů
2) Jak určit průměr, když máš body na kruhu

První úloha je jednodušší, neboť chápeš, že každou barvu lze vyjádřit v několika různých barevných prostorech (RGB, HSV, ...).
Přirozeně budeš chtít provést transformaci takovou, že ve výsledném prostoru bude jedna složka přesně odpovídat odstínu.
Takovým vhodným prostorem je HSV nebo HSL.
OK, první část je hotová. Máme funkci, která umí převést barvu na čistý odstín zanedbáním zbylých dvou složek v cílovém prostoru.

Druhá úloha je spíše filosofická.
Jak definujeme průměrného reprezentanta bodů na kružnici?
Uvědom si, že neprůměrujeme jednoduché číselné hodnoty (důvod jsem ukazoval dříve).
Jednou možností je rozsekat kruh na čtvrtiny, osminy atd. a najít takový dílek, do kterého padne největší počet bodů.
Jistě tušíš, že toto nebude mít pěkné vlastnosti: pokud těch dílků bude málo, bude výsledek příliš hrubý; pokud jich naopak bud hodně, může se stát, že do žádného dílku nepadne moc barev, ale přesto bude existovat skupina dílků, které jsou blízko sebe obsahující většinu bodů.

Z toho důvodu jsem zvolil geometrický součet jednotlivých bodů.
Na začátku jsem ve středu kruhu, pak vezmu odstín prvního bodu obrázku a posunu se ze středu ve směru tohoto odstínu.
Dále pak budu považovat tento bod za nový střed kruhu a s dalším odstínem bodu obrázku se pohnu v daném směru.
Je vidět, že pokud bude obrázek obsahovat jen stejný počet bodů dvou opačných odstínů, pohnu se vždy v jednom směru a opačný odstín mě vrátí zpět - budu kmitat kolem počátku a výsledkem je pozice, kde jsme začínal.
Pokud těch odstínů bude víc a nebudou stejně časté, vždy vyběhnu z počáteční pozice některým směrem a právě tento směr mi udává, který odstín budu považovat průměrný.
Když si představím, že dva podobné odstíny (na kruhu blízko sebe) se budou přetahovat, tak můj odhad průměru se bude pohybovat ve výseči mezi nimi.
Čím dál od středu se během této procházku dostanu, tím dominantnější je to odstín.
Výsledný průměrný odstín získám jako průsečík půvdního kruhu v počátku s úsečkou spojující počátek s bodem, ve kterém jsem skončil.
Tento průsečík odpovídá nějakému odstínu.

Jako zlepšení (teoretické, možná to v praxi nebude fungovat pěkně) jsem si dovolil neposouvat vždy o celý poloměr (střed -> bod na kružnici), ale velikost posunutí jsem vztáhl k dalším dvoum složkám barvy v cílovém prostoru.
Čím nečistější barva (méně sytá či přesvícená), tím méně vhodná je pro můj odhad průměru.
Takovou barvu znevýhodním menším posunem; čistou barvu reprezentuji maximálním posunem).

Online si můžeš zkusit nějaké color-pickery, stačí hledat.
Můžu doporučit http://colorschemedesigner.com/, umí toho mnohem více; je však zaměřený spíše na design - jak spolu barvy ladí.
O jiné interaktivní pomůcce nevím; můžeš však koukat na hromadu obrázků na googlu nebo si zkusit něco svého naprogramovat.

TeX je nástroj pro přesnou sazbu textů, autor však potřeboval sázet i matematické vzorce, tak jejich podporu zabudoval přímo do jazyka.
Pomocí několika málo značek lze napsat libovolný výraz.
Stříška značí horní index (nebo mocninu), podtržítko dolní index, \frac je zlomek, \sqrt je odmocnina, závorky {} seskupují nějakou část, \pm je plus mínus.
Návštěvník
Profil *
Můžeš mi vysvětlit co je v proměnné $ah? Očekával bych tři hodnoty, ale jsou tam jen dvě. Tvou funkci jsem přejmenoval, v average mám výpočet průměru z mých vybraných barev. Jde o to, že např. černá se nezahrnuje do průměru. Já si to představoval tak jak to mám v average(), že zadám několik základních barev bez nutnosti zadávat přechody mezi základními barvami a to vypočítá průměr. U tebe to je asi trochu jinak, protože počítáš se všema barvama, takže nevím co z toho vyleze, ale teď nevím jak dál když average vrací jen dvě hodnoty.

http://paste.ofcode.org/AEj4R6EdLTNBkAWp4p5hrS
juriad
Profil
Asi myslíš proměnnou $avg. Opravdu má jen dvě složky.
$avg[0] je x-ová složka bodu v rovině
$avg[1] je y-ová složka body v rovině

Pěkně je to vidět na tomto obrázku (ten pravý)
Máš barevný kruh a ten vektor (šipka ze stredu) označuje vypočítanou průměrnou barvu (malý modrý puntík).
Jeho polohu můžeš vyjádřit buď pomocí x a y (vůči vodorovné a svislé ose, které na obrázku nejsou nakresleny), nebo pomocí vzdálenosti od středu a úhlu od kladné vodorovné polosy. Naznačeno svislou zahnutou šipkou vycházející z červené.

Funkce getAngle() pak z polohy vyjádřené ve složkách x a y v rovině vypočítá odpovídající úhel.
Návštěvník
Profil *
No ve tvém kódu používáš:
$ah = average($im); // průměr vypočítaný z obrázku
var_dump($ah);
$rgb = Lux_Color::hsv2rgb([$ah, 1, 1]);

Já používám
$avg_color = color_math::average( color_math::HEXarr2RGBarr($myColors) ); // průměr vypočítaný z vybraných barev
$rgb = Lux_Color::hsv2rgb(array($avg_color, 1, 1)); // už jsem si to opravil

Tak můj výsledek: obrázek úst má světle červeno oranžovočervenou matnou barvu a kraje jsou světlou nebo sytější červenou. Výsledný obrázek, který se mi vygeneroval je červený sytý, takže vlastně to je ten cílový odstín podle kterého by se měl změnit obrázek. A já si teď myslím, že když na to aplikuju tuto barvu tak ten obrázek se zbarví do hodně červené (plná sytost barvy - tato barva ale v obrázku není protože obrázek je matný nebo "pastelový"). Blíže, zdá se že byl tvůj kód který mi vracel sytou oranžovou, ale taky to bylo syté.

Co bych musel udělat, kdyby mě zajímala místo syté barvy barva nesytá, tedy včetně složky saturace a míry světla/tmavosti obrázku?

A když bych potřeboval podle té výsledné zprůměrované barvy kterou jsme dostali změnit všechny složky


aktuální kod
http://paste.ofcode.org/76z6KFnCL9r9cPwGjwcEsH
juriad
Profil
Návštěvník:
V tom je ten problém, já jsem zvolil nějakou definici průměrného odstínu, další složky jsem neřešil.
Jak definuješ průměrnou saturaci a světlost je jiná otázka; jelikož jsou tyto dvě složky lineární, lze snad pro ně použít obyčejný aritmetický průměr.

@@ -27,19 +27,15 @@ class color_math {
    
   public function average($colors ) {    
                $avg = array(0, 0);
+      $as = 0;
+      $av = 0;
       for ($n = 0; $n < count($colors); $n++) {
               $r = $colors[$n][0]; $g = $colors[$n][1]; $b = $colors[$n][2];
               $rgb = array($r, $g, $b);
               $hsv = Lux_Color::rgb2hsv($rgb);
               $avg[0] += ($hsv[1] + $hsv[2]) / 2 * $hsv[1] * cos($hsv[0] * 2 * M_PI);
               $avg[1] += ($hsv[1] + $hsv[2]) / 2 * $hsv[1] * sin($hsv[0] * 2 * M_PI);
+              $as += $hsv[1];
+              $av += $hsv[2];
       }
       var_dump($avg);
+      return array(self::getAngle($avg[0], $avg[1]), $as/count($colors), $av/count($colors));
-      return self::getAngle($avg[0], $avg[1]);
   }
 
   public function getAngle($x, $y) {

Jinak:
array(1,2,3) lze v PHP od verze 5.4 zapsat jako [1,2,3].
Návštěvník
Profil *
Skvělé. Konečně se dobýrám nějakých správných výsledků. Dík.

Takže mám tu průměrnou barvu, jaká je v obrázku na základě barev, ze kterých jsem počítal průměr.

<?PHP
include 'lux_class.php';

class color_math { 
  public function averageFromImage($im) {
      $w = imagesx($im); // image width
      $h = imagesy($im); // image height     
          $avg_angle = array(0, 0); $avg_sat = 0; $avg_light = 0;
      for ($x = 0; $x < $w; $x++) {
          for ($y = 0; $y < $h; $y++) {
              $rgb = imagecolorat($im, $x, $y);
              $r = ($rgb >> 16) & 0xFF;
              $g = ($rgb >> 8) & 0xFF;
              $b = $rgb & 0xFF;
              $rgb = array($r, $g, $b);
              $hsv = Lux_Color::rgb2hsv($rgb);   
              $avg_angle[0] += ($hsv[1] + $hsv[2]) / 2 * $hsv[1] * cos($hsv[0] * 2 * M_PI);
              $avg_angle[1] += ($hsv[1] + $hsv[2]) / 2 * $hsv[1] * sin($hsv[0] * 2 * M_PI);
                            $avg_sat += $hsv[1]; // saturace se mění lineárně
              $avg_light += $hsv[2]; // světlost se mění lineárně
          }
      }
            $avg_sat = $avg_sat / $n;
            $avg_light = $avg_light / $n; 
      return array('avgangle'=>self::getAngle($avg_angle[0], $avg_angle[1] ), 'avgsat'=>$avg_sat,'avglight'=>$avg_light ); 
  }
   
  public function average($colors ) {    
          $avg_angle = array(0, 0); $avg_sat = 0; $avg_light = 0;
      for ($n = 0; $n < count($colors); $n++) {
              $r = $colors[$n][0]; $g = $colors[$n][1]; $b = $colors[$n][2];
              $rgb = array($r, $g, $b);
              $hsv = Lux_Color::rgb2hsv($rgb);
              $avg_angle [0] += ($hsv[1] + $hsv[2]) / 2 * $hsv[1] * cos($hsv[0] * 2 * M_PI);
              $avg_angle [1] += ($hsv[1] + $hsv[2]) / 2 * $hsv[1] * sin($hsv[0] * 2 * M_PI);
                            $avg_sat += $hsv[1]; // saturace se mění lineárně
              $avg_light += $hsv[2]; // světlost se mění lineárně
      }
            $avg_sat = $avg_sat / $n;
            $avg_light = $avg_light / $n; 
      return array('avgangle'=>self::getAngle($avg_angle[0], $avg_angle[1] ), 'avgsat'=>$avg_sat,'avglight'=>$avg_light ); 
  }

  public function getAngle($x, $y) {
      if ($x >= 0) {
          if ($y >= 0) {
              return atan2($y, $x) / (2 * M_PI);
          } else {
              return self::getAngle(-$y, $x) + 0.75;
          }
      } else {
          if ($y >= 0) {
              return self::getAngle($y, -$x) + 0.25;
          } else {
              return self::getAngle(-$x, -$y) + 0.5;
          }
      }
  }
    
    /** arg1 - single dimension array, where 
             arg1[0] = string hex color 1 , hex1[1] = string hex color 2 , etc. **/
    public function HEXarr2RGBarr(&$HEXcolors)
    {
    $RGBcolors = array();
    foreach ( $HEXcolors as $HEXcolor):
                    $RGBcolors[] = Lux_Color::hex2rgb($HEXcolor);
    endforeach;
    return $RGBcolors;
    }

} // end of class 
 
$im = imagecreatefrompng("test.png");
$w = imagesx($im); // image width
$h = imagesy($im); // image height

$myColors = array( 'cc825f', 'f21a1a', '9b5042', '933f31', 'd3b8af', 'ff9393', 'd09e9e');
/** vrací avgangle, avgsat, avglight **/
$avg_color_info = color_math::average( color_math::HEXarr2RGBarr($myColors) );

/// $avarage_color_info_from_file = color_math::averageFromImage($im);
// var_dump($avarage_color_info_from_file);
 
$rgb = Lux_Color::hsv2rgb(array($avg_color_info['avgangle'], $avg_color_info['avgsat'], $avg_color_info['avglight'])); // array($avg_color[''], 1, 1)
var_dump($rgb);
$c = imagecolorallocate ($im, $rgb[0], $rgb[1], $rgb[2]);
var_dump($c);
imagefilledrectangle ($im, 0, 0, $w, $h, $c);
 
imagepng($im, 'result.png');
imagedestroy($im);
?>

Takže pokud to chápu správně, teď musím vytvořit smyčku, která bude procházet soubor pixel po pixelu, RGB originálního pixelu převedu na HSV, a přičtu k tomu rozdíl mezi HSV originálu a HSV zprůměrované hodnoty. Mám tam dát nějakou podmínku na posouzení větší / menší nebo to tam mám dát natvrdo?
juriad
Profil
Chceš vlastně funkci, která ti vypočítá novou barvu na základě:
- _o: originální barvy (pixelu),
- _a: průměrné barvy v obrázku (ta vypočítaná),
- _t: cílové barvy (barvy, ke které chceš celý obrázek posunout)
Výstup bude _n.

Ano, převedeš všechno na HSV. A pak:
# přičte rozdíl a pokud je mimo rozsah <0, 1), tak posune hodnotu do tohoto intervalu
h_n = h_o + (h_t - h_a); if (h_n >= 1) h_n--; if (h_n < 0) h_n++;
# přičte rozdíl a pokud je mimo rozsah <0, 1>, tak hodnotu ořízne (přesycená, podsycená barva)
s_n = s_o + (s_t - s_a); if (s_n > 1) s_n = 1; if (s_n < 0) s_n = 0;
# přičte rozdíl a pokud je mimo rozsah <0, 1>, tak hodnotu ořízne (přepaly, přejasy)
v_n = v_o + (v_t - v_a); if (v_n > 1) v_n = 1; if (v_n < 0) v_n = 0;
Návštěvník
Profil *
juriad:
Ještě k té teorii. Takže funkce getAngle vrací úhel na základě vstupních hodnot x,y, které odpovídají dvěma složkám barvy v HSV, tedy saturace a světlost. V hartéském systému souřadnic se to tedy zobrazuje jako saturaxe na ose x, světlost na ose y a výsledný bod, náš výsledek zakreslený v grafu by byl ten stupeň, čili odstín hue. A existuje někde na netu najaký obrázek, který by v tom kartézském systému souřadnic vykreslil přímo ty barvy v HSV/HSL? To by mě zajímalo, proč to nikdo zatím neudělal třeba na wiki na pochopení by to bylo klíčové.

Pochopil jsem to totiž tak, že se tam jakoby počítá se čtyřmi barvama jedna dole v záporu a opačná na druhé straně v kladných číslech. Ještě nemám úplně jistotu v tom jestli imaginární číslo může být dole, nahoře vpravo vlevo (-x nebo +x)?
juriad
Profil
Ne, během výpočtu průměrného odstínu se pohybujeme v rovině (kartézských souřadnicích). Pokud jsme ve středu, neznáme průměrný odstín.
Čím dále od středu se nacházíme, tím jistější si jsme, že směr (počátek -> bod) odpovídá příslušnému odstínu.

Při výpočtu si tedy dovolíme sejít z barevného kruhu a interpretujeme to jako jistotu, že nejbližší odstín na kruhu je tím dominantním.
Proto na konci výpočtu získáme obecný bod [x,y] v rovině. Cílem však byl odstín, odstín leží na kružnici kolem počátku a jednotlivé odstíny jsou definované úhlem v rozsahu 0 -- 360 stupňů.


V této rovině (levý obrázek) neexistuje žádná interpretace toho, co je sytost či světlost; reprezentuje pouze odstín. Na celém poloměru je identická barva, nijak se směrem od/ke středu neliší. Ve středu neexistuje žádná barva. Porovnej to s obrázkem napravo, který zohledňuje světlost.

Takže máme bod někde (kdekoli) v rovině a nás zajímá jaký úhel mu odpovídá. To počítá funkce getAngle()
Návštěvník
Profil *
Dotaz k hex2hsv mi vracel chybu že používám this when not in object context. Tak jsem tam dal self:: to bych ale měl zaměnit v celé třídě, ne? když ji používám staticky?
    public function hex2hsv($hex)
    {
///            return $this->rgb2hsv($this->hex2rgb($hex));
        return self::rgb2hsv(self::hex2rgb($hex));
    }



Už jsem se dobral k výsledku:

if ( $s && $s !=1  ) 
{
$replace = Lux_Color::hsv2rgb(array($h,$s,$v));
// here we use the new color, but the original alpha channel
$colorB = imagecolorallocatealpha($im, round($replace[0]), round($replace[1]), round($replace[2]), $c['alpha']);
imagesetpixel($im, $x, $y, $colorB);
}

A mám tam problém, že ten původní transparentní obrázek, kde je průsvitné pozadí (nahoře) vypadá dost výrazně jinak (obr. dole). Tmavě červená (cihlová) je správná barva, tu jsem zvolil jako cílovou. Ale vypadá to, že se kolem nevytovořila transparentní vrstva takže bílá se změnila na růžovou. Chtěl jsem černou, šedou a bílou neměnit. Ale ještě se tam zobrazili nějaké žluté pixely.
http://oi62.tinypic.com/2q833n9.jpg

PS: Firefox mi to zobrazil rozmazaně a ještě to neumím vytisknout do souboru. na konci metody mám toto:

    imageAlphaBlending($im, true);
    imageSaveAlpha($im, true);
    header('Content-Type: image/png');
    imagepng($im);
    imagepng($im, $file_copy);
    imagedestroy($im);
Návštěvník
Profil *
Už jsem to vyřešil podmínkou
if ($s <= 0.500323723558 )  // téměř bílá, šedá nebo černá
  $s = 0;
a druhou podmínkou
if ( $s ) ... replace color ...

Takže dík za pomoc


Jo, ještě jeden dotaz. Ty zuby v barvě #d3b8af by se taky měly vynechat. Mohl bych udělat podle kde zadám barvy, které se nemají zaměnit, ale v tomto případě zase hrozí, že tam budou další přechody blízko barvě #d3b8af např. tam jsou tmavší varianty k zubům: #c2988d a #c9a196 jak bych tedy mohl udělat nějaký výpočet na jakýsi fuzz či toleranci? Na zubech je 12-13°, saturace 12-17%, světlost 79-83%. Pak tam jsou ještě podobné barvy, ale ty mají odstín 0-3° takže tam problém nehrozí.
peta
Profil
Neni mi jasne ale, proc pouzivas HSV, kdyz novejsi je model HSL.
juriad
Profil
peta:
Protože je to jedno. Důležité je, že jedna složka je kruhová (hue) a zbylé dvě lineární.
Zvolil jsem HSV, protože jsem na něj zvyklý. Je však pravda, že HSL je na webu (CSS vlastnost color) preferovaná.
Změna barevného modelu v tomto případě je triviální. Nic podstatného se nezmění.

Návštěvník:
Odpovím, až budu mít čas, nejspíš zítra.
Návštěvník
Profil *
juriad, peta:
Jo, asi to pak změním na HSL, mě víc totiž vyhovuje to třetí písmeno kvůli tomu že to L tam znamená lightness což je více výstižnější než V jako Value. Ale kvůli tomu že juriad to psal v HSV jsem použil to co on.

Řešení podmínek tolerance už dělám. Rozhodl jsem se vytvořit tři "tokeny", které mohu libovolně předat v poli. Token je objekt vyvozený z tříd "_exceptions", kde každá třída vyhodnocuje určitý typ porovnávacích operací. Stručně řečeno první token porovnává barvy jestli mají některou z hodnot menší nebo rovno ..., druhý token porovnává jestli je barva větší nebo rovno, a třetí porovnává zda je v intervalu. Je to vlastně mnohem dokonalejší než fuzz, protože si to mohu přesně nastavit a nehrozí "překryv" barev. Docela mě překvapilo, že taková zdánlivě jednoduchá myšlenka (třída), zabere tolik času na zrealizování.
Návštěvník
Profil *
Teď jsem narazil na problém s převodem stupňů na hue

Jak jsem psal že mám nějaký rozsah hue ve stupních, tak jak to převést?

Já píšu

if (isset($h)): 
 // např. $h['ll'] = 12°  
 // $h['ul'] = 13°
$this->ll1 = (is_array($h)?$h['ll']:$h)*(1/360); 
$this->ul1 = (is_array($h)?$h['ul']:$h)*(1/360); 
endif; // hue

a dostávám chybu:

Unsupported operand types
« 1 2 »

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: