001/******************************************************************************* 002 * This software is provided as a supplement to the authors' textbooks on digital 003 * image processing published by Springer-Verlag in various languages and editions. 004 * Permission to use and distribute this software is granted under the BSD 2-Clause 005 * "Simplified" License (see http://opensource.org/licenses/BSD-2-Clause). 006 * Copyright (c) 2006-2023 Wilhelm Burger, Mark J. Burge. All rights reserved. 007 * Visit https://imagingbook.com for additional details. 008 ******************************************************************************/ 009package More_; 010 011import ij.ImagePlus; 012import ij.gui.GenericDialog; 013import ij.plugin.PlugIn; 014import ij.process.FloatProcessor; 015import ij.process.ImageProcessor; 016import imagingbook.common.noise.hashing.Hash32Shift; 017import imagingbook.common.noise.hashing.Hash32ShiftMult; 018import imagingbook.common.noise.hashing.Hash32Ward; 019import imagingbook.common.noise.hashing.HashFunction; 020import imagingbook.common.noise.hashing.HashPermute; 021import imagingbook.common.noise.perlin.PerlinNoiseGenerator2d; 022import imagingbook.core.jdoc.JavaDocHelp; 023 024/** 025 * <p> 026 * This ImageJ plugin creates a new noise image using a 2D gradient noise generator [1]. Several parameters can be 027 * adjusted. See Ch. 8 of [2] for details. The resulting noise image is of type {@link FloatProcessor}. 028 * </p> 029 * <p> 030 * [1] K. Perlin. Improving noise. In "SIGGRAPH’02: Proceedings of the 29th Annual Conference on Computer Graphics and 031 * Interactive Techniques", pp. 681–682, San Antonio, Texas (2002). 032 * <br> 033 * [2] W. Burger and M. J. Burge. "Principles of 034 * Digital Image Processing - Advanced Methods" (Vol. 3). Undergraduate Topics in Computer Science. Springer-Verlag, 035 * London (2013). 036 * </p> 037 * 038 * @author WB 039 * @version 2022/11/24 040 */ 041public class Perlin_Noise_2D implements PlugIn, JavaDocHelp { 042 043 private enum HasFunctionType { 044 HashPermute, Hash32Ward, Hash32Shift, Hash32ShiftMult; 045 } 046 047 private static int W = 400; // image width 048 private static int H = 300; // image height 049 private static int K = 3; // number of noise frequencies 050 private static double fmin = 0.01; // min. frequency (in cycles per pixel unit) 051 private static double fmax = fmin * Math.pow(2, K); 052 private static double persistence = 0.5; // persistence 053 private static HasFunctionType HashT = HasFunctionType.HashPermute; 054 private static int seed = 0; // hash seed 055 056 @Override 057 public void run(String arg0) { 058 059 if (!runDialog()) { 060 return; 061 } 062 063 HashFunction hf = getHashFun(); 064 065 // create the noise generator: 066 PerlinNoiseGenerator2d ng = new PerlinNoiseGenerator2d(fmin, fmax, persistence, hf); 067 068 // create a new image and fill with noise: 069 ImageProcessor fp = new FloatProcessor(W, H); 070 071 for (int v = 0; v < H; v++) { 072 for (int u = 0; u < W; u++) { 073 double val = ng.getNoiseValue(u, v); 074 fp.setf(u, v, (float)val); 075 } 076 } 077 078 // display the new image: 079 (new ImagePlus("Perlin Noise Image", fp)).show(); 080 } 081 082 private HashFunction getHashFun() { 083 switch(HashT) { 084 case Hash32Shift: 085 return new Hash32Shift(seed); 086 case Hash32ShiftMult: 087 return new Hash32ShiftMult(seed); 088 case Hash32Ward: 089 return new Hash32Ward(seed); 090 case HashPermute: 091 return new HashPermute(seed); 092 } 093 return null; 094 } 095 096 private boolean runDialog() { 097 GenericDialog gd = new GenericDialog(this.getClass().getSimpleName()); 098 gd.addHelp(getJavaDocUrl()); 099 gd.addNumericField("Image width", W, 0); 100 gd.addNumericField("Image height", H, 0); 101 gd.addNumericField("Number of noise frequencies", K, 0); 102 gd.addNumericField("Min. frequency (f_min)", fmin, 2); 103 gd.addNumericField("Max. frequency (f_max)", fmax, 2); 104 gd.addNumericField("Persistence", persistence, 2); 105 gd.addNumericField("Seed (0 = random)", seed, 0); 106 gd.addEnumChoice("Hash function type", HashT); 107 108 gd.showDialog(); 109 if (gd.wasCanceled()) { 110 return false; 111 } 112 113 W = (int) gd.getNextNumber(); 114 H = (int) gd.getNextNumber(); 115 K = (int) gd.getNextNumber(); 116 fmin = gd.getNextNumber(); 117 fmax = gd.getNextNumber(); 118 persistence = gd.getNextNumber(); 119 seed = (int) gd.getNextNumber(); 120 HashT = gd.getNextEnumChoice(HasFunctionType.class); 121 return true; 122 } 123}