001package org.openimaj.image.processing; 002 003import java.io.IOException; 004 005import org.apache.commons.io.IOUtils; 006import org.openimaj.image.CLImageConversion; 007import org.openimaj.image.Image; 008 009import com.nativelibs4java.opencl.CLBuildException; 010import com.nativelibs4java.opencl.CLContext; 011import com.nativelibs4java.opencl.CLEvent; 012import com.nativelibs4java.opencl.CLImage2D; 013import com.nativelibs4java.opencl.CLKernel; 014import com.nativelibs4java.opencl.CLMem; 015import com.nativelibs4java.opencl.CLProgram; 016import com.nativelibs4java.opencl.CLQueue; 017import com.nativelibs4java.opencl.JavaCL; 018import com.nativelibs4java.opencl.CLPlatform.DeviceFeature; 019 020/** 021 * Simple image arithmetic functions implemented using OpenCL. 022 * 023 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 024 */ 025public class CLImageArithmetic { 026 private CLContext context; 027 028 private CLKernel addImage; 029 private CLKernel addConstant; 030 private CLKernel subtractImage; 031 private CLKernel subtractConstant; 032 private CLKernel multiplyImage; 033 private CLKernel multiplyConstant; 034 private CLKernel divideImage; 035 private CLKernel divideConstant; 036 037 /** 038 * Default constructor. Automatically selects the context. 039 */ 040 public CLImageArithmetic() { 041 CLProgram program = null; 042 try { 043 this.context = JavaCL.createBestContext(DeviceFeature.GPU); 044 try { 045 program = context.createProgram(IOUtils.toString(CLImageArithmetic.class.getResource("ImageArithmetic.cl"))); 046 } catch (IOException e) { e.printStackTrace(); } 047 loadKernels(program); 048 } catch (CLBuildException e) { 049 //fallback to OpenCL on the CPU 050 this.context = JavaCL.createBestContext(DeviceFeature.CPU); 051 try { 052 program = context.createProgram(IOUtils.toString(CLImageArithmetic.class.getResource("ImageArithmetic.cl"))); 053 } catch (IOException e1) { e.printStackTrace(); } 054 loadKernels(program); 055 } 056 } 057 058 /** 059 * Construct with the given context. 060 * @param context the context. 061 */ 062 public CLImageArithmetic(CLContext context) { 063 try { 064 this.context = context; 065 CLProgram program = context.createProgram(IOUtils.toString(CLImageArithmetic.class.getResource("ImageArithmetic.cl"))); 066 loadKernels(program); 067 } catch (IOException e) { e.printStackTrace(); } 068 } 069 070 private void loadKernels(CLProgram program) { 071 addImage = program.createKernel("addImage"); 072 addConstant = program.createKernel("addConstant"); 073 subtractImage = program.createKernel("subtractImage"); 074 subtractConstant = program.createKernel("subtractConstant"); 075 multiplyImage = program.createKernel("multiplyImage"); 076 multiplyConstant = program.createKernel("multiplyConstant"); 077 divideImage = program.createKernel("divideImage"); 078 divideConstant = program.createKernel("divideConstant"); 079 } 080 081 private synchronized CLEvent process(CLKernel kernel, CLQueue queue, CLImage2D in1, CLImage2D in2, CLImage2D out) { 082 kernel.setArgs(in1, in2, out); 083 return kernel.enqueueNDRange(queue, new int[] {(int) in1.getWidth(), (int) in1.getHeight()}); 084 } 085 086 private synchronized CLEvent process(CLKernel kernel, CLQueue queue, CLImage2D in1, float[] amt, CLImage2D out) { 087 kernel.setArgs(in1, amt, out); 088 return kernel.enqueueNDRange(queue, new int[] {(int) in1.getWidth(), (int) in1.getHeight()}); 089 } 090 091 private synchronized CLImage2D process(CLKernel kernel, CLImage2D in1, CLImage2D in2) { 092 CLQueue queue = context.createDefaultQueue(); 093 CLImage2D out = context.createImage2D(CLMem.Usage.Output, in1.getFormat(), in1.getWidth(), in1.getHeight()); 094 095 process(kernel, queue, in1, in2, out).waitFor(); 096 queue.release(); 097 098 return out; 099 } 100 101 private synchronized CLImage2D process(CLKernel kernel, CLImage2D in, float[] amt) { 102 CLQueue queue = context.createDefaultQueue(); 103 CLImage2D out = context.createImage2D(CLMem.Usage.Output, in.getFormat(), in.getWidth(), in.getHeight()); 104 105 process(kernel, queue, in, amt, out).waitFor(); 106 queue.release(); 107 108 return out; 109 } 110 111 /** 112 * Add two images, storing the result in another image 113 * 114 * @param queue the command queue 115 * @param in1 the first image to add 116 * @param in2 the second image to add 117 * @param out the result image 118 * @return the event 119 */ 120 public CLEvent add(CLQueue queue, CLImage2D in1, CLImage2D in2, CLImage2D out) { 121 return process(addImage, queue, in1, in2, out); 122 } 123 124 /** 125 * Add a constant to an image, storing the result in another image 126 * 127 * @param queue the command queue 128 * @param in1 the first image to add 129 * @param amt the constant 130 * @param out the result image 131 * @return the event 132 */ 133 public CLEvent add(CLQueue queue, CLImage2D in1, float[] amt, CLImage2D out) { 134 return process(addConstant, queue, in1, amt, out); 135 } 136 137 /** 138 * Add two images, returning a new image with the result 139 * 140 * @param in1 the first image to add 141 * @param in2 the second image to add 142 * @return the event 143 */ 144 public CLImage2D add(CLImage2D in1, CLImage2D in2) { 145 return process(addImage, in1, in2); 146 } 147 148 /** 149 * Add a constant to an image, returning a new image with the result 150 * 151 * @param in1 the first image to add 152 * @param amt the constant 153 * @return the event 154 */ 155 public CLImage2D add(CLImage2D in1, float[] amt) { 156 return process(addImage, in1, amt); 157 } 158 159 /** 160 * Add two images, returning a new image with the result 161 * 162 * @param <I> The type of image 163 * 164 * @param in1 the first image to add 165 * @param in2 the second image to add 166 * @return the event 167 */ 168 public <I extends Image<?, I>> I add(I in1, I in2) { 169 CLQueue queue = context.createDefaultQueue(); 170 171 CLImage2D clin1 = CLImageConversion.convert(context, in1); 172 CLImage2D clin2 = CLImageConversion.convert(context, in2); 173 CLImage2D clout = context.createImage2D(CLMem.Usage.Output, clin1.getFormat(), clin1.getWidth(), clin1.getHeight()); 174 175 CLEvent evt = process(addImage, queue, clin1, clin2, clout); 176 177 I out = CLImageConversion.convert(queue, evt, clout, in1.newInstance(in1.getWidth(), in1.getHeight())); 178 179 clin1.release(); 180 clin2.release(); 181 clout.release(); 182 queue.release(); 183 184 return out; 185 } 186 187 /** 188 * Add a constant to an image, returning a new image with the result 189 * 190 * @param <I> The type of image 191 * 192 * @param in1 the first image to add 193 * @param amt the constant 194 * @return the event 195 */ 196 public <I extends Image<?, I>> I add(I in1, float[] amt) { 197 CLQueue queue = context.createDefaultQueue(); 198 199 CLImage2D clin1 = CLImageConversion.convert(context, in1); 200 CLImage2D clout = context.createImage2D(CLMem.Usage.Output, clin1.getFormat(), clin1.getWidth(), clin1.getHeight()); 201 202 CLEvent evt = process(addImage, queue, clin1, amt, clout); 203 204 I out = CLImageConversion.convert(queue, evt, clout, in1.newInstance(in1.getWidth(), in1.getHeight())); 205 206 clin1.release(); 207 clout.release(); 208 queue.release(); 209 210 return out; 211 } 212 213 /** 214 * Subtract two images, storing the result in another image 215 * 216 * @param queue the command queue 217 * @param in1 the first image to subtract 218 * @param in2 the second image to subtract 219 * @param out the result image 220 * @return the event 221 */ 222 public CLEvent subtract(CLQueue queue, CLImage2D in1, CLImage2D in2, CLImage2D out) { 223 return process(subtractImage, queue, in1, in2, out); 224 } 225 226 /** 227 * Subtract a constant from an image, storing the result in another image 228 * 229 * @param queue the command queue 230 * @param in1 the first image to subtract 231 * @param amt the constant 232 * @param out the result image 233 * @return the event 234 */ 235 public CLEvent subtract(CLQueue queue, CLImage2D in1, float[] amt, CLImage2D out) { 236 return process(subtractConstant, queue, in1, amt, out); 237 } 238 239 /** 240 * Subtract two images, returning a new image with the result 241 * 242 * @param in1 the first image to subtract 243 * @param in2 the second image to subtract 244 * @return the event 245 */ 246 public CLImage2D subtract(CLImage2D in1, CLImage2D in2) { 247 return process(subtractImage, in1, in2); 248 } 249 250 /** 251 * Subtract a constant from an image, returning a new image with the result 252 * 253 * @param in1 the first image to subtract 254 * @param amt the constant 255 * @return the event 256 */ 257 public CLImage2D subtract(CLImage2D in1, float[] amt) { 258 return process(subtractImage, in1, amt); 259 } 260 261 /** 262 * Subtract two images, returning a new image with the result 263 * 264 * @param <I> The type of image 265 * 266 * @param in1 the first image to subtract 267 * @param in2 the second image to subtract 268 * @return the event 269 */ 270 public <I extends Image<?, I>> I subtract(I in1, I in2) { 271 CLQueue queue = context.createDefaultQueue(); 272 273 CLImage2D clin1 = CLImageConversion.convert(context, in1); 274 CLImage2D clin2 = CLImageConversion.convert(context, in2); 275 CLImage2D clout = context.createImage2D(CLMem.Usage.Output, clin1.getFormat(), clin1.getWidth(), clin1.getHeight()); 276 277 CLEvent evt = process(subtractImage, queue, clin1, clin2, clout); 278 279 I out = CLImageConversion.convert(queue, evt, clout, in1.newInstance(in1.getWidth(), in1.getHeight())); 280 281 clin1.release(); 282 clin2.release(); 283 clout.release(); 284 queue.release(); 285 286 return out; 287 } 288 289 /** 290 * Subtract a constant from an image, returning a new image with the result 291 * 292 * @param <I> The type of image 293 * 294 * @param in1 the first image to subtract 295 * @param amt the constant 296 * @return the event 297 */ 298 public <I extends Image<?, I>> I subtract(I in1, float[] amt) { 299 CLQueue queue = context.createDefaultQueue(); 300 301 CLImage2D clin1 = CLImageConversion.convert(context, in1); 302 CLImage2D clout = context.createImage2D(CLMem.Usage.Output, clin1.getFormat(), clin1.getWidth(), clin1.getHeight()); 303 304 CLEvent evt = process(subtractImage, queue, clin1, amt, clout); 305 306 I out = CLImageConversion.convert(queue, evt, clout, in1.newInstance(in1.getWidth(), in1.getHeight())); 307 308 clin1.release(); 309 clout.release(); 310 queue.release(); 311 312 return out; 313 } 314 315 /** 316 * Multiply two images, storing the result in another image 317 * 318 * @param queue the command queue 319 * @param in1 the first image to multiply 320 * @param in2 the second image to multiply 321 * @param out the result image 322 * @return the event 323 */ 324 public CLEvent multiply(CLQueue queue, CLImage2D in1, CLImage2D in2, CLImage2D out) { 325 return process(multiplyImage, queue, in1, in2, out); 326 } 327 328 /** 329 * Multiply an image by a constant, storing the result in another image 330 * 331 * @param queue the command queue 332 * @param in1 the first image to multiply 333 * @param amt the constant 334 * @param out the result image 335 * @return the event 336 */ 337 public CLEvent multiply(CLQueue queue, CLImage2D in1, float[] amt, CLImage2D out) { 338 return process(multiplyConstant, queue, in1, amt, out); 339 } 340 341 /** 342 * Multiply two images, returning a new image with the result 343 * 344 * @param in1 the first image to multiply 345 * @param in2 the second image to multiply 346 * @return the event 347 */ 348 public CLImage2D multiply(CLImage2D in1, CLImage2D in2) { 349 return process(multiplyImage, in1, in2); 350 } 351 352 /** 353 * Multiply an image by a constant, returning a new image with the result 354 * 355 * @param in1 the first image to multiply 356 * @param amt the constant 357 * @return the event 358 */ 359 public CLImage2D multiply(CLImage2D in1, float[] amt) { 360 return process(multiplyImage, in1, amt); 361 } 362 363 /** 364 * Multiply two images, returning a new image with the result 365 * 366 * @param <I> The type of image 367 * 368 * @param in1 the first image to multiply 369 * @param in2 the second image to multiply 370 * @return the event 371 */ 372 public <I extends Image<?, I>> I multiply(I in1, I in2) { 373 CLQueue queue = context.createDefaultQueue(); 374 375 CLImage2D clin1 = CLImageConversion.convert(context, in1); 376 CLImage2D clin2 = CLImageConversion.convert(context, in2); 377 CLImage2D clout = context.createImage2D(CLMem.Usage.Output, clin1.getFormat(), clin1.getWidth(), clin1.getHeight()); 378 379 CLEvent evt = process(multiplyImage, queue, clin1, clin2, clout); 380 381 I out = CLImageConversion.convert(queue, evt, clout, in1.newInstance(in1.getWidth(), in1.getHeight())); 382 383 clin1.release(); 384 clin2.release(); 385 clout.release(); 386 queue.release(); 387 388 return out; 389 } 390 391 /** 392 * Multiply an image by a constant, returning a new image with the result 393 * 394 * @param <I> The type of image 395 * 396 * @param in1 the first image to multiply 397 * @param amt the constant 398 * @return the event 399 */ 400 public <I extends Image<?, I>> I multiply(I in1, float[] amt) { 401 CLQueue queue = context.createDefaultQueue(); 402 403 CLImage2D clin1 = CLImageConversion.convert(context, in1); 404 CLImage2D clout = context.createImage2D(CLMem.Usage.Output, clin1.getFormat(), clin1.getWidth(), clin1.getHeight()); 405 406 CLEvent evt = process(multiplyImage, queue, clin1, amt, clout); 407 408 I out = CLImageConversion.convert(queue, evt, clout, in1.newInstance(in1.getWidth(), in1.getHeight())); 409 410 clin1.release(); 411 clout.release(); 412 queue.release(); 413 414 return out; 415 } 416 417 /** 418 * Divide two images, storing the result in another image 419 * 420 * @param queue the command queue 421 * @param in1 the first image to divide 422 * @param in2 the second image to divide 423 * @param out the result image 424 * @return the event 425 */ 426 public CLEvent divide(CLQueue queue, CLImage2D in1, CLImage2D in2, CLImage2D out) { 427 return process(divideImage, queue, in1, in2, out); 428 } 429 430 /** 431 * Divide an image by a constant, storing the result in another image 432 * 433 * @param queue the command queue 434 * @param in1 the first image to divide 435 * @param amt the constant 436 * @param out the result image 437 * @return the event 438 */ 439 public CLEvent divide(CLQueue queue, CLImage2D in1, float[] amt, CLImage2D out) { 440 return process(divideConstant, queue, in1, amt, out); 441 } 442 443 /** 444 * Divide two images, returning a new image with the result 445 * 446 * @param in1 the first image to divide 447 * @param in2 the second image to divide 448 * @return the event 449 */ 450 public CLImage2D divide(CLImage2D in1, CLImage2D in2) { 451 return process(divideImage, in1, in2); 452 } 453 454 /** 455 * Divide an image by a constant, returning a new image with the result 456 * 457 * @param in1 the first image to divide 458 * @param amt the constant 459 * @return the event 460 */ 461 public CLImage2D divide(CLImage2D in1, float[] amt) { 462 return process(divideImage, in1, amt); 463 } 464 465 /** 466 * Divide two images, returning a new image with the result 467 * 468 * @param <I> The type of image 469 * 470 * @param in1 the first image to divide 471 * @param in2 the second image to divide 472 * @return the event 473 */ 474 public <I extends Image<?, I>> I divide(I in1, I in2) { 475 CLQueue queue = context.createDefaultQueue(); 476 477 CLImage2D clin1 = CLImageConversion.convert(context, in1); 478 CLImage2D clin2 = CLImageConversion.convert(context, in2); 479 CLImage2D clout = context.createImage2D(CLMem.Usage.Output, clin1.getFormat(), clin1.getWidth(), clin1.getHeight()); 480 481 CLEvent evt = process(divideImage, queue, clin1, clin2, clout); 482 483 I out = CLImageConversion.convert(queue, evt, clout, in1.newInstance(in1.getWidth(), in1.getHeight())); 484 485 clin1.release(); 486 clin2.release(); 487 clout.release(); 488 queue.release(); 489 490 return out; 491 } 492 493 /** 494 * Divide an image by a constant, returning a new image with the result 495 * 496 * @param <I> The type of image 497 * 498 * @param in1 the first image to divide 499 * @param amt the constant 500 * @return the event 501 */ 502 public <I extends Image<?, I>> I divide(I in1, float[] amt) { 503 CLQueue queue = context.createDefaultQueue(); 504 505 CLImage2D clin1 = CLImageConversion.convert(context, in1); 506 CLImage2D clout = context.createImage2D(CLMem.Usage.Output, clin1.getFormat(), clin1.getWidth(), clin1.getHeight()); 507 508 CLEvent evt = process(divideImage, queue, clin1, amt, clout); 509 510 I out = CLImageConversion.convert(queue, evt, clout, in1.newInstance(in1.getWidth(), in1.getHeight())); 511 512 clin1.release(); 513 clout.release(); 514 queue.release(); 515 516 return out; 517 } 518}