public interface ImageTransformFactory {

    ImageTransform create();

} public class ImageTransformFactoryImpl implements ImageTransformFactory {

    private final ImageTransformConfigurationResource imageTransformConfigurationResource;
    private final Random random;

    @Inject
    public ImageTransformFactoryImpl(ImageTransformConfigurationResource imageTransformConfigurationResource, Random random) {
        this.imageTransformConfigurationResource = imageTransformConfigurationResource;
        this.random = random;
    }

    @Override
    public ImageTransform create() {
        int width = imageTransformConfigurationResource.getScaledWidth();
        int height = imageTransformConfigurationResource.getScaledHeight();
        return new MultiImageTransform(
                new RangeImageTransform(random),
                new NotImageTransform(random),
                new CropImageTransform(random),
                new ResizeImageTransform(random, width, height)
        );
    }
} public class RandomProvider implements Provider<Random> {

    private static final int SEED = 1337;

    @Setter
    @Inject(optional = true)
    @Named("application.seed")
    private int seed = SEED;

    @Override
    public Random get() {
        return new Random(seed);
    }
} public interface MultiLayerNetworkLoader {

    MultiLayerNetwork load();

} public interface OutputLabeler {

    String getLabel(INDArray output);

} public class MultiLayerNetworkLoaderImpl implements MultiLayerNetworkLoader {

    private final Object lock = new Object();
    private MultiLayerNetwork loadedNetwork = null;

    @Override
    public MultiLayerNetwork load() {
        if (loadedNetwork == null) {
            synchronized (lock) {
                if (loadedNetwork == null) {
                    loadedNetwork = loadInternal();
                }
            }
        }
        return loadedNetwork;
    }

    private MultiLayerNetwork loadInternal() {
        try {
            return ModelSerializer.restoreMultiLayerNetwork(getClass().getClassLoader().getResourceAsStream("model.zip"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

} public class OutputLabelerImpl implements OutputLabeler {

    private final LabelsFactory labelsFactory;

    @Inject
    public OutputLabelerImpl(LabelsFactory labelsFactory) {
        this.labelsFactory = labelsFactory;
    }

    @Override
    public String getLabel(INDArray output) {
        int maxValueIndex = getMaxValueIndex(output);
        return labelsFactory.create().get(maxValueIndex);
    }

    private static int getMaxValueIndex(INDArray indArray) {
        int maxIndex = 0;
        float maxValue = Float.MIN_VALUE;
        for (int i = 0; i < indArray.columns(); i++) {
            float value = indArray.getFloat(i);
            if (maxValue <= value) {
                maxValue = value;
                maxIndex = i;
            }
        }
        return maxIndex;
    }

} public interface BufferedImageToINDArrayConverter {

    INDArray convert(BufferedImage bufferedImage);

} public interface ByteArrayToBufferedImageConverter {

    BufferedImage convert(byte[] data);

} public class BufferedImageToINDArrayConverterImpl implements BufferedImageToINDArrayConverter {

    private final ImageTransformFactory imageTransformFactory;
    private final ImageTransformConfigurationResource imageTransformConfigurationResource;

    @Inject
    public BufferedImageToINDArrayConverterImpl(ImageTransformFactory imageTransformFactory, ImageTransformConfigurationResource imageTransformConfigurationResource) {
        this.imageTransformFactory = imageTransformFactory;
        this.imageTransformConfigurationResource = imageTransformConfigurationResource;
    }

    @Override
    public INDArray convert(BufferedImage bufferedImage) {
        try {
            ImageTransform transform = imageTransformFactory.create();
            int height = imageTransformConfigurationResource.getScaledHeight();
            int width = imageTransformConfigurationResource.getScaledWidth();
            int channels = imageTransformConfigurationResource.getChannels();
            return new NativeImageLoader(height, width, channels, transform).asMatrix(bufferedImage);
        } catch (IOException ex) {
            throw new ImageProcessingException(ex);
        }
    }
} public class ByteArrayToBufferedImageConverterImpl implements ByteArrayToBufferedImageConverter {
    @Override
    public BufferedImage convert(byte[] data) {
        try {
            return ImageIO.read(new ByteArrayInputStream(data));
        } catch (IOException e) {
            throw new ImageProcessingException(e);
        }
    }
} public interface LabelsFactory {

    List<String> create();

} public class PropertiesLabelsFactory implements LabelsFactory {

    private static final String LABELS = "2,3,4,6,7,8,9,a,c,d,e,f,g,h,j,k,l,n,p,q,r,t,u,v,x,y,z";
    private static final String LABELS_SEPARATOR = ",";

    @Setter
    @Inject
    @Named("application.labels")
    private String labels = LABELS;

    @Setter
    private String labelsSeparator = LABELS_SEPARATOR;

    @Override
    public List<String> create() {
        String[] labels = this.labels.split(labelsSeparator);
        return Arrays.asList(labels);
    }
} public class CEIDGCaptchaPredictionHandler implements Handler {

    private final CEIDGCaptchaPredictionService ceidgCaptchaPredictionService;
    private final ByteArrayToBufferedImageConverter byteArrayToBufferedImageConverter;

    @Inject
    public CEIDGCaptchaPredictionHandler(CEIDGCaptchaPredictionService ceidgCaptchaPredictionService, ByteArrayToBufferedImageConverter byteArrayToBufferedImageConverter) {
        this.ceidgCaptchaPredictionService = ceidgCaptchaPredictionService;
        this.byteArrayToBufferedImageConverter = byteArrayToBufferedImageConverter;
    }

    @Override
    public void handle(Context ctx) throws Exception {
        ctx.parse(Form.class).then(form -> {
            UploadedFile image = form.file("image");
            BufferedImage bufferedImage = byteArrayToBufferedImageConverter.convert(image.getBytes());
            String prediction = ceidgCaptchaPredictionService.predict(bufferedImage);

            CEIDGCaptchaResponseResource ceidgCaptchaResponseResource = new CEIDGCaptchaResponseResource();
            ceidgCaptchaResponseResource.setPrediction(prediction);
            ctx.render(json(ceidgCaptchaResponseResource));
        });
    }

} public interface CEIDGCaptchaPredictionService {

    String predict(BufferedImage bufferedImage);

} public class CEIDGCaptchaPredictionServiceImpl implements CEIDGCaptchaPredictionService {

    private final MultiLayerNetworkLoader multiLayerNetworkLoader;
    private final BufferedImageToINDArrayConverter bufferedImageToINDArrayConverter;
    private final OutputLabeler outputLabeler;

    @Inject
    public CEIDGCaptchaPredictionServiceImpl(MultiLayerNetworkLoader multiLayerNetworkLoader,
                                             BufferedImageToINDArrayConverter bufferedImageToINDArrayConverter,
                                             OutputLabeler outputLabeler) {
        this.multiLayerNetworkLoader = multiLayerNetworkLoader;
        this.bufferedImageToINDArrayConverter = bufferedImageToINDArrayConverter;
        this.outputLabeler = outputLabeler;
    }

    @Override
    public synchronized String predict(BufferedImage bufferedImage) {
        INDArray input = bufferedImageToINDArrayConverter.convert(bufferedImage);
        MultiLayerNetwork network = multiLayerNetworkLoader.load();
        INDArray output = network.output(input);
        return outputLabeler.getLabel(output);
    }

}
import com.google.inject.AbstractModule;
import net.scaliby.ceidgcaptcha.machinelearning.factory.ImageTransformFactory;
import net.scaliby.ceidgcaptcha.machinelearning.factory.impl.ImageTransformFactoryImpl;
import net.scaliby.ceidgcaptcha.machinelearning.provider.RandomProvider;
import net.scaliby.ceidgcaptcha.rest.common.MultiLayerNetworkLoader;
import net.scaliby.ceidgcaptcha.rest.common.OutputLabeler;
import net.scaliby.ceidgcaptcha.rest.common.impl.MultiLayerNetworkLoaderImpl;
import net.scaliby.ceidgcaptcha.rest.common.impl.OutputLabelerImpl;
import net.scaliby.ceidgcaptcha.rest.converter.BufferedImageToINDArrayConverter;
import net.scaliby.ceidgcaptcha.rest.converter.ByteArrayToBufferedImageConverter;
import net.scaliby.ceidgcaptcha.rest.converter.impl.BufferedImageToINDArrayConverterImpl;
import net.scaliby.ceidgcaptcha.rest.converter.impl.ByteArrayToBufferedImageConverterImpl;
import net.scaliby.ceidgcaptcha.rest.factory.LabelsFactory;
import net.scaliby.ceidgcaptcha.rest.factory.impl.PropertiesLabelsFactory;
import net.scaliby.ceidgcaptcha.rest.handler.CEIDGCaptchaPredictionHandler;
import net.scaliby.ceidgcaptcha.rest.service.CEIDGCaptchaPredictionService;
import net.scaliby.ceidgcaptcha.rest.service.impl.CEIDGCaptchaPredictionServiceImpl;
import java.util.Random;
package net.scaliby.ceidgcaptcha.rest;



public class CEIDGCaptchaRestModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(Random.class).toProvider(RandomProvider.class);