    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}