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}