package gal.udc.fic.muei.tfm.dap.flipper.service; import com.drew.imaging.ImageProcessingException; import gal.udc.fic.muei.tfm.dap.flipper.config.LireConfiguration; import gal.udc.fic.muei.tfm.dap.flipper.domain.Metadata; import gal.udc.fic.muei.tfm.dap.flipper.domain.Picture; import gal.udc.fic.muei.tfm.dap.flipper.domain.enumeration.FeatureEnumerate; import gal.udc.fic.muei.tfm.dap.flipper.repository.*; import gal.udc.fic.muei.tfm.dap.flipper.security.SecurityUtils; import gal.udc.fic.muei.tfm.dap.flipper.service.util.MetadataExtactorUtils; import gal.udc.fic.muei.tfm.dap.flipper.service.util.PictureScaleUtil; import gal.udc.fic.muei.tfm.dap.flipper.service.util.cbir.Feature; import gal.udc.fic.muei.tfm.dap.flipper.service.util.cbir.FeatureExtractorBuilder; import gal.udc.fic.muei.tfm.dap.flipper.service.util.cbir.LireBuilder; import net.semanticmetadata.lire.utils.ImageUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import javax.imageio.ImageIO; import javax.inject.Inject; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.*; @Service public class PictureService { private static final int MAX_RESULTS = 100; private static final int LIMIT_PER_WRITE = 100; private static final int MAX_WRITES = 25; private static final int CURRENT_YEAR = Calendar.getInstance().get(Calendar.YEAR); private final Logger log = LoggerFactory.getLogger(PictureService.class); @Autowired private LireConfiguration lireConfiguration; @Inject private PictureFoundRepository pictureFoundRepository; @Inject private PictureRepository pictureRepository; @Inject private GeneralCounterRepository generalCounterRepository; @Inject private UserCounterRepository userCounterRepository; @Inject private MetadataRepository metadataRepository; /** * Create new picture with title, description and file * * @param picture * @return */ public Picture create(Picture picture) throws ImageProcessingException, IOException { /* set picture date */ picture.setCreated(new Date()); /* set owner */ String user = SecurityUtils.getCurrentLogin(); picture.setOwner(user); /* increment year */ generalCounterRepository.incrementPicture(CURRENT_YEAR); /* increment user picture */ userCounterRepository.incrementPicture(user); /* Save */ pictureRepository.save(picture); /* start async process */ /* Scale images */ scaleImage(picture); /* Create all features */ createFeatures(picture); /* Create all metadata */ createMetadata(picture); return picture; } /** * Scale pictures * @param picture * @throws IOException */ @Async private void scaleImage(Picture picture) throws IOException { ByteArrayInputStream in = new ByteArrayInputStream(picture.getPictureFile().array()); BufferedImage image = ImageIO.read(in); /* little */ if (Math.max(image.getHeight(), image.getWidth()) > PictureScaleUtil.LITTLE_SIZE) { BufferedImage littleImg = ImageUtils.scaleImage(image, PictureScaleUtil.LITTLE_SIZE); picture.setLittlePictureFile(ByteBuffer.wrap(PictureScaleUtil.setBufferedImage(littleImg))); }else { picture.setLittlePictureFile(ByteBuffer.wrap(picture.getPictureFile().array())); } /* medium */ if (Math.max(image.getHeight(), image.getWidth()) > PictureScaleUtil.MEDIUM_SIZE) { BufferedImage littleImg = ImageUtils.scaleImage(image, PictureScaleUtil.MEDIUM_SIZE); picture.setMediumPictureFile(ByteBuffer.wrap(PictureScaleUtil.setBufferedImage(littleImg))); }else { picture.setMediumPictureFile(ByteBuffer.wrap(picture.getPictureFile().array())); } /* big */ if (Math.max(image.getHeight(), image.getWidth()) > PictureScaleUtil.BIG_SIZE) { BufferedImage littleImg = ImageUtils.scaleImage(image, PictureScaleUtil.BIG_SIZE); picture.setBigPictureFile(ByteBuffer.wrap(PictureScaleUtil.setBufferedImage(littleImg))); }else { picture.setBigPictureFile(ByteBuffer.wrap(picture.getPictureFile().array())); } /* update picture */ pictureRepository.save(picture); } /** * Extract and create feature for picture * * @param picture */ @Async private void createFeatures(Picture picture) throws IOException { // Index in Lucene LireBuilder.index(picture.getPictureFile().array(), picture.getId(), lireConfiguration.getConf()); Feature autocolor = FeatureExtractorBuilder.extract(picture.getPictureFile().array(), FeatureEnumerate.AutoColorCorrelogram); if(autocolor != null){ picture.setAutocolorCorrelogram(autocolor.getFeature()); picture.setAutocolorCorrelogramAsBase64(autocolor.getFeatureAsBase64()); } Feature cedd = FeatureExtractorBuilder.extract(picture.getPictureFile().array(), FeatureEnumerate.CEDD); if(cedd != null){ picture.setCedd(cedd.getFeature()); picture.setCeddAsBase64(cedd.getFeatureAsBase64()); } Feature colorHistogram = FeatureExtractorBuilder.extract(picture.getPictureFile().array(), FeatureEnumerate.ColorHistogram); if(colorHistogram != null){ picture.setColorHistogram(colorHistogram.getFeature()); picture.setColorHistogramAsBase64(colorHistogram.getFeatureAsBase64()); } Feature colorLayout = FeatureExtractorBuilder.extract(picture.getPictureFile().array(), FeatureEnumerate.ColorLayout); if(colorLayout != null){ picture.setColorLayout(colorLayout.getFeature()); picture.setColorLayoutAsBase64(colorLayout.getFeatureAsBase64()); } Feature edgeHistogram = FeatureExtractorBuilder.extract(picture.getPictureFile().array(), FeatureEnumerate.EdgeHistogram); if(edgeHistogram != null){ picture.setEdgeHistogram(edgeHistogram.getFeature()); picture.setEdgeHistogramAsBase64(edgeHistogram.getFeatureAsBase64()); } Feature phog = FeatureExtractorBuilder.extract(picture.getPictureFile().array(), FeatureEnumerate.PHOG); if(phog != null){ picture.setPhog(phog.getFeature()); picture.setPhogAsBase64(phog.getFeatureAsBase64()); } /* update picture */ pictureRepository.save(picture); } /** * Extract and create Metadata * * @param picture * @throws ImageProcessingException * @throws IOException */ @Async private void createMetadata(Picture picture) throws ImageProcessingException, IOException { Set<Metadata> metadataSet = MetadataExtactorUtils.readMetadata(picture.getPictureFile().array()); if (metadataSet.size() > LIMIT_PER_WRITE) { Set<Metadata>[] subsets = new Set[MAX_WRITES]; Metadata[] array = metadataSet.toArray(new Metadata[metadataSet.size()]); for (int i = 1; i <= MAX_WRITES; i++) { int fromIndex = (i - 1) * metadataSet.size() / MAX_WRITES; int toIndex = i * metadataSet.size() / MAX_WRITES - 1; Set<Metadata> subHashSet = new HashSet<>(); subHashSet.addAll(Arrays.asList(Arrays.copyOfRange(array, fromIndex, toIndex))); subsets[i - 1] = subHashSet; } // Avoid OutOfBand in Heap memory int writes = 0; for (Set<Metadata> subset : subsets) { metadataRepository.saveAll(subset, picture); writes++; } /* increment metadata for subsets */ generalCounterRepository.incrementMetadata(writes, CURRENT_YEAR); userCounterRepository.incrementMetadata(writes, picture.getOwner()); } else { metadataRepository.saveAll(metadataSet, picture); /* increment metadata for all metadata */ generalCounterRepository.incrementMetadata(metadataSet.size(), CURRENT_YEAR); userCounterRepository.incrementMetadata(metadataSet.size(), picture.getOwner()); } } /** * Update picture only title and description * Set modified date * * @param updated * @return */ public Picture update(Picture updated) { /* find by id */ Picture picture = pictureRepository.findOne(updated.getId()); String oldTitle = picture.getTitle(); String newTitle = updated.getTitle(); /* update data */ picture.setTitle(newTitle); picture.setDescription(updated.getDescription()); /* set picture date */ picture.setModified(new Date()); /* set modifier user */ picture.setModifiedBy(SecurityUtils.getCurrentLogin()); /* save picture */ pictureRepository.save(picture); if (oldTitle != newTitle) { updateMetadataAndFeatures(picture); } return picture; } /** * TODO perform update * @param picture */ @Async private void updateMetadataAndFeatures(Picture picture) { metadataRepository.updateAllFromPicture(picture); } /** * Delete a picture and its metadata from a given uuid * @param uuid * @throws IOException */ public void delete(UUID uuid) throws IOException { Picture picture = pictureRepository.findOne(uuid); /* delete picture */ pictureRepository.delete(picture); /* delete extra info as asyncronous method */ deleteMetadataAndLuceneIndex(uuid, picture.getOwner()); generalCounterRepository.decrementPicture(CURRENT_YEAR); userCounterRepository.decrementPicture(picture.getOwner()); } /** * Delete metadata information * @param uuid * @throws IOException */ @Async private void deleteMetadataAndLuceneIndex(UUID uuid, String user) throws IOException { /* delete from Lucene */ LireBuilder.delete(uuid, lireConfiguration.getConf()); /* decrement metadata for all metadata */ int total = metadataRepository.findByPicture_id(uuid).size(); generalCounterRepository.decrementMetadata(total, CURRENT_YEAR); userCounterRepository.decrementMetadata(total, user); /* delete all features list from picture */ metadataRepository.deleteFromPicture(uuid); } // TODO Add asynctask for every year to update counters; }