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

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

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

这是用于澄清的代码段:

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());
        }
    }

最佳答案

有许多可能的方法用于这种颜色映射。下面的程序中概述了最简单的方法。

此代码段的核心是initColorMap方法。它需要许多插值步骤以及要在其间进行插值的颜色数组。在屏幕截图中,这些是

  • 红色
  • 红色,绿色
  • 红色,绿色,蓝色(类似于问题的第一个图像)
  • 红色,黄色,绿色,青色,蓝色,洋红色
  • 黑色,橙色,白色,蓝色,深蓝色(尝试从问题中的第二张图像中获取与颜色图相似的颜色图)
  • 红色,绿色,蓝色,使用正弦函数
  • 采样

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

    对于您的应用程序案例,这可能像这样使用:
    double value = (double)iterations / maxIterations;
    int rgb = colorMap.getColor(value);
    

    ( EDIT :以下说明和代码已根据注释中的请求进行了更新和扩展)

    这样的“归一化”到范围[0.0,1.0]和使用接口进行抽象通常是有益的。

    为了说明这种抽象可能带来的影响:ColorMaps1D类包含几种创建ColorMap1D实例的方法:
  • ColorMaps1D#createDefault(int steps, Color ... colors):创建一个默认颜色图,该颜色图以给定的颜色序列以预定义的步数(颜色图的“分辨率”)进行插值
  • ColorMaps1D#create(ColorMap1D delegate, DoubleFunction<Double> function):此方法创建一个颜色图,其中getColor方法的参数在传递给定委托的getColor方法之前,先使用给定函数进行转换。

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

    例如,我添加了一个色图,该色图使用默认的简单的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());
            }
    
        }
    }
    

    (关于颜色平滑:这可能应该在一个单独的问题中提出。也许不是,因为在StackOverflow上已经有很多关于此问题的问题。例如,请参阅Smooth spectrum for Mandelbrot Set rendering(或许多其他问题))

    07-25 21:40