class ProjectiveTransformerCL extends ProjectiveTransformer implements ImageTransformerCL {
    public ProjectiveTransformerCL(JavaCVCL context) {
        this(context, null, null, null, null, null, new double[0], null);
    }
    public ProjectiveTransformerCL(JavaCVCL context, double[] referencePoints) {
        this(context, null, null, null, null, null, referencePoints, null);
    }
    public ProjectiveTransformerCL(JavaCVCL context, ProjectiveDevice d1, ProjectiveDevice d2, CvMat n,
            double[] referencePoints1, double[] referencePoints2) {
        // assuming d1 has identity values, use d2's stuff directly
        this(context, d1.cameraMatrix, d2.cameraMatrix, d2.R, d2.T, n, referencePoints1, referencePoints2);
    }
    public ProjectiveTransformerCL(JavaCVCL context, CvMat K1, CvMat K2, CvMat R, CvMat t, CvMat n,
            double[] referencePoints1, double[] referencePoints2) {
        super(K1, K2, R, t, n, referencePoints1, referencePoints2);
        final int dotSize = createParameters().size();
        this.context = context;
        this.HBuffer = context.getCLContext().createFloatBuffer(dotSize*9,  CLBuffer.Mem.READ_ONLY);
        if (getClass() == ProjectiveTransformerCL.class) {
            CLKernel[] kernels = context.buildKernels(
                    JavaCVCL.fastCompilerOptions + " -DDOT_SIZE=" + dotSize,
                    "ImageTransformer.cl:ProjectiveTransformer.cl",
                    "transformOne", "transformSub", "transformDot", "reduceOutputData");
            this.oneKernel    = kernels[0];
            this.subKernel    = kernels[1];
            this.dotKernel    = kernels[2];
            this.reduceKernel = kernels[3];
        }
    }

    protected final JavaCVCL context;
    protected final CLBuffer<FloatBuffer> HBuffer;
    private CLKernel oneKernel, subKernel, dotKernel, reduceKernel;

    public JavaCVCL getContext() {
        return context;
    }

    protected void prepareHomographies(CLBuffer HBuffer, int pyramidLevel, 
            ImageTransformer.Parameters[] parameters, boolean[] inverses) {
        FloatBuffer floatH = (FloatBuffer)HBuffer.getBuffer().rewind();
        CvMat H = H3x3.get();
        for (int i = 0; i < parameters.length; i++) {
            prepareHomography(H, pyramidLevel, (ProjectiveTransformer.Parameters)parameters[i],
                    inverses == null ? false : inverses[i]);
            for (int j = 0; j < 9; j++) {
                floatH.put((float)H.get(j));
            }
        }
        floatH.rewind();
    }

    public void transform(CLImage2d srcImg, CLImage2d subImg, CLImage2d srcDotImg,
            CLImage2d transImg, CLImage2d dstImg, CLImage2d maskImg,
            ImageTransformer.Parameters[] parameters, boolean[] inverses,
            InputData inputData, OutputData outputData) {<FILL_FUNCTION_BODY>}
}

        prepareHomographies(HBuffer, inputData.pyramidLevel, parameters, inverses);

        final int dotSize = parameters[0].size();
        final int localSize = parameters.length > 1 ? parameters.length : (inputData.roiWidth > 32 ? 64 : 32);
        final int globalSize = JavaCVCL.alignCeil(inputData.roiWidth, localSize);
        final int reduceSize = globalSize/localSize;

        // allocate buffers if necessary
        CLBuffer inputBuffer = inputData.getBuffer(context);
        CLBuffer outputBuffer = outputData.getBuffer(context, dotSize, reduceSize);

        CLEventList list = new CLEventList(1);

        // setup kernel
        context.writeBuffer(HBuffer, false); // upload H
        if (inputData.autoWrite) {
            inputData.writeBuffer(context);
        }
        CLKernel kernel = null;
        if (subImg == null) {
            assert parameters.length == 1;
            kernel = oneKernel.putArg(srcImg).putArg(dstImg == null ? transImg : dstImg).putArg(maskImg)
                    .putArg(HBuffer).putArg(inputBuffer).putArg(outputBuffer).rewind();
        } else if (srcDotImg == null) {
            assert parameters.length == 1;
            kernel = subKernel.putArg(srcImg).putArg(subImg).putArg(transImg).putArg(dstImg).putArg(maskImg)
                    .putArg(HBuffer).putArg(inputBuffer).putArg(outputBuffer).rewind();
        } else {
            assert parameters.length == dotSize;
            kernel = dotKernel.putArg(srcImg).putArg(subImg).putArg(srcDotImg).putArg(maskImg)
                    .putArg(HBuffer).putArg(inputBuffer).putArg(outputBuffer).rewind();
        }
        context.executeKernel(kernel, inputData.roiX, 0, 0,
                globalSize, 1, parameters.length,
                localSize, 1, parameters.length, list); // execute program
        if (reduceSize > 1) {
            reduceKernel.putArg(outputBuffer).rewind();
            context.executeKernel(reduceKernel, 0, reduceSize, reduceSize);
        }
        if (outputData.autoRead) {
            outputData.readBuffer(context);
        }

//        CLEvent event = list.getEvent(0);
//        System.out.println(kernel + " " + (event.getProfilingInfo(CLEvent.ProfilingCommand.END) -
//                            event.getProfilingInfo(CLEvent.ProfilingCommand.START))/1000000.0);

//        long res = q.getDevice().getProfilingTimerResolution();
//        System.out.println(res);
    
<methods>public void <init>() ,public void <init>(double[]) ,public void <init>(org.bytedeco.javacv.ProjectiveDevice, org.bytedeco.javacv.ProjectiveDevice, CvMat, double[], double[]) ,public void <init>(CvMat, CvMat, CvMat, CvMat, CvMat, double[], double[]) ,public org.bytedeco.javacv.ProjectiveTransformer.Parameters createParameters() ,public CvScalar getFillColor() ,public CvMat getInvK1() ,public CvMat getInvK2() ,public CvMat getK1() ,public CvMat getK2() ,public CvMat getN() ,public CvMat getR() ,public double[] getReferencePoints1() ,public double[] getReferencePoints2() ,public CvMat getT() ,public void setFillColor(CvScalar) ,public void transform(IplImage, IplImage, CvRect, int, org.bytedeco.javacv.ImageTransformer.Parameters, boolean) ,public void transform(CvMat, CvMat, org.bytedeco.javacv.ImageTransformer.Parameters, boolean) ,public void transform(org.bytedeco.javacv.ImageTransformer.Data[], CvRect, org.bytedeco.javacv.ImageTransformer.Parameters[], boolean[]) <variables>protected CvMat[] H,protected static ThreadLocal<CvMat> H3x3,protected CvMat K1,protected CvMat K2,protected CvMat R,protected CvScalar fillColor,protected CvMat invK1,protected CvMat invK2,protected KernelData kernelData,protected CvMat n,protected static ThreadLocal<CvMat> pts4x1,protected double[] referencePoints1,protected double[] referencePoints2,protected CvMat t