Return Styles: Pseud0ch, Terminal, Valhalla, NES, Geocities, Blue Moon. Entire thread

Hue Shift

Name: Anonymous 2019-07-19 9:30

Ok. The HSV formula for transferring RGB triplet into the HSV form is widely published. But I've failed to find any hints to how one arrives at it, so I could try to optimize it for my game's blitter routine. Therefore I tried to infer h,s,v formula myself, using basic vector math.

v = (r+g+b)/3 //value
hs = v-r,v-g,v-b //hue*saturation vector
s = hs.length //saturation
h = angle(hs/s) //hue is the angle of the vector


Guess when one needs to just change the saturation, there is no reason compute the angle or even vector length in full, but the common requirement to change the hue requires nasty computation. Can anyone hint at any efficient way of shifting hue?

Name: Anonymous 2019-07-19 10:00

I just took a really hue shift

Name: Anonymous 2019-07-19 10:46

>>2
hot

Name: Anonymous 2019-07-19 15:55

Note that in the hs/s vector the sum of any two elements is always equal to the third element, and its length is always less than 1, so it can be safely reduced to a single angle value.

Name: Anonymous 2019-07-19 18:31

Ok. Tried to complete the implementation of that h = angle(hs/s)

And found that I can't really represent hs/s as an angle, but I can still represent it as a single value by solving quadratic equation. I also had to solve the issue with v, doing hs=(v-r,v-g,v-b)/v instead, to normalize the shit. Now it is better than HSV, because it handles saturation properly. But the problem is: it is not HSV or HSL, because it still works inside the RGB cube.

Now the colormap looks like a strange irregular nonsense: http://lj.rossia.org/users/sadkov/457191.html

Still needs further research on how its hue part works, because I noticed that quadratic equation solely by accident.

Name: Anonymous 2019-07-19 18:40

Here is the full code, in case anyone can help with it.
qsolve B C =
| D = B*B - 4.0*C
| when D < 0.0: leave 0
| D = D.sqrt
| S0 = (D - B)/2.0
| S1 = (-D - B)/2.0
| S0,S1

dergb RGB =
| R = 0
| G = 0
| B = 0
| unrgb RGB R G B
| R = R.float/255.0
| G = G.float/255.0
| B = B.float/255.0
| V = (R+G+B)/3.0
| HS = [R-V G-V B-V]/V
| S = HS.abs
| H = HS/S
| Hue = H.0
| D = 0
| A = H.0
| Bs = qsolve A (A*A - 0.5)
| when Bs:
| B0,B1 = Bs
| B = H.1
| D <= if (B-B0).abs < (B-B1).abs then 1 else 2
| Hue,S,V,D

enrgb HSV =
| Hue,S,V,D = HSV
| A = Hue
| B = 0
| if D then
| Bs = qsolve A (A*A - 0.5)
| less Bs: leave rgb{ 0 0}
| B <= if D><1 then Bs.0 else Bs.1
else
| B <= -(A*0.5)
| C = -(A+B)
| H = [A B C]
| HS = H*S
| R,G,B = (HS*V + [V V V])*255.0
| rgb R.int G.int B.int

Name: Anonymous 2019-07-19 20:26

Warning: you're posting in a Nikita thread.

Name: Anonymous 2019-07-20 7:38

Ok. Dropped this shit. It solved the brightness-saturation clash problem, but I found that at higher brightness values it gets non-uniform. Beyond my current grasp in math to analyze further:
https://www.youtube.com/watch?v=qMgVFyn7M14

Found another format - Lab,
https://github.com/gka/chroma.js/blob/master/src/io/lab/lab2rgb.js

Name: Anonymous 2019-07-20 7:42

>>8
In addition it required at least 16 bits to hold the hue. But this Lab also requires 16 bits, so I cant used 32bit array to hold values, or have to sacrifice the alpha channel :(

Name: Anonymous 2019-07-20 7:51

>>7
Sage this post as untrue

Name: Anonymous 2019-07-20 12:10

>>10
Get mad, hamsterfucker.

Name: Anonymous 2019-07-20 15:33

>>7
Saging as fake new

Name: Anonymous 2019-07-20 16:19

>>12
Log off, GRU boy.

Name: Anonymous 2019-07-22 8:01

Ok. I designed a custom color space: https://www.youtube.com/watch?v=9JjjQEu9XHY

Name: Anonymous 2019-07-23 16:24

I found a nice video:

https://www.youtube.com/watch?v=82ItpxqPP4I

basically they call that crap XYZ plane, due to the property of X+Y+Z = 1.0. I botched the calculation, because I have no experience with linear algebra and plane equation. But well, I've admitted that my math skills are near zero and I prefer doing hacks, than solving the shit analytically.

Name: Anonymous 2019-07-24 10:40

r is 0, g is 120, b is 240 with the usual units
0, 80, 160 would fit in a byte

you should be able to change both saturation and value without changing hue
it might do some funny things like 0.0 sat, 1.0 val is #ffffff

Name: Anonymous 2019-07-24 11:34

I think val might be max(r,g,b)/255, sat = max(rgb) - min(rgb),
and then hue ~= mid-min / max-min

Name: Anonymous 2019-07-24 11:44

~hue = 0 is a solid r/g/b, or 0 degrees, ~hue = 1 is yellow/cyan, or +/- 45's

Name: Anonymous 2019-07-24 12:57

>>16
That is the problem with usual HSV - its saturation has interference with its value. Therefore HSV is considered bad choice even for modern video gaming graphics.

Name: Anonymous 2019-07-24 17:01

( r + g + b ) / 3

Name: Anonymous 2019-07-24 21:54

(r * 0.299f + g * 0.587f + b * 0.114f)

Name: Anonymous 2019-07-24 21:57

make blacker nigger

Name: Anonymous 2019-07-25 4:11

>>21
you could use this to make it keep it's greyscale value constant while changing hues

Name: Anonymous 2019-07-25 4:38

https://en.wikipedia.org/wiki/HSL_and_HSV#/media/File:HSV-RGB-comparison.svg
shows the linearity, it's not doing vector rotation

Hue is more of a mock-angle, the rgb vector length will vary as the hue is changed

Name: Anonymous 2019-07-25 7:42

>>24
Well, I actually used cosine to interpolate, because I disliked these pronounced magenta, cyan and yellow lines. And used proper weighted gamma (instead of (r + g + b)/3) to avoid botching brightness.

Name: Anonymous 2019-07-25 9:11

>>22
Tsk.

Name: Anonymous 2019-07-25 16:17

Here is the final encoder routine, converting the usual RGB into my version of HSL. Had to use int64_t, because of overflow in RGB_METRIC:
#define RGB_METRIC(dist, item, rr, gg, bb) \
do { \
int64_t x = (int64_t)(item)->r - rr; \
int64_t y = (int64_t)(item)->g - gg; \
int64_t z = (int64_t)(item)->b - bb; \
dist = x*x + y*y + z*z; \
} while (0)

uint32_t rgb2hsl(uint32_t rgb) {
uint32_t lm;
uint32_t hs;
uint32_t r = rgb>>16; //no div by 255, cuz luma() gives us L*255
uint32_t g = (rgb>>8)&0xff;
uint32_t b = rgb&0xff;
uint32_t rgb16 = R5G6B5(r,g,b);
uint32_t l = LUMA8(r,g,b);
lut_item_t *item = rgb_to_hs_lut + rgb16*3;
lut_item_t *best_item = item;
uint64_t dist,best_dist;
item += 1;
if (!item->hs) return (l<<16) | best_item->hs;
lm = inv8[l];
r = (r*lm)<<6;
g = (g*lm)<<6;
b = (b*lm)<<6;
RGB_METRIC(best_dist, item, r, g, b);
item += 1;
RGB_METRIC(dist, item, r, g, b);
if (dist < best_dist) {
best_dist = dist;
best_item = item;
}
item += 1;
if (!item->hs) return (l<<16) | best_item->hs;
RGB_METRIC(dist, item, r, g, b);
if (dist < best_dist) {
best_dist = dist;
best_item = item;
}
return (l<<16) | best_item->hs;
}

Name: Anonymous 2019-07-25 16:51

Why do you need to implement your own hueshift op? There are multiple good options available.

Name: Anonymous 2019-07-25 17:29

>>28
All options are some variation of usual HSV, or this academic Lab, which gives you useless a and b params.

Name: Anonymous 2019-07-25 17:58

Name: Anonymous 2019-07-25 20:04

>>30
Russian algorithm, no thank you!

Name: Anonymous 2019-07-26 1:42

>>25
I guess the trouble with using hue for gradients is you always get the colour cycling /rainbow type effect, as it separates out the rgb channel fades

It's probably good for storing a colour palette, beats trying to cycle through rgb values in code, and it'll even do a slight compression for groups of S/V/L

Name: Anonymous 2019-07-26 6:52

shift my dubs

Name: Anonymous 2019-07-26 7:25

Name: Anonymous 2019-07-26 7:29

>>32
You can still mix colors in HSV:
http://lj.rossia.org/users/sadkov/462113.html

although not the same as in rgb, because (Color1.h+Color2.h)/2 doesn't work with the spectral wheel. I.e. using this format makes mixing colors more expensive.

Name: Anonymous 2019-07-31 10:22

TLDR: I came with different color space, and now need a fast integer sqrt for gamma crunching.

Name: Anonymous 2019-07-31 10:23

>>36
TLDR: I came

Name: Anonymous 2019-07-31 10:32

>>36
everything depends on how much accuracy you want to sacrifice for speed, how fast you really need to be and do you have other requirements like memory use. iterative methods based on Newton's formula are a classic, and you can set max number of iterations to control the tradeoff. if this isn't enough, pre-computed lookup tables can help. here's a solution using those made by some stackoverflowgrammer: https://stackoverflow.com/a/1100591

Name: Anonymous 2019-07-31 12:07

>>38
I would have gone with just blending raw RGB, ignoring there gamma nature, but people say that is incorrect:
If it helps, you can think of sRGB as being an opaque compression format. You wouldn’t try to add two ZIP files together, and you wouldn’t try to multiply a CRC32 result by 2 and expect to get something useful, so don’t do it with sRGB! The fact that you can get something kinda reasonable out is a red herring, and will lead you down the path of pain and deep deep bugs. Before doing any maths, you have to “decompress” from sRGB to linear, do the maths, and then “recompress” back.

Name: Anonymous 2019-07-31 16:18

Update to: http://lj.rossia.org/users/sadkov/467130.html

Checked my sqrt against the log2 based sqrt, using clang's __builtin_clz (which should expand to single assembly opcode), and the library's sqrtf, called using (int)sqrtf((float)i):
#define CLZ(x) __builtin_clz(x)
uint32_t clz_sqrt(uint32_t value) {
if (!value) return 0;
uint32_t xn = 1 << ((32 - CLZ(value))/2);
xn = (xn + value/xn)/2;
xn = (xn + value/xn)/2;
xn = (xn + value/xn)/2;
return xn;
}



got rather strange results:
$ gcc -O3 test.c -o test && ./test
isqrt16: 6.498955
sqrtf: 6.981861
log2_sqrt: 61.755873



Clang provided CPU based sqrtss, which is nearly as fast as my one. Lesson learned: on x86 compiler can provide fast enough sqrt, which is less than %10 slower than what you can come with up yourself, wasting a lot of time, or can be 10 times faster, if you use some ugly bitwise hacks. And still sqrtss is a bit slower than custom function, so if you really need these 5%, you can get them. Yet ARM for example has no sqrtss, so log2_sqrt shouldn't lag that bad.

Newer Posts
Don't change these.
Name: Email:
Entire Thread Thread List