为 Julia 集生成自定义调色板

Posted

技术标签:

【中文标题】为 Julia 集生成自定义调色板【英文标题】:Generating Custom Color Palette for Julia set 【发布时间】:2014-07-05 20:59:41 【问题描述】:

我需要一种算法或方法来生成调色板来为 Julia 集图像着色。当使用逃逸时间算法生成图像时,例如我想出了以下图像:

但是我需要一些方法来生成自定义调色板,例如 the Wikipedia page:

如何获得类似的图像?另外,Julia集应该使用什么颜色平滑算法?

这里是代码sn-p用于澄清:

int max_iter = 256;
ComplexNumber constant = new ComplexNumber(cReal,cImag);
float Saturation = 1f;
    for(int X=0; X<WIDTH; X++)
    
        for(int Y=0; Y<HEIGHT; Y++)
        
            ComplexNumber oldz = new ComplexNumber();
            ComplexNumber newz = new ComplexNumber(2.0*(X-WIDTH/2)/(WIDTH/2), 1.33*(Y-HEIGHT/2)/(HEIGHT/2) );
            int i;
            for(i=0;i<max_iter; i++)
            
                oldz = newz;
                newz = newz.square();
                newz.add(constant);
                if(newz.mod() > 2)
                    break;
            
            float Brightness = i < max_iter ? 1f : 0;
            float Hue = (i%256)/255.0f;
            Color color = Color.getHSBColor((float)Hue, Saturation, Brightness);
            img.setRGB(X,Y,color.getRGB());
        
    

【问题讨论】:

颜色设置的细节都取决于你的代码,有些东西没有显示出来。我们看不到或无法理解的东西,我们无法帮助您。关于“平滑算法”,根据我对图像的看法,使用了 none。颜色变化是离散的。 @HovercraftFullOfEels 谢谢大哥的回复。我添加了必要的代码。请看一下。另外,我没有说这些图像使用了平滑算法。我只是问了哪一个用于 Julia 集。 【参考方案1】:

这种颜色映射有很多可能的方法。最简单的在下面的程序中画了出来。

这个sn-p的核心是initColorMap方法。它需要许多插值步骤和一组颜色进行插值。在屏幕截图中,这些是

红色 红、绿 红、绿、蓝(如问题的第一张图片) 红、黄、绿、青、蓝、品红 黑色、橙色、白色、蓝色、深蓝色(尝试从问题中的第二张图片中获取类似于颜色图) 红、绿、蓝,用正弦函数采样

该方法返回一个 int 数组,其中包含插值颜色的 RGB 值。这可以直接使用。但是为了提高通用性,这些数组被包装到ColorMap1D 接口中,该接口提供了一种方法,可以为 0.0 到 1.0 之间的任何给定值返回 RGB 颜色。

对于您的应用案例,可能会这样使用:

double value = (double)iterations / maxIterations;
int rgb = colorMap.getColor(value);

编辑:以下描述和代码已根据评论中的要求进行了更新和扩展)

这种对范围 [0.0, 1.0] 的“规范化”和使用接口的抽象通常是有益的。

作为此抽象可能产生的效果的演示:ColorMaps1D 类包含多个创建 ColorMap1D 实例的方法:

ColorMaps1D#createDefault(int steps, Color ... colors):创建一个默认颜色映射,该颜色映射使用预定义的步数(颜色映射的“分辨率”)在给定的颜色序列上进行插值 ColorMaps1D#create(ColorMap1D delegate, DoubleFunction&lt;Double&gt; function) :此方法创建一个颜色映射,其中 getColor 方法的参数在传递给给定委托的 getColor 方法之前使用给定函数进行转换。

因此,可以轻松创建一个ColorMap1D,在颜色之间进行非线性插值。甚至可以创建一个ColorMap1D 实现,该实现在其他几个颜色映射上进行插值。

作为示例,我添加了一个使用默认的简单 Red->Green->Blue 颜色映射的颜色映射,但使用计算参数正弦的函数访问它。这样,可以多次“循环”通过 Red->Green->Blue 颜色图。

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.util.Arrays;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class ColorMapsTest

    public static void main(String[] args)
    
        SwingUtilities.invokeLater(new Runnable()
        
            @Override
            public void run()
            
                createAndShowGUI();
            
        );
    

    private static void createAndShowGUI()
    
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        f.getContentPane().setLayout(new GridLayout(0,1));

        int steps = 1024;
        f.getContentPane().add(
            createPanel(steps, Color.RED));
        f.getContentPane().add(
            createPanel(steps, Color.RED, Color.GREEN));
        f.getContentPane().add(
            createPanel(steps, Color.RED, Color.GREEN, Color.BLUE));
        f.getContentPane().add(
            createPanel(steps,
                Color.RED, Color.YELLOW,
                Color.GREEN, Color.CYAN,
                Color.BLUE, Color.MAGENTA));
        f.getContentPane().add(
            createPanel(steps,
                Color.BLACK, Color.ORANGE, Color.WHITE,
                Color.BLUE, new Color(0,0,128)));


        JPanel panel = new JPanel(new BorderLayout());
        Color colors[] = new Color[] Color.RED, Color.GREEN, Color.BLUE ;
        String info = "With sine over "+createString(colors);
        panel.add(new JLabel(info), BorderLayout.NORTH);
        ColorMapPanel1D colorMapPanel =
            new ColorMapPanel1D(
                ColorMaps1D.createSine(
                    ColorMaps1D.createDefault(steps, colors), Math.PI * 4));
        panel.add(colorMapPanel, BorderLayout.CENTER);
        f.getContentPane().add(panel);


        f.setSize(500, 400);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    



    private static JPanel createPanel(int steps, Color ... colors)
    
        JPanel panel = new JPanel(new BorderLayout());
        String info = "In "+steps+" steps over "+createString(colors);
        panel.add(new JLabel(info), BorderLayout.NORTH);
        ColorMapPanel1D colorMapPanel =
            new ColorMapPanel1D(ColorMaps1D.createDefault(steps, colors));
        panel.add(colorMapPanel, BorderLayout.CENTER);
        return panel;
    

    private static String createString(Color ... colors)
    
        StringBuilder sb = new StringBuilder();
        for (int i=0; i<colors.length; i++)
        
            sb.append(createString(colors[i]));
            if (i < colors.length - 1)
            
                sb.append(", ");
            
        
        return sb.toString();
    
    private static String createString(Color color)
    
        return "("+color.getRed()+","+color.getGreen()+","+color.getBlue()+")";
    


// NOTE: This is an interface that is equivalent to the functional
// interface in Java 8. In an environment where Java 8 is available,
// this interface may be omitted, and the Java 8 version of this
// interface may be used instead.
interface DoubleFunction<R>

    R apply(double value);




/**
 * Interface for classes that can map a single value from the range
 * [0,1] to an int that represents an RGB color
 */
interface ColorMap1D

    /**
     * Returns an int representing the RGB color, for the given value in [0,1]
     *
     * @param value The value in [0,1]
     * @return The RGB color
     */
    int getColor(double value);


/**
 * Default implementation of a @link ColorMap1D that is backed by
 * a simple int array
 */
class DefaultColorMap1D implements ColorMap1D

    /**
     * The backing array containing the RGB colors
     */
    private final int colorMapArray[];

    /**
     * Creates a color map that is backed by the given array
     *
     * @param colorMapArray The array containing RGB colors
     */
    DefaultColorMap1D(int colorMapArray[])
    
        this.colorMapArray = colorMapArray;
    

    @Override
    public int getColor(double value)
    
        double d = Math.max(0.0, Math.min(1.0, value));
        int i = (int)(d * (colorMapArray.length - 1));
        return colorMapArray[i];
    



/**
 * Methods to create @link ColorMap1D instances
 */
class ColorMaps1D

    /**
     * Creates a @link ColorMap1D that walks through the given delegate
     * color map using a sine function with the given frequency
     *
     * @param delegate The delegate
     * @param frequency The frequency
     * @return The new @link ColorMap1D
     */
    static ColorMap1D createSine(ColorMap1D delegate, final double frequency)
    
        return create(delegate, new DoubleFunction<Double>()
        
            @Override
            public Double apply(double value)
            
                return 0.5 + 0.5 * Math.sin(value * frequency);
            
        );
    

    /**
     * Creates a @link ColorMap1D that will convert the argument
     * with the given function before it is looking up the color
     * in the given delegate
     *
     * @param delegate The delegate @link ColorMap1D
     * @param function The function for converting the argument
     * @return The new @link ColorMap1D
     */
    static ColorMap1D create(
        final ColorMap1D delegate, final DoubleFunction<Double> function)
    
        return new ColorMap1D()
        
            @Override
            public int getColor(double value)
            
                return delegate.getColor(function.apply(value));
            
        ;
    


    /**
     * Creates a new ColorMap1D that maps a value between 0.0 and 1.0
     * (inclusive) to the specified color range, internally using the
     * given number of steps for interpolating between the colors
     *
     * @param steps The number of interpolation steps
     * @param colors The colors
     * @return The color map
     */
    static ColorMap1D createDefault(int steps, Color ... colors)
    
        return new DefaultColorMap1D(initColorMap(steps, colors));
    

    /**
     * Creates the color array which contains RGB colors as integers,
     * interpolated through the given colors.
     *
     * @param steps The number of interpolation steps, and the size
     * of the resulting array
     * @param colors The colors for the array
     * @return The color array
     */
    static int[] initColorMap(int steps, Color ... colors)
    
        int colorMap[] = new int[steps];
        if (colors.length == 1)
        
            Arrays.fill(colorMap, colors[0].getRGB());
            return colorMap;
        
        double colorDelta = 1.0 / (colors.length - 1);
        for (int i=0; i<steps; i++)
        
            double globalRel = (double)i / (steps - 1);
            int index0 = (int)(globalRel / colorDelta);
            int index1 = Math.min(colors.length-1, index0 + 1);
            double localRel = (globalRel - index0 * colorDelta) / colorDelta;

            Color c0 = colors[index0];
            int r0 = c0.getRed();
            int g0 = c0.getGreen();
            int b0 = c0.getBlue();
            int a0 = c0.getAlpha();

            Color c1 = colors[index1];
            int r1 = c1.getRed();
            int g1 = c1.getGreen();
            int b1 = c1.getBlue();
            int a1 = c1.getAlpha();

            int dr = r1-r0;
            int dg = g1-g0;
            int db = b1-b0;
            int da = a1-a0;

            int r = (int)(r0 + localRel * dr);
            int g = (int)(g0 + localRel * dg);
            int b = (int)(b0 + localRel * db);
            int a = (int)(a0 + localRel * da);
            int rgb =
                (a << 24) |
                (r << 16) |
                (g <<  8) |
                (b <<  0);
            colorMap[i] = rgb;
        
        return colorMap;
    

    /**
     * Private constructor to prevent instantiation
     */
    private ColorMaps1D()
    
        // Private constructor to prevent instantiation
    



/**
 * A panel painting a @link ColorMap1D
 */
class ColorMapPanel1D extends JPanel

    /**
     * The @link ColorMap1D that is painted
     */
    private final ColorMap1D colorMap;

    /**
     * Creates a new panel that paints the given color map
     *
     * @param colorMap The @link ColorMap1D to be painted
     */
    ColorMapPanel1D(ColorMap1D colorMap)
    
        this.colorMap = colorMap;
    

    @Override
    protected void paintComponent(Graphics g)
    
        super.paintComponent(g);

        for (int x=0; x<getWidth(); x++)
        
            double d = (double)x / (getWidth() - 1);
            int rgb = colorMap.getColor(d);
            g.setColor(new Color(rgb));
            g.drawLine(x, 0, x, getHeight());
        

    

(关于颜色平滑:这可能应该在一个单独的问题中提出。或者可能不是,因为在 *** 上已经有 许多 问题。例如,请参阅 @987654321 @(或许多其他人))

【讨论】:

谢谢兄弟。你刚有胆量。这正是我想要实现的。我进一步请您详细说明,在两种颜色之间进行非线性插值,以及如何实现这一点。 (+1) @AbdulFatir 我稍微扩展了答案和代码。它现在展示了如何使用“正弦曲线”功能多次循环浏览给定的颜色图。但是您可以使用任意函数将 [0,1] 范围内的值映射到 [0,1] 范围内的另一个值 这是纯粹的美丽!谢谢! 确实,一个精心设计的答案。 1+(数小时前给出)。

以上是关于为 Julia 集生成自定义调色板的主要内容,如果未能解决你的问题,请参考以下文章

应用于动态主题的自定义调色板

seaborn使用boxplot函数进行箱图可视化(使用色彩调色板自定义设置箱图的颜色自定义颜色列表并创建为自己的调色板sns.set_palette全局设置palette参数)

为Gutenberg自定义块添加内置调色板

R语言使用treemap包中的treemap函数可视化treemap图:treemap将分层数据显示为一组嵌套矩形自定义设置treemap图的调色板自定义设置treemap标题字体的大小

如何在Julia中为结构实现自定义序列化/反序列化?

使用相同的自定义调色板将 8bpp 索引位图转换为 24bpp 并再次转换