Cześć. Programuję program graficzny i poszukuję funkcji do edycji pikseli.
Jak na razie potrzebuję funkcji zmiana jasności oraz zmiana kontrastu, ale też poszukuję innych funkcji.
Chętnie też przejrzę różne źródła i książki:-)
Cześć. Programuję program graficzny i poszukuję funkcji do edycji pikseli.
Jak na razie potrzebuję funkcji zmiana jasności oraz zmiana kontrastu, ale też poszukuję innych funkcji.
Chętnie też przejrzę różne źródła i książki:-)
Zmianę jasności możesz uzyskać mnożąc równolegle wartości wszystkich składowych RGB przez jakiś współczynnik. Np. jeśli masz taką strukturę i funkcję:
struct Barwa {
unsigned char czer, ziel, nieb;
};
/* Nie testowane, może być jakiś drobny błąd. */
void jasnosc (struct Barwa *brw, struct Barwa *wynik, float czynnik) {
unsigned int iloczyn;
iloczyn = brw->czer * czynnik;
wynik->czer = iloczyn < 256 ? iloczyn : 255;
iloczyn = brw->ziel * czynnik;
wynik->ziel = iloczyn < 256 ? iloczyn : 255;
iloczyn = brw->nieb * czynnik;
wynik->nieb = iloczyn < 256 ? iloczyn : 255;
}
To wywołanie jasnosc (&mojpiksel, &mojpiksel, 2.0F); podwoi jasność (w miarę możliwości True Color) koloru przechowywanego w zmiennej struct Barwa mojpiksel;, a jasnosc (&zrodlowy, &docelowy, 0.75F); wpisze do zmiennej docelowy kolor ze zmiennej zrodlowy przyciemniony o 25%.
Jednorodne zwiększenie barw składowych to z pewnością proste podejście, ale prawdopodobnie lepsze rezultaty da podejście wywodzące się z cylindrycznych model barw (HSL i HSV), gdzie jasność oblicza się bardziej percepcyjnie, jako kombinację liniową kolorów. Tutaj modeli jest wiele, na przykład ten używany w HDR zakłada pomnożenie czerwieni przez 0,2627, zieleni przez 0,678, a niebieskiego przez brakujące do jedności 0,0593, oco zmieni pseudokod wyżej na:
struct Barwa {
unsigned char czer, ziel, nieb;
};
/* Nie testowane, może być jakiś drobny błąd. */
void jasnosc (struct Barwa *brw, struct Barwa *wynik, float czynnik) {
unsigned int iloczyn;
iloczyn = brw->czer * czynnik * 0.2627;
wynik->czer = iloczyn < 256 ? iloczyn : 255;
iloczyn = brw->ziel * czynnik * 0.678;
wynik->ziel = iloczyn < 256 ? iloczyn : 255;
iloczyn = brw->nieb * czynnik * 0.0593;
wynik->nieb = iloczyn < 256 ? iloczyn : 255;
}
Można poszukać też innych modeli, jak kogoś to ciekawi.
tBane napisał(a):
Chętnie też przejrzę różne źródła i książki:-)
to przejrzyj sobie źródła gimpa
https://github.com/GNOME/gimp
tBane napisał(a):
Cześć. Programuję program graficzny i poszukuję funkcji do edycji pikseli.
Jak na razie potrzebuję funkcji zmiana jasności oraz zmiana kontrastu, ale też poszukuję innych funkcji.
Nie rób tego, chyba że chwilowo dla ćwiczeń i zabawy. Ale napisanie czegoś nawet pod własne potrzeby co będziesz mógł użyć szybciej niż gotowego produktu zajmie ci lata, to dużo bardziej skomplikowany temat niż się może wydawać, zwłaszcza jeśli jesteś na etapie zmiany wartości rgb pojedynczego piksela.
Jak już chcesz się bawić to przede wszystkim poczytaj o różnych systemach przestrzeni kolorów sRGB, adobe rgb, korekcie gamma. Znajdź sobie funkcje zamieniającą rgb na hsl i na odwrót. W HSL łatwiej się miesza kolory. Poza tym nie przechowuj kolorów jako tylko 8 bitów wewnętrznie bo jakość obrazu szybko się pogorszy przez sumujące się błędy obliczeń przy nałożeniu wielu filtrów. Przy wczytaniu zamień to na obraz o większej precyzji, nałóż filtry i dopiero z powrotem to znormalizuj do 8 bitów na kolor.
Bardziej polecałbym ci bawienie się pixel shaderami - naucz się języka takiego jak GLSL, HLSL czy WGSL - też można się pobawić kolorami i łatwo uzyskiwać fajne efekty a przy okazji możesz się nauczyć czegoś przydatnego np w post processingu gier albo taki shader możesz nałożyć na grafiki w swojej aplikacji, w tym webowej albo nawet stworzyć i opublikować własny filtr do instagrama czy snapchata. Dużo ciekawsze i praktyczniejsze niż wynajdowanie koła od nowa, a zabawa podobna.
Problem w tym, że wartość każdej składowej RGB nie jest proporcjonalna do postrzeganej jasności koloru, podobnie jest też z obrazem monochromatycznym. Jak już wspomniano, oprócz różnych przestrzeni barw, trzeba też przeliczyć wartość na jasność i odwrotnie. Do amatorskich zabaw, w większości przypadków można posłużyć się standardem sRGB lub gamma 2.2. Dawniej potrzebowałem automatycznego przetworzenia, wymyśliłem swego czasu kompletną klasę, która to umożliwi.
Kod jest e C#, ale w C++ i Java będzie praktycznie tak samo:
public class ColorCalc
{
int[] ByteToValueLUT_gamma;
byte[] ValueToByteLUT_gamma;
int[] ByteToValueLUT_sRGB;
byte[] ValueToByteLUT_sRGB;
public ColorCalc()
{
ByteToValueLUT_gamma = new int[256];
ByteToValueLUT_sRGB = new int[256];
for (int i = 0; i < 256; i++)
{
double i_ = i;
i_ = i_ / 255.0;
i_ = Math.Pow(i_, 2.2);
i_ = i_ * 65535.0;
ByteToValueLUT_gamma[i] = (int)Math.Round(i_);
i_ = i;
i_ = i_ / 255.0;
if (i_ <= 0.04045)
{
i_ = i_ / 12.92;
}
else
{
i_ = Math.Pow((i_ + 0.055) / 1.055, 2.4);
}
i_ = i_ * 65535.0;
ByteToValueLUT_sRGB[i] = (int)Math.Round(i_);
}
ValueToByteLUT_gamma = new byte[65536];
ValueToByteLUT_sRGB = new byte[65536];
for (int i = 0; i < 65536; i++)
{
double i_ = i;
i_ = i_ / 65535.0;
i_ = Math.Pow(i_, 1.0 / 2.2);
i_ = i_ * 255.0;
ValueToByteLUT_gamma[i] = (byte)((int)Math.Round(i_));
i_ = i;
i_ = i_ / 65535.0;
if (i_ <= 0.0031308)
{
i_ = i_ * 12.92;
}
else
{
i_ = 1.055 * Math.Pow(i_, 1.0 / 2.4) - 0.055;
}
i_ = i_ * 255.0;
ValueToByteLUT_sRGB[i] = (byte)((int)Math.Round(i_));
}
}
bool sRGB = true;
public int ByteToValue(byte B)
{
if (sRGB)
{
return ByteToValueLUT_sRGB[B];
}
else
{
return ByteToValueLUT_gamma[B];
}
}
public byte ValueToByte(int V)
{
if (V < 0)
{
return 0;
}
if (V > 65535)
{
return 255;
}
if (sRGB)
{
return ValueToByteLUT_sRGB[V];
}
else
{
return ValueToByteLUT_gamma[V];
}
}
public void Test()
{
for (int i = 0; i < 256; i++)
{
sRGB = false;
int T = ByteToValue((byte)i);
int TT = ValueToByte(T);
Console.Write(i);
Console.Write("\t");
Console.Write(T);
Console.Write("\t");
Console.Write(TT);
sRGB = true;
T = ByteToValue((byte)i);
TT = ValueToByte(T);
Console.Write("\t");
Console.Write(T);
Console.Write("\t");
Console.Write(TT);
Console.WriteLine();
}
}
}
Zmieniając linię bool sRGB = true można zmienić działanie klasy. Funkcja ByteToValue zamienia bajt składowej RGB na wartość jasności z zakresu od 0 do 65535. W tej przestrzeni, wartość 32768 to jest faktycznie jasność piksela w połowie. Funkcja ValueToByte zamienia w drugą stronę.
Jasność i kontrast to są akurat najprostsze algorytmy:
W obu przypadkach, jeżeli wynik wyjdzie poza skrajną wartość, np. z zakresu od 0 do 65535, należy skorygować do skrajnej wartości.
Praca bezpośrednio na wartościach RGB, tych od 0 do 255 może przynieść mierne rezultaty właśnie z powodu braku proporcjonalności między wartością liczbową, a postrzeganą jasnością.
@pradoslaw pracował ostatnio nad programem graficznym: https://4programmers.net/Forum/AI/375595-o3_mini_high_prototyp_ms_paint_gotowy_w_19s
Może podrzuciłby tu jakieś algorytmy...