001package org.openimaj.image.processing;
002
003import java.io.IOException;
004import java.io.InputStream;
005import java.net.URL;
006
007import org.apache.commons.io.IOUtils;
008import org.openimaj.image.CLImageConversion;
009import org.openimaj.image.Image;
010import org.openimaj.image.processor.ImageProcessor;
011
012import com.nativelibs4java.opencl.CLBuildException;
013import com.nativelibs4java.opencl.CLContext;
014import com.nativelibs4java.opencl.CLEvent;
015import com.nativelibs4java.opencl.CLImage2D;
016import com.nativelibs4java.opencl.CLKernel;
017import com.nativelibs4java.opencl.CLMem;
018import com.nativelibs4java.opencl.CLPlatform.DeviceFeature;
019import com.nativelibs4java.opencl.CLProgram;
020import com.nativelibs4java.opencl.CLQueue;
021import com.nativelibs4java.opencl.JavaCL;
022
023/**
024 * Base {@link ImageProcessor} for GPGPU accelerated processing.
025 * 
026 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
027 *
028 * @param <I> Type of {@link Image} being processed
029 */
030public class CLImageProcessor<I extends Image<?, I>> implements ImageProcessor<I> {
031        protected CLContext context;
032        protected CLKernel kernel;
033
034        /**
035         * Construct with the given OpenCL program
036         * @param program the OpenCL program
037         */
038        public CLImageProcessor(CLProgram program) {
039                try {
040                        this.context = JavaCL.createBestContext(DeviceFeature.GPU);
041                        this.kernel = program.createKernels()[0];
042                } catch (CLBuildException e) {
043                        //fallback to OpenCL on the CPU
044                        this.context = JavaCL.createBestContext(DeviceFeature.CPU);
045                        this.kernel = program.createKernels()[0];                       
046                }
047        }
048
049        /**
050         * Construct with the given OpenCL program source, given in
051         * the form of {@link String}s.
052         * 
053         * @param programSrcs the source of the program
054         */
055        public CLImageProcessor(String... programSrcs) {
056                CLProgram program;
057                try {
058                        this.context = JavaCL.createBestContext(DeviceFeature.GPU);
059                        program = context.createProgram(programSrcs);
060                        this.kernel = program.createKernels()[0];
061                } catch (CLBuildException e) {
062                        //fallback to OpenCL on the CPU
063                        this.context = JavaCL.createBestContext(DeviceFeature.CPU);
064                        program = context.createProgram(programSrcs);
065                        this.kernel = program.createKernels()[0];
066                }
067        }
068
069        /**
070         * Construct with the program sourcecode at the given URL. 
071         * @param srcUrl the url
072         * @throws IOException
073         */
074        public CLImageProcessor(URL srcUrl) throws IOException {
075                this(IOUtils.toString(srcUrl));
076        }
077
078        /**
079         * Construct by reading the program source from a stream
080         * @param src the source stream
081         * @throws IOException
082         */
083        public CLImageProcessor(InputStream src) throws IOException {
084                this(IOUtils.toString(src));
085        }
086
087        /**
088         * Construct with the given OpenCL program
089         * @param context the OpenCL context to use
090         * @param program the OpenCL program
091         */
092        public CLImageProcessor(CLContext context, CLProgram program) {
093                this.context = context;
094                this.kernel = program.createKernels()[0];
095        }
096
097        /**
098         * Construct with the given OpenCL program source, given in
099         * the form of {@link String}s.
100         * @param context the OpenCL context to use
101         * 
102         * @param programSrcs the source of the program
103         */
104        public CLImageProcessor(CLContext context, String... programSrcs) {
105                this.context = context;
106                CLProgram program = context.createProgram(programSrcs);
107                this.kernel = program.createKernels()[0];
108        }
109        
110        /**
111         * Construct with the given OpenCL kernel
112         * @param kernel the OpenCL kernel to use
113         */
114        public CLImageProcessor(CLKernel kernel) {
115                this.context = kernel.getProgram().getContext();
116                this.kernel = kernel;
117        }
118
119        /**
120         * Construct with the program sourcecode at the given URL. 
121         * @param context the OpenCL context to use
122         * @param srcUrl the url
123         * @throws IOException
124         */
125        public CLImageProcessor(CLContext context, URL srcUrl) throws IOException {
126                this(context, IOUtils.toString(srcUrl));
127        }
128
129        /**
130         * Construct by reading the program source from a stream
131         * @param context the OpenCL context to use
132         * @param src the source stream
133         * @throws IOException
134         */
135        public CLImageProcessor(CLContext context, InputStream src) throws IOException {
136                this(context, IOUtils.toString(src));
137        }
138
139        @Override
140        public void processImage(I image) {
141                CLQueue queue = context.createDefaultQueue();
142
143                CLImage2D in = CLImageConversion.convert(context, image);
144                CLImage2D out = context.createImage2D(CLMem.Usage.Output, in.getFormat(), in.getWidth(), in.getHeight());
145                
146                kernel.setArgs(in, out);
147                CLEvent evt = kernel.enqueueNDRange(queue, new int[] {(int) in.getWidth(), (int) in.getHeight()});
148
149                CLImageConversion.convert(queue, evt, out, image);
150                
151                in.release();
152                out.release();
153                queue.release();
154        }
155}