Pour faire plaisir a progfou et PRomu@ld (), un petit algo pour faire de la synthese de texture...



A gauche: image originale (64x64)
A droite: image générée (256x256)

Code java : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
/**
 * @author Xavier Philippeau
 * 
 * TextureSynthesis (Mono scale) 
 *
 */
public class TextureSynthesis {
 
	// class NeighborhoodCache: precomputed "Neighborhood" value (rgb) around a pixel
	private class NeighborhoodCache extends ArrayList<int[]> {};
 
	// size of the half-window used as neighborhood
	private int neighborhoodHalfSize = 0;
 
	// precomputed "Neighborhood" for all pixels in the sample
	private NeighborhoodCache neighboroodCache = null;; 
 
	/**
         * Constructor
         * 
         * @param neighborhoodHalfSize size of the half-window used as neighborhood
         */
	public TextureSynthesis(int neighborhoodHalfSize) {
		this.neighborhoodHalfSize = neighborhoodHalfSize;
	}
 
	/**
         * Create, then initialize the output image with pseudo-random values
         * 
         * @param input the input sample
         * @param width the width of the output image to create
         * @param height the height of the output image to create
         * @return the output image
         */
	private BufferedImage initialize(BufferedImage input, int width, int height) {
		BufferedImage output = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
 
		Random rand = new Random(width+height);
 
		for(int y=0;y<height;y++) {
			for(int x=0;x<width;x++) {
 
				// only the right & bottow bands are needed due to
				// the toric boundary in neighborhood computation
				if (x<width-neighborhoodHalfSize && y<height-neighborhoodHalfSize) {
					continue;
				}
 
				// "scanline distance" in output 
				int d = x+y*width;
 
				// corresponding "scanline distance" in input 
				int ds = d % (input.getWidth()*input.getHeight());
				int ys = ds/input.getWidth();
				int xs = ds-ys*input.getWidth();
 
				// copy input to output
				output.setRGB(x, y, input.getRGB(xs,ys));
			}
		}
 
		return output;
	}
 
	/**
         * Return the neighborhood list of pixels 
         * 
         * @param img the image to explore
         * @param x coordinate x of the pixel
         * @param y coordinate y of the pixel
         * @return neighborhood of pixel(x,y)
         */
	private int[] neighborhood(BufferedImage img, int x, int y) {
		int width = img.getWidth();
		int height = img.getHeight();
 
		// neighborhood = previous pixel in scanline order  
		//
		// X X X X X
		// X X X X X
		// X X O . .
		// . . . . .
		// . . . . .
 
		int halfsize = this.neighborhoodHalfSize;
 
		int neighborhood_size = (2*halfsize+1)*(halfsize) + halfsize;
		int[] neighborhood = new int[neighborhood_size];
		int index=0;
		//ArrayList<Integer> neighborhood = new ArrayList<Integer>();
 
		for(int dy=-halfsize; dy<=0; dy++) {
			for(int dx=-halfsize; dx<=halfsize; dx++) {
				int xk = x + dx;
				int yk = y + dy;
 
				// Shape constraint
				if (dy==0 && dx==0) break;
 
				// toric boundary
				if (xk<0) xk=width+xk;
				if (yk<0) yk=height+yk;
				if (xk>=width) xk=xk-width;
				if (yk>=height) yk=yk-height;
 
				//neighborhood.add( img.getRGB(xk,yk) );
				neighborhood[index++]=img.getRGB(xk,yk);
			}
		}
		return neighborhood;
	}
 
	/**
         * Square Difference of two Neighborhoods
         * 
         * @param n1 Neighborhood 1
         * @param n2 Neighborhood 2
         * @param bestscore fast exit condition: exit if difference exceed this threshold
         * @return the Square Difference
         */
	private double squareDiff(int[] n1, int[] n2, double bestscore) {
 
		double squareDiff = 0;
		for(int i=0;i<n1.length;i++) {
 
			int rgb1 = n1[i], rgb2=n2[i];
			double dr = ((rgb1>>16)&0xFF)-((rgb2>>16)&0xFF);
			double dg = ((rgb1>>8)&0xFF)-((rgb2>>8)&0xFF);
			double db = ((rgb1>>0)&0xFF)-((rgb2>>0)&0xFF);
 
			squareDiff += dr*dr + dg*dg + db*db;
 
			// fast exit condition
			if (squareDiff>=bestscore) break;
		}
 
		return squareDiff;
	}
 
	/**
         * 
         * Precompute Neighborhood for all pixels in the image
         * 
         * @param img the image to explore
         * @return the list of Neighborhood
         */
	private NeighborhoodCache precomputeNeighborhood(BufferedImage img) {
		NeighborhoodCache cache = new NeighborhoodCache();
		for(int y=0;y<img.getHeight();y++) {
			for(int x=0;x<img.getWidth();x++) {
				int[] nb = neighborhood(img,x,y);
				cache.add(y*img.getWidth()+x,nb);
			}
		}
		return cache;
	}
 
	/**
         * compute the value of one pixel
         * 
         * @param sample Image reference
         * @param output Image computed
         * @param x coordinate x of the pixel
         * @param y coordinate y of the pixel
         * @return (r,g,b) value of the computed pixel
         */
	private int computePixel(BufferedImage sample, BufferedImage output, int x, int y) {
 
		double bestscore = Double.MAX_VALUE;
		int bestValue = 0;
 
		// Neighborhood in the Ouput image
		int[] outputNeighborhood = neighborhood(output,x,y);
 
		// Search best matching Neighborhood in the Sample image
        // (exhaustive search !) 
		int width = sample.getWidth();
		int height = sample.getHeight();
		for(int ys=0; ys<height; ys++) {
			for(int xs=0; xs<width; xs++) {
 
				// get Sample Neighborhood from the precomputed cache
				int position = ys*width+xs;
				int[] sampleNeighborhood = this.neighboroodCache.get(position);
 
				// compare using square difference
				double score = squareDiff(sampleNeighborhood,outputNeighborhood,bestscore);
				//double score = cosDiff(sampleNeighborhood,outputNeighborhood,bestscore);
 
				// keep the best score
				if (score<bestscore) {
					bestscore = score;
					bestValue = sample.getRGB(xs,ys);	
				}
			}
		}
 
		return bestValue;
	}
 
	/**
         * Generate a new image of size width x height, 
         * using the sample image as a texture seed.
         * 
         * @param sample The sample image
         * @param outputwidth Generated image width
         * @param outputheight Generated image height
         * @return the generated image
         */
	public BufferedImage synthesis(BufferedImage sample, int outputwidth, int outputheight) {
 
		// initialize output with pseudo-random values
		BufferedImage output = initialize(sample,outputwidth,outputheight);
 
		// precompute the neighborhood of all pixels in the sample
		this.neighboroodCache = precomputeNeighborhood(sample);
 
		// compute each pixel of the output
		for(int y=0; y<outputheight; y++) {
			System.out.println(y+"/"+outputheight);
			for(int x=0; x<outputwidth; x++) {
				int rgb = computePixel(sample,output,x,y);
				output.setRGB(x,y,rgb);
			}
		}
 
		return output;
	}
}