001package javax.visrec;
002
003import java.io.File;
004import java.io.IOException;
005import java.io.InputStream;
006import java.util.Map;
007import java.util.Objects;
008import java.util.Optional;
009import javax.visrec.ml.classification.Classifier;
010import javax.visrec.spi.ServiceProvider;
011import javax.visrec.util.Builder;
012
013/**
014 * Skeleton abstract class to make it easier to implement image classifier.
015 * It provides implementation of Classifier interface for images, along with
016 * image factory for specific type of images.
017 * This class solves the problem of using various implementation of images and machine learning models in Java,
018 * and provides standard Classifier API for clients.
019 *
020 * By default the type of key in the Map the {@link Classifier} is {@code String}
021 *
022 * @author Zoran Sevarac
023 *
024 * @param <IMAGE_CLASS> class of images
025 * @param <MODEL_CLASS> class of machine learning model
026 *
027 *
028 */
029public abstract class AbstractImageClassifier<IMAGE_CLASS, MODEL_CLASS> implements Classifier<IMAGE_CLASS, String> { // could also implement binary classifier
030
031    private ImageFactory<IMAGE_CLASS> imageFactory; // image factory impl for the specified image class
032    private MODEL_CLASS model; // the model could be injected from machine learning container?
033
034    private float threshold; // this should ba a part of every classifier
035
036    // TODO: add constructor with model instance
037    public AbstractImageClassifier(MODEL_CLASS model) {
038        setModel(model);
039    }
040
041    protected AbstractImageClassifier(final Class<IMAGE_CLASS> cls) {
042        final Optional<ImageFactory<IMAGE_CLASS>> optionalImageFactory = ServiceProvider.current()
043                .getImageFactoryService()
044                .getByImageType(cls);
045        if (!optionalImageFactory.isPresent()) {
046            throw new IllegalArgumentException(String.format("Could not find ImageFactory by '%s'", cls.getName()));
047        }
048        imageFactory = optionalImageFactory.get();
049    }
050
051    public ImageFactory<IMAGE_CLASS> getImageFactory() {
052        return imageFactory;
053    }
054
055    public Map<String, Float> classify(File file) throws IOException {
056        IMAGE_CLASS image = imageFactory.getImage(file);
057        return classify(image);
058    }
059
060    public Map<String, Float> classify(InputStream inStream) throws IOException {
061        IMAGE_CLASS image = imageFactory.getImage(inStream);
062        return classify(image);
063    }
064
065    // do we need this now, when impl is loaded using service provider?
066    // Kevin and Zoran disussed: probably not needed now when we have service provider impl, and we dont want to allow user to mess with it
067//    public void setImageFactory(ImageFactory<IMAGE_CLASS> imageFactory) {
068//        this.imageFactory = imageFactory;
069//    }
070
071    public MODEL_CLASS getModel() {
072        return model;
073    }
074
075    protected void setModel(MODEL_CLASS model) {
076        Objects.requireNonNull(model, "Model cannot bu null!");
077        this.model = model;
078    }
079
080    public float getThreshold() {
081        return threshold;
082    }
083
084    public void setThreshold(float threshold) {
085        this.threshold = threshold;
086    }
087}