lewisje All American 9196 Posts user info edit post |
I've looked for a while, but I wonder what the best way is to make a 2-dimensional rectangular gradient using a given color at each corner and interpolating the colors in the rest of the rectangle in the desired color space (like RGB, HSV, LAB, LUV, or HCL); I mean you see images like this...
...all the time, but I haven't found anything more workable than simply making gradients for two of the edges, and then based on the color values at each point of each edge, making gradients across the rectangle. (I've tried some questionable R code I found on StackOverflow, and a GIMP script called "2D Gradients," and an old "custom wallpaper" app that only did RGB interpolation, but I haven't found anything that easily makes images like the one above.)
Maybe chroma.js is a better idea: http://vis4.net/blog/posts/avoid-equidistant-hsv-colors/ I'm also going to see whether Processing will work better: http://hclcolor.com/ It's too bad that Inkscape doesn't implement "diffusion curves" because that more general idea would work too: http://wiki.inkscape.org/wiki/index.php/Diffusion_Curves
BTW that image was used in a Wikipedia article about why color TV signals can be understood by the ol' black-and-white: https://en.wikipedia.org/wiki/YUV 3/9/2013 1:36:33 AM |
darkone (\/) (;,,,;) (\/) 11610 Posts user info edit post |
If you can interpolate in one dimension to make the edge gradient, what's keeping you from handling the 2D interpolation? This is a pretty easy task assuming you know how the color spaces work. 3/9/2013 8:19:38 AM |
lewisje All American 9196 Posts user info edit post |
I was hoping there would be some way to make a gradient involving four colors at the corners of a rectangle that was as easy as the ways offered by typical image-editing programs to make gradients involving just two colors, like something I hadn't found yet, rather than something I'd have to whip up myself. 3/9/2013 12:10:16 PM |
dtownral Suspended 26632 Posts user info edit post |
This is the second strange color gradient post you've made, its weird 3/9/2013 2:23:41 PM |
sarijoul All American 14208 Posts user info edit post |
^^you're basically making two linear two-color gradients. Then you're making multiple linear two color gradients connecting those two lines. 3/9/2013 4:41:24 PM |
lewisje All American 9196 Posts user info edit post |
^I already said that in the OP, and I was hoping someone had automated the process of making all those gradients.
[Edited on March 9, 2013 at 6:19 PM. Reason : ^^The "first one" you're thinking of was not about gradients but about individual colors. 3/9/2013 6:18:33 PM |
Noen All American 31346 Posts user info edit post |
what kind of "automation" are you looking for? You could easily write a Photoshop script to do it. 3/9/2013 9:02:46 PM |
lewisje All American 9196 Posts user info edit post |
The kind of automation is an interface in which you just specify the colors at the four corners and the dimensions of the rectangle and out pops the gradient; I know I could code such a thing (I'm actually thinking about doing it with the HTML5 canvas) but wondered whether it had already been done. 3/9/2013 9:11:25 PM |
lewisje All American 9196 Posts user info edit post |
k I tried using canvas and couldn't even get it to work for some reason...I might get back to that and even try out dat chroma.js: http://vis4.net/labs/185
but I did figure out how to do almost what I wanted in R, using the following code to make the image below (the inverse of the image above, flipped so that lighter shades are still at the top)... color.bar <- function(tpal,bpal,min,wid=100,hgt=wid,max=-min){ tlut=tpal(wid) blut=bpal(wid) scale = (hgt-1)/(max-min) dev.new(width=wid,height=hgt) plot(c(0,wid),c(min,max),type='n',bty='n',xaxt='n',xlab='',yaxt='n',ylab='') for(j in 1:wid){ lut=colorRampPalette(c(tlut[j],blut[j]),space="Lab")(hgt) for(k in 1:(hgt-1)){ y=(k-1)/scale+min rect(j-1,y,j,y+1/scale,col=lut[k],border=NA) } } } tpal=colorRampPalette(c("#AAFFF9","#BFC6FF"),space="Lab") bpal=colorRampPalette(c("#004055","#090040"),space="Lab") color.bar(tpal,bpal,1,200) Unfortunately, the colorRampPalette function only allows the use of CIELAB ("Lab") or RGB ("rgb") for interpolation, while it would be more convenient to interpolate over hue or chroma, as in HSV, HSL, or even better, HCL, which is why I might try out Chroma.js
The colors specified at the code are near the extremes of what one could call "blue"; the first image in the post, with a perceptibly wider range of colors, is therefore called the "Opposite of Blue" and it would be better if all of the colors in the image had maximum chroma (or lay on the "surface" of the RGB cube) the way the colors I chose do, and I think interpolating over a hue-indexed space might allow me to do that (in particular, the "Opposite of Blue" image would look more orange than brown in the middle).
[Edited on March 10, 2013 at 3:45 AM. Reason : the code was sad3/10/2013 3:43:57 AM |
moron All American 34141 Posts user info edit post |
I'm still confused what you're looking for...
are you asking for an algorithm, an application, or a website?
What size images do you want? Is this for an on-going purpose? Is it a one-time thing? Is it curiosity? You don't appear to care about language, but you don't want to have to use an image editing app either... 3/11/2013 1:09:03 AM |
lewisje All American 9196 Posts user info edit post |
I'm asking for a way, as easily as possible, to do something like this, whether it's already done on some website, packaged into a (preferably free) application, or described by an algorithm (which I could probably implement but would prefer not to have to), and it is indeed a curiosity.
It's not an ongoing thing, and ideally it would create images of an arbitrary size; I don't get where you thought I "don't want to have to use an image editing app either" because I would actually love to see such a thing implemented in an image editor (I even tried a script in The GIMP that usually either gave me random rainbows or solid blocks of color).
I think I'll further modify this R code I came up with (heavily modified itself from something I found on the Internets) to manually interpolate HSV color values, but I was hoping there was something more elegant than using brute force and essentially an O(n^2) algorithm (I mean the more general "Diffusion Curves" idea I could understand being that inefficient, but I'd think there would be a more elegant solution in this special case). 3/11/2013 1:28:52 AM |
darkone (\/) (;,,,;) (\/) 11610 Posts user info edit post |
Here's my implementation in Matlab:
%RGB, range: 0-1 colorInput1=[1 0 0]; colorInput2=[0.5 0.5 0.2]; colorInput3=[0.2 0.5 0.2]; colorInput4=[1 0 0.2];
%output size, plot with imshow outputDim=[250 250];
%output preallocation outPut=NaN(outputDim(1),outputDim(2),3);
%schematic illustrating how inputs translate to corners % 1 2 % % 3 4
%Linear interpolate values for each color components for top and bottom edges R_1_2=linspace(colorInput1(1),colorInput2(1),outputDim(1)); G_1_2=linspace(colorInput1(2),colorInput2(2),outputDim(1)); B_1_2=linspace(colorInput1(3),colorInput2(3),outputDim(1));
R_3_4=linspace(colorInput3(1),colorInput4(1),outputDim(1)); G_3_4=linspace(colorInput3(2),colorInput4(2),outputDim(1)); B_3_4=linspace(colorInput3(3),colorInput4(3),outputDim(1));
%use loop to handle interpolation for each pair of edge points for i=1:outputDim(2) outPut(:,i,1)=linspace(R_1_2(i),R_3_4(i),outputDim(2)); outPut(:,i,2)=linspace(G_1_2(i),G_3_4(i),outputDim(2)); outPut(:,i,3)=linspace(B_1_2(i),B_3_4(i),outputDim(2)); end
This is for RGB, but with the following color space converter, it should be simple to use any color space. http://www.mathworks.com/matlabcentral/fileexchange/28790-colorspace-transformations
This method isn't very efficient, but that shouldn't matter unless you're generating a lot of very large arrays.3/11/2013 6:28:24 PM |
darkone (\/) (;,,,;) (\/) 11610 Posts user info edit post |
Another method that's slower but provides a proper distance weighted interpolation:
colorInput1=[1 1 0.7]; colorInput2=[1 0 1]; colorInput3=[0 1 0]; colorInput4=[0 1 1];
outputDim=[1250 1250];
x=[1 1 outputDim(1) outputDim(1)]; y=[1 outputDim(2) 1 outputDim(2)]; zr=[colorInput1(1) colorInput2(1) colorInput3(1) colorInput4(1)]; zg=[colorInput1(2) colorInput2(2) colorInput3(2) colorInput4(2)]; zb=[colorInput1(3) colorInput2(3) colorInput3(3) colorInput4(3)];
[xq,yq] = meshgrid(1:outputDim(1),1:outputDim(2));
vqr = griddata(x,y,zr,xq,yq); vqg = griddata(x,y,zg,xq,yq); vqb = griddata(x,y,zb,xq,yq); outPut=cat(3,vqr,vqg,vqb);
3/11/2013 7:49:16 PM |
lewisje All American 9196 Posts user info edit post |
I hope this works in GNU Octave lol 3/11/2013 8:05:59 PM |
darkone (\/) (;,,,;) (\/) 11610 Posts user info edit post |
It should. 3/11/2013 8:27:40 PM |
darkone (\/) (;,,,;) (\/) 11610 Posts user info edit post |
This is much faster than that last bit of code I posted I remember that since this is effectively interpolating with a Cartesian grid, the interp2 function is orders of magnitude faster than the griddata of TriScatteredInterp functions:
colorInput1=[1 0.1 0]; colorInput2=[1 0 1]; colorInput3=[0 1 0]; colorInput4=[0 0.1 1];
outputDim=[1250 1250];
x=[1 1; outputDim(1) outputDim(1)]; y=[1 outputDim(2); 1 outputDim(2)]; zr=[colorInput1(1) colorInput2(1); colorInput3(1) colorInput4(1)]; zg=[colorInput1(2) colorInput2(2); colorInput3(2) colorInput4(2)]; zb=[colorInput1(3) colorInput2(3); colorInput3(3) colorInput4(3)];
[xq,yq] = meshgrid(1:outputDim(1),1:outputDim(2));
vqr = interp2(y,x,zr,xq,yq); vqg = interp2(y,x,zg,xq,yq); vqb = interp2(y,x,zb,xq,yq); outPut=cat(3,vqr,vqg,vqb);
[Edited on March 11, 2013 at 10:19 PM. Reason : more info]3/11/2013 10:17:24 PM |
lewisje All American 9196 Posts user info edit post |
I'm guessing that your example uses vermilion, magenta, green, and blue, but I wonder whether there are any stability problems caused by, say, using four colors with 1 or 0 in all components, or possibly in having a gradient that goes through white (as the R documentation said would be problematic if I used CIELAB space, recommending RGB gradients if they would go through white). 3/11/2013 11:19:43 PM |
darkone (\/) (;,,,;) (\/) 11610 Posts user info edit post |
The way I've done my interpolation, you can't pass through white since the color spaces is rgb. There should be zero issues for valid rgb colors.
Using other color spaces raises some interesting issues. If you convert to rgb and do the interpolation in that color space you have to worry about colors that don't map from one space to another. If you do the same interpolation in the native color space, then you may have to change the interpolation technique sine some color spaces are cylindrical or spherical or just map colors in odd ways.
It's an interesting problem. It's a similar challenge to a function I wrote to create color maps with constantly shifting hues that increase linearly in luminosity. The goal there was to create a color map that degraded to grayscale without banding and to aid colorblind readers of technical figures. 3/11/2013 11:48:51 PM |
moron All American 34141 Posts user info edit post |
^^^ seems to break when using non-square dimensions. 3/12/2013 1:10:45 AM |
moron All American 34141 Posts user info edit post |
changed these 2 lines: x=[1 1; outputDim(2) outputDim(2)]; y=[1 outputDim(1); 1 outputDim(1)]; 3/12/2013 1:51:23 AM |
lewisje All American 9196 Posts user info edit post |
I still haven't tried out the Matlab code, but I did roll my own solution for HSV-based four-corner gradients and although I've only gotten it to work if the gradients don't cross the 0-hue plane, it doesn't really look much different for the range I've tried and only really looks different (albeit with an artifact at yellow that the CIELAB-based gradients lack) if it involves a massive difference in hue.
Also, as might be imagined, if all four corners have high saturation but wildly differing hues, the gradients will zoom around the outside of HSV space rather than going through grey as an RGB or CIELAB-based gradient would. 3/18/2013 6:06:28 PM |
darkone (\/) (;,,,;) (\/) 11610 Posts user info edit post |
Quote : | "Also, as might be imagined, if all four corners have high saturation but wildly differing hues, the gradients will zoom around the outside of HSV space rather than going through grey as an RGB or CIELAB-based gradient would." |
This is due to your method. How are you handling the interpolation? If you treat the HSV space like a unit cylinder and then handle the color transformations like a spatial interpolation along a surface defined by your corner points, you should translate through gray just fine.3/18/2013 6:25:01 PM |
lewisje All American 9196 Posts user info edit post |
I naively treated it like a rectangular prism, with the explicit goal of preserving hue as much as possible. 3/18/2013 7:43:37 PM |
lewisje All American 9196 Posts user info edit post |
k so I still haven't tried out the Matlab code, but in the R code I think I made an image that is very much like that Wikipedia image that inspired this whole thread; I started with the slightly inaccurate notion that U represents "yellow vs. blue" (hues 60 vs. 240) and V represents "green vs. pink" (perpendicularly in HSV space, hues 150 vs. 330) and imagined that if we were interpolating in RGB space, the extreme -U,+V corner would be the most saturated orange at hue 15, the +U,+V corner would be the most saturated purple at hue 285, and so on (it's not quite accurate, because the YUV and sRGB gamuts don't quite match up, but it makes for good illustration), and then I interpolated in CIELAB space even though yet again it's not quite the same as actually doing the calculations of UV coordinates and it doesn't even match up with the spacing of the HSV model... For comparison, the pic from the OP is on the right.
I've noticed that the Wikipedia image has broader regions of nearly flat color, possibly indicative of areas out of the sRGB gamut.
[Edited on March 23, 2013 at 2:35 AM. Reason : a clearer comparison 3/23/2013 2:34:10 AM |
darkone (\/) (;,,,;) (\/) 11610 Posts user info edit post |
This might help you understand YUV better:
I'm still unclear what your goal is. 3/23/2013 11:16:46 PM |
lewisje All American 9196 Posts user info edit post |
1. knew that, understood what it was about
2. I wanted to make pretty pictures. 3/24/2013 5:33:15 AM |
lewisje All American 9196 Posts user info edit post |
It turns out that the standard term (as a generalization) is "mesh gradient"; it's now available by default in Inkscape 0.92: http://wiki.inkscape.org/wiki/index.php/Mesh_Gradients 1/4/2017 8:28:18 PM |
|