/** * Copyright 2014 Comcast Cable Communications Management, LLC * * This file is part of CATS. * * CATS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * CATS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with CATS. If not, see <http://www.gnu.org/licenses/>. */ package com.comcast.cats.provider.impl; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.List; import javax.imageio.ImageIO; import javax.xml.bind.JAXBException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.comcast.cats.image.ImageRegionInfo; import com.comcast.cats.image.RegionInfo; import com.comcast.cats.image.RegionInfoXmlUtil; import com.comcast.cats.provider.RegionLocatorProvider; /** * Interface that allows users to define/store and locate regions for * ImageCompare, OCR or any other purposes. * * @author sajayjk */ public class RegionLocatorProviderImpl implements RegionLocatorProvider { /** * ******************************************************** End of legacy. * ******************************************************** */ private static final String IMAGE_FILE_FORMAT = "jpg"; private static final Logger logger = LoggerFactory .getLogger(RegionLocatorProviderImpl.class); Class<?> resourceClass = null; public RegionLocatorProviderImpl() { resourceClass = getClass(); } public RegionLocatorProviderImpl(Class<?> resourceClass) { this.resourceClass = resourceClass; } /** * Saves the RegionInfo to the xml file corresponding to the image file path. Xml is created it it doesn't exist. * * @param regionInfo - RegionInfo instance. * @param imageFilePath - The image file path * @return xml file path corresponding to the image path. * @throws IOException * @throws JAXBException */ @Override public String saveImageRegion( RegionInfo regionInfo, String imageFilePath ) throws IOException, JAXBException { if ( null == regionInfo || null == imageFilePath || imageFilePath.isEmpty() ) { throw new IllegalArgumentException( "regionInfo and imageFilePath can't be null or empty" ); } ImageRegionInfo imageRegionInfo = null; String xmlFilePath = changeExtensionToXML( imageFilePath ); File xmlFile = new File( xmlFilePath ); if ( xmlFile.exists() && xmlFile.isFile() ) { logger.debug( "xmlfile exists: " + xmlFile.getAbsolutePath() ); imageRegionInfo = loadFromXML( getClass(), xmlFilePath ); if ( null != imageRegionInfo ) { if ( null != imageRegionInfo.getRegion( regionInfo.getName() ) ) { logger.debug( "Removing already existing region: " + regionInfo.getName() ); imageRegionInfo.deleteRegion( regionInfo.getName() ); } } else { imageRegionInfo = new ImageRegionInfo(); } } else { logger.debug( "xmlfile does not exist." + xmlFilePath ); imageRegionInfo = new ImageRegionInfo(); } logger.debug( "Adding current region: " + regionInfo.getName() ); imageRegionInfo.addRegionInfo( regionInfo ); if( null == regionInfo.getFilepath() ) { regionInfo.setFilepath( new File( imageFilePath ).getName() ); } writeToXML( imageRegionInfo, xmlFilePath ); return xmlFilePath; } /** * Saves the image, the region metadata to the filepath location mentioned. * * @param regionInfoList - The list of regions. * @param refImage - the image to be saved. * @param filePath - the file location to save the data. * @throws IOException. * @throws JAXBException * */ @Override public void saveImageAndRegion(List<RegionInfo> regionInfoList, BufferedImage refImage, String filePath) throws IOException, JAXBException { if (null == refImage || null == regionInfoList || null == filePath || filePath.isEmpty()) { throw new IllegalArgumentException("Input parameter cant be null"); } ImageRegionInfo imageRegionInfo = new ImageRegionInfo(); imageRegionInfo.setRegionInfoList(regionInfoList); for (int i = 0; i < regionInfoList.size(); i++) { File file = new File(filePath); regionInfoList.get(i).setFilepath(file.getName()); } logger.debug("Writing to XML"); writeToXML(imageRegionInfo, filePath); File outputfile = new File(filePath); logger.debug("Writing to JPG :" + outputfile); ImageIO.write(refImage, IMAGE_FILE_FORMAT, outputfile); } /** * Gets the all the RegionInfo corresponding to the filepath provided. * * @param filepath - to the region metadata * @return List of RegionInfo. * @throws IOException */ @Override public List<RegionInfo> getRegionInfo(String filepath) throws IOException { if (filepath == null || filepath.isEmpty()) { throw new IllegalArgumentException( "xmlPath cannot be null or empty"); } List<RegionInfo> regionInfoList = null; ImageRegionInfo imageRegionInfo = loadFromXML(resourceClass, filepath); logger.debug("imageRegionInfo " + imageRegionInfo); if (imageRegionInfo != null) { regionInfoList = imageRegionInfo.getRegionInfoList(); } for (RegionInfo regInfo : regionInfoList) { // Jpeg images will be found in the same folder as the xml" // Set the relative path to the jpeg" String relativeJPGPath = getRelativePathForJPG(regInfo, filepath); regInfo.setFilepath(relativeJPGPath); } return regionInfoList; } /** * Gets the RegionInfo corresponding to the filepath and regionName provided. * * @param filepath - the filepath to the location of the region metadata. * @param regionName - the name of the particular region * @return - RegionInfo for the particular region. * @throws IOException */ @Override public RegionInfo getRegionInfo(String filepath, String regionName) throws IOException { if (regionName == null || filepath == null || filepath.isEmpty()) { throw new IllegalArgumentException( "xmlPath cannot be null or empty"); } RegionInfo regionInfo = null; // TODO: should this be null ImageRegionInfo imageRegionInfo = loadFromXML(getClass(), filepath); logger.debug("imageRegionInfo " + imageRegionInfo); if (imageRegionInfo != null) { regionInfo = imageRegionInfo.getRegion(regionName); if(regionInfo != null){ // Jpeg images will be found in the same folder as the xml" // Set the relative path to the jpeg" String relativeJPGPath = getRelativePathForJPG(regionInfo, filepath); regionInfo.setFilepath(relativeJPGPath); logger.debug("regionInfo " + regionInfo + " regionName " + regionName); } } return regionInfo; } /** * Populate this object with the image path and RegionInfo list. * * @param resourceClass * The Class that is in the same location as the XML resource we * will load. This is null if we load from disk or a valid class * if loading from a jar. * @param xmlPath * The xml file to use to populate this object with. This can be * a absolute path to the file or path to the resource. * @throws FileNotFoundException * if the XML document cannot be loaded from the specified path. * @throws IllegalArgumentException * if xmlPath is null or empty. * @throws RuntimeException * if a region is not defined properly in the XML file. * @return Populated ImageRegionInfo object. Null if there is an error * loading the file. */ private final ImageRegionInfo loadFromXML(Class<?> resourceClass, String xmlPath) throws FileNotFoundException { if (xmlPath == null || xmlPath.isEmpty()) { logger.error("xmlPath cannot be null or empty"); throw new IllegalArgumentException( "xmlPath cannot be null or empty"); } ImageRegionInfo imageRegInfo = null; InputStream is = getAppropriateIS(xmlPath); if (is != null) { imageRegInfo = loadFromSerializedXML(is); closInputStream(is); logger.debug("imageRegInfo " + imageRegInfo); } if (imageRegInfo == null) { throw new FileNotFoundException(xmlPath + " could not be found on disk or as resource"); } return imageRegInfo; } private InputStream getAppropriateIS(String xmlPath){ InputStream is = null; File xmlFile = new File(xmlPath); logger.debug("xmlFile " + xmlFile); if (xmlFile.isFile()) { logger.debug("loading from disk"); try { is = new FileInputStream(xmlFile); } catch ( FileNotFoundException e ) { is = null; } } else { logger.debug("loading from resource"); is = loadResource(resourceClass, xmlPath); } return is; } private void closInputStream(InputStream is){ try { is.close(); } catch (IOException e) { logger.error(e.getMessage()); } } /** * Attempts to load a resource from the specified path. * * @param resourceClass * The Class that is in the same location as the resources we * will load. * @param path * The path to load. * @return The ImputStream for the resource. null if the resource is not * found. */ private InputStream loadResource(Class<?> resourceClass, String path) { InputStream is = null; if (resourceClass != null && path != null) { is = resourceClass.getResourceAsStream(path); // this might work if the resource is in the classpath. if (is == null && !path.startsWith("/")) { is = resourceClass.getResourceAsStream("/" + path); } } return is; } /** * Loads RegionInfo from the specified input stream. This function does not * close the input stream. * * @param is * The input stream to load object from. * @return Populated RegionInfo object. null on error. */ private final ImageRegionInfo loadFromSerializedXML(InputStream inputStream) { ImageRegionInfo imageRegionInfo = null; try { imageRegionInfo = RegionInfoXmlUtil.loadFromJaxbXML(inputStream); } catch (Exception e) { logger.warn("Error loading serialized object: " + e.getMessage()); logger.warn("This may be a legacy file format."); } return imageRegionInfo; } /** * Writes this object instance to an xml file. This function will close the * output stream. This needs to happen otherwise the </java> take is not * added by the XMLEncoder. * * @param os * The output stream write to. * @throws JAXBException */ private final void writeToXML(ImageRegionInfo imageRegionInfo, String filePath) throws IOException, JAXBException { if (filePath == null || filePath.isEmpty()) { throw new IllegalArgumentException("filePath cannot be null."); } filePath = changeExtensionToXML(filePath); FileOutputStream fos = null; try { fos = new FileOutputStream(filePath); imageRegionInfo.writeToXML(fos); } catch (IOException e) { logger.error(e.getMessage()); throw e; } finally { if (null != fos) { try { fos.close(); } catch (IOException e) { logger.error(e.getMessage()); } } } } private String changeExtensionToXML(String filePath) { logger.debug("Changing extension to xml " + filePath); int dotPos = filePath.lastIndexOf("."); String filename = filePath.substring(0, dotPos); String xmlfile = filename + "." + "xml"; logger.debug("XML FIle " + xmlfile); return xmlfile; } private String getRelativePathForJPG(RegionInfo regInfo, String filepath) { String relativePath = null; if (filepath != null && regInfo != null) { File xmlFile = new File(filepath); File jpegFile = new File(regInfo.getFilepath()); if (xmlFile.getParent() == null) { relativePath = jpegFile.getName(); } else { relativePath = xmlFile.getParent() + System.getProperty("file.separator") + jpegFile.getName(); } } return relativePath; } }