Logo by Trifox - Contribute your own Logo!

END OF AN ERA, FRACTALFORUMS.COM IS CONTINUED ON FRACTALFORUMS.ORG

it was a great time but no longer maintainable by c.Kleinhuis contact him for any data retrieval,
thanks and see you perhaps in 10 years again

this forum will stay online for reference
News: Did you know ? you can use LaTex inside Postings on fractalforums.com!
 
*
Welcome, Guest. Please login or register. March 29, 2024, 02:52:23 PM


Login with username, password and session length


The All New FractalForums is now in Public Beta Testing! Visit FractalForums.org and check it out!


Pages: 1 [2] 3   Go Down
  Print  
Share this topic on DiggShare this topic on FacebookShare this topic on GoogleShare this topic on RedditShare this topic on StumbleUponShare this topic on Twitter
Author Topic: What colour is a Koch curve?  (Read 3503 times)
0 Members and 1 Guest are viewing this topic.
kram1032
Fractal Senior
******
Posts: 1863


« Reply #15 on: March 16, 2017, 07:06:06 PM »

I found my error! Two errors, in fact. What I get now does indeed look like it should. Conversion from spectrum to color seems to work too, pretty much. The only thing I'm still not entirely sure about is how to properly normalize the color I get. If I simply divide by the number of samples taken I get some slightly colored grey rather than white. From what I gathered I could also normalize it such that the Y value ends up at 1 which, if I understood right, means the color I get will be at the maximum brightness of the color system (though I might still get some hue since it isn't "perfect white")

Perhaps this is usable as is to you either way. It's fairly straight forward code. If you need me to explain anything, just ask away. I'd love to see full-spectrum Koch gears.

Code:
from math import exp, floor, ceil
import random
import numpy as np
from matplotlib import pyplot as plt
import csv


# a bunch of utility functions to get to proper units


def to_eV(v):
    return v * 4.135668


def to_nm(v):
    return 299.8/v

def from_nm(v):
    return 299.8/v

def from_K(T):
    return T * 1.e-4


def from_THz(v):
    return v * 1.e-3

C = 77.766  # prefactor such that peak is 1
K = 4.79924  # prefactor if units are in PHz and 10^4 K
# desired range for v is .4 to .789
# desired range for T is 7.98e-2 to 2.
# typical range would be .5 to .7, commonly .65


def Planck(v, T):  # the distribution of spectral frequencies for a given temperature
    vT = v / T
    return C * vT * vT * vT / (exp(K * vT) - 1)

topPHz = from_nm(390)
botPHz = from_nm(830)


def MC(T):  # generates samples from the distribution using Markov Chain Monte Carlo method
    v0 = random.uniform(botPHz, topPHz)
    I0 = Planck(v0, T)
    while True:
        v = random.uniform(botPHz, topPHz)
        I = Planck(v, T)
        a = I/I0
        if a >= 1:
            v0 = v
            I0 = I
        else:
            p = random.random()
            if p < a:
                v0 = v
                I0 = I
        yield v0

# example histogram for some temperature

samples = 1000000
temp = 5777  # in Kelvin

data = [to_nm(v) for v, _ in zip(MC(from_K(temp)), range(samples))]  # in nanometers, from 100k samples.

specxyz = []
with open('lin2012xyz10e_fine_7sf.csv', newline='') as inputfile:
    for row in csv.reader(inputfile):
        specxyz.append(row)

col = (0, 0, 0)

r, g, b = 0., 0., 0.
for d in data:
    x = d*10 - 3900
    i = floor(x)
    y = x - i
    vals = zip(specxyz[i], specxyz[i + 1])
    ivals = []
    for v in vals:
        ivals.append(float(v[0])*(1-y) + float(v[1])*y)
    r += ivals[1]
    g += ivals[2]
    b += ivals[3]

t = r + g + b
r /= g

b /= g
g = 1

def xyztorgb(x, y, z):
    r = 3.2404542 * x - 1.5371385 * y - 0.4985314 * z
    g = -0.969266 * x + 1.8760108 * y + 1.8760108 * z
    b = 0.0556434 * x - 0.2040259 * y + 1.0572252 * z
    return r, g, b

print('XYZ')
print(r)
print(g)
print(b)
print()
r, g, b = xyztorgb(r, g, b)

r = int(min(1, max(0, r)) * 255)
g = int(min(1, max(0, g)) * 255)
b = int(min(1, max(0, b)) * 255)

print('RGB')
print(r)
print(g)
print(b)
print()

binbot = ceil(min(data))
bintop = floor(max(data))
bincount = int(bintop-binbot)
bins = np.linspace(binbot,
                   bintop,
                   bincount)

plt.xlim([min(data), max(data)])

plt.hist(data, bins=bins, alpha=0.5)
plt.title('spectral distribution of ' + repr(temp) + 'K light source')
plt.xlabel('nm')
plt.ylabel('count')

plt.show()

It will output a plot of the sampled spectrum's histogram as well as the corresponding XYZ and RGB values (using a XYZ-to-RGB matrix I found somewhere)

The file "lin2012xyz10e_fine_7sf.csv" is just a csv I got from here: http://cvrl.ioo.ucl.ac.uk/
In particular, and this might be a slight error, not quite sure, I used
"10-deg XYZ CMFs transformed from the CIE (2006) 2-deg LMS cone fundamentals"
in
"New CIE XYZ (from LMS) functions (proposed)"

I picked (and in this code it's hardcoded) the 0.1nm option as a csv file.

What might be problematic about that is the "proposed": It's a new standard that's apparently not yet in common use. Presumably it's more accurate than the old version, however it might actually need a slightly different matrix to convert from XYZ to sRGB. Not sure.

And I chose the variant where I normalize my XYZ such that Y = 1 which might not actually be what you'd want to do.

My output for a 5777K lightsource ended up being
Code:
XYZ
0.9739410242962122
1
0.9990353465096308

RGB
255
255
231



Which agrees really well with an actual visible portion of a black body spectrum of this temperature.

EDIT:

So the way this works would be, you'd use spectral data to calculate the color of your gear (use lots of samples. The more samples, the closer to actual white your light source will be). You don't actually need to store the whole spectrum: Just convert each final ray as it hits the camera into XYZ colors using the lookup table from the page I linked above. In the XYZ color space, add everything up. If I understood right, it's conveniently linear, so adding up in spectral space and then converting to XYZ is the same as converting to XYZ and then adding up in that space.
Then, once your render is finished, first find your maximum Y value and normalize the entire image by that. That will mean that no point is actually brighter than this. If you are concerned with saturation-clipping, it might help to actually make the brightest point darker, so normalize it such that the maximum Y is only like .8 or something. Then, convert it all over to your desired color space, for instance, for screens, the sRGB color space. That then is the final image.

Oh and the way I set it up in the python script above, I actually interpolate wavelengths linearly to get an approximate color for wavelengths that aren't exactly .1nm. Presumably, at that resolution, you could also just round them. Would probably work just as well.
« Last Edit: March 16, 2017, 08:47:33 PM by kram1032 » Logged
lycium
Fractal Supremo
*****
Posts: 1158



WWW
« Reply #16 on: March 16, 2017, 08:54:35 PM »

Hi Mark, cool to see you doing some spectral ray tracing, deja vu? cheesy

I don't see why you're using importance sampling for the wavelengths, and via a massively overpowered method like MCMC; there isn't even that much variance, and the visible spectrum is rather "small" 1D domain. I'd recommend simply using eg. 8 wavelengths at once, uniformly spaced with a random offset in range [0, 1/8).

I'd be interested to look at and mess around with the code for Koch curve rendering when it gets released; I'm good at ray tracing / path tracing, but don't understand how the surface reflection model works, probably due to my very limited knowledge of quantum mechanical effects in light reflection.
Logged

kram1032
Fractal Senior
******
Posts: 1863


« Reply #17 on: March 16, 2017, 11:54:20 PM »

I'm not sure how accurate it'd need to be - how much of a visual difference it'd make. Maybe 8 samples are enough. - Heck, maybe the full spectral result wouldn't even look so different from the already shown 3 sample take. But I think it should be tried at least once, just to see if the difference is worth it.
That the spectrum is fairly uniform kind of is the point though: It's supposed to be white light after all.

I'd also like to see the code. I have a rough idea how it works. If I got what you said right, Tglad, you're currently actually using the HSV colorspace to get a proxy conversion from "wavelengths" (actually hues) to RGB?
Logged
Tglad
Fractal Molossus
**
Posts: 703


WWW
« Reply #18 on: March 17, 2017, 12:33:41 PM »

here is the code: https://github.com/TGlad/FractalColour2D, using 10 frequency samples by default. It will make 60 .bmp files in a folder called kochs. I just use http://gifcreator.me/ to convert it to a gif.
Logged
lycium
Fractal Supremo
*****
Posts: 1158



WWW
« Reply #19 on: March 17, 2017, 02:07:16 PM »

Just in time for the weekend smiley Thanks for sharing, Tglad!

Edit: zomg the code is so complex  shocked
Logged

claude
Fractal Bachius
*
Posts: 563



WWW
« Reply #20 on: March 17, 2017, 05:30:09 PM »

EDITED to say, this code is buggy (should consider 2x the distance to the surface in the interference calculations), will post corrected version soon

I had a go using distance estimation ray marching, see attached image and C99 source code.  Took 35mins to render the image on my quad core desktop...

The image shows the Koch curve horizontally, and each scanline is at a different scale (with a factor of 3 between the top row and the bottom row, interpolated exponentially).

Light is parallel to ray direction, heading vertically down towards the curve in its traditional orientation.


* koch-color.png (125.9 KB, 256x256 - viewed 210 times.)
* koch-color.c.txt (3.96 KB - downloaded 70 times.)
* lin2012xyz2e_1_7sf.h.txt (23.03 KB - downloaded 68 times.)
« Last Edit: March 19, 2017, 05:40:07 PM by claude, Reason: bugs » Logged
kram1032
Fractal Senior
******
Posts: 1863


« Reply #21 on: March 17, 2017, 11:02:35 PM »

so this one has multiple reflections and thus is much brighter? Or how does that work? It looks pretty.
At that render time I don't suppose doing a rotating version will be feasible?
Logged
claude
Fractal Bachius
*
Posts: 563



WWW
« Reply #22 on: March 18, 2017, 02:07:42 PM »

so this one has multiple reflections and thus is much brighter? Or how does that work? It looks pretty.
At that render time I don't suppose doing a rotating version will be feasible?

No just one hit point per ray, I think it was 32 rays per pixel, accumulated over a 10-pixel region.  It might be more saturated because the rays are equally spaced instead of randomly sampled?  Not sure.

Some things could be tuned to reduce render time, like number of wavelengths, number of rays per pixel, number of ray steps, distance estimator recursion depth.  Some things are totally unoptimized - it traces the rays for each wavelength, when it could cache the distances and just redo the phasor additions...
Logged
claude
Fractal Bachius
*
Posts: 563



WWW
« Reply #23 on: March 19, 2017, 09:06:44 PM »

Here's some fixed code that renders one scale, rotating through 60 degrees.  1024 rays per pixel equally spaced over 4 pixels with a raised cosine magnitude window.

It took so long to render the first 30deg (45mins) that I did the second half by rotating the first half in GIMP instead, should have thought about the symmetry before launching the code.


* koch-colour-rotating.jpg (238.99 KB, 512x512 - viewed 129 times.)
* koch-color.c.txt (5.23 KB - downloaded 70 times.)
Logged
Tglad
Fractal Molossus
**
Posts: 703


WWW
« Reply #24 on: March 20, 2017, 02:59:03 AM »

It's really good to see an independent confirmation of the overall effect that the colour flips every 30 degrees, I think that those intermediate colours (the oranges) would blend out if you did it under ambient light, rather than your single front-facing light. The brighter image is just a stronger incident light compared to mine (roughly 4th one down):

You also seem to be scaling the wavelength of light (vertically) rather than the actual size of the fractal... so there's a slight difference between ours in the scaling of the spot size. I'm also only using 3 spectrum samples (r,g,b) in that image.

A nice thing about using the DE approach is that it is simple and could migrate to 3D fractals, but an advantage of my more complicated code is that it is super-quick for 2D curves, takes about 20s per image (which uses 20 incident light angles, and 10 spectrum samples), and I haven't tried openMP yet.

Kram, I haven't tried multiple bounces, but it seems to me that if the reflectivity was 1 then all colour would be lost, so higher reflectivity probably just reduces colouration... but that's just an educated guess.
Logged
Tglad
Fractal Molossus
**
Posts: 703


WWW
« Reply #25 on: March 22, 2017, 11:01:57 AM »

I have tidied up the description: https://sites.google.com/site/tomloweprojects/scale-symmetry/what-colour-are-fractals
and included a gif for the levy curve and dragon curve:



The colour sensitivity functions I was using weren't working so I reverted to a much simpler 7 frequency samples. I doubt more samples makes much difference as it isn't spikey in frequency.

Each of these similar curves gives a different colour behaviour.
Logged
kram1032
Fractal Senior
******
Posts: 1863


« Reply #26 on: March 22, 2017, 11:49:11 AM »

the lower one is the dragon curve? Looking great!
Logged
Tglad
Fractal Molossus
**
Posts: 703


WWW
« Reply #27 on: March 26, 2017, 06:39:51 AM »

Yes, that's right. Shows much more colour.
Logged
claude
Fractal Bachius
*
Posts: 563



WWW
« Reply #28 on: March 29, 2017, 11:31:42 PM »

Here's one rotating:



32 rays per pixel, positions randomized slightly.

I found that the magic number (6e-5 in the attached) to match wavelength to structure size is crucial, too small and colours are very saturated but noisy/grainy, too large and it blends to white (relatively large spot size).  I guess measuring everything in terms of wavelengths would be one way to deal with this properly.

EDITED to add: 10mins render time total for 60 frames at 128x128, previous images were so slow due to needing many more rays to reduce noise with small scaling (magic number), and rendered at larger resolutions

* koch-color.c.txt (5.22 KB - downloaded 64 times.)
* lin2012xyz2e_1_7sf.h.txt (23.03 KB - downloaded 57 times.)
Logged
kram1032
Fractal Senior
******
Posts: 1863


« Reply #29 on: March 29, 2017, 11:43:44 PM »

neat! what kind of scaling are you using here? It looks like you're reducing the size by a factor of 3 as you go down, but it isn't linear rescaling. Is it exponential?
Logged
Pages: 1 [2] 3   Go Down
  Print  
 
Jump to:  

Related Topics
Subject Started by Replies Views Last post
Koch curve spherical mutation Images Showcase (Rate My Fractal) doncasteel8587 2 1772 Last post February 18, 2007, 02:01:14 AM
by lycium
Lévy C curve Help & Support barns2 0 591 Last post June 04, 2011, 11:08:55 PM
by barns2
Curve Images Showcase (Rate My Fractal) Dinkydau 5 1066 Last post May 29, 2013, 01:47:00 PM
by Kalles Fraktaler
On the Curve Fragmentarium Gallery Tim Emit 5 1127 Last post December 16, 2016, 03:37:26 AM
by 3dickulus
Hidden Treasure Of Non-Integer Power Mandelbrots - Koch Curve Images Showcase (Rate My Fractal) greentexas 0 1241 Last post June 13, 2017, 10:15:10 PM
by greentexas

Powered by MySQL Powered by PHP Powered by SMF 1.1.21 | SMF © 2015, Simple Machines

Valid XHTML 1.0! Valid CSS! Dilber MC Theme by HarzeM
Page created in 0.929 seconds with 24 queries. (Pretty URLs adds 0.055s, 2q)