Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

♻️ Refactor code on Image Handling and Integrate OpenCV Java #464

Merged
merged 8 commits into from
Jul 9, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions classifai-api/src/main/java/ai/classifai/ClassifaiApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ public static void main(String[] args)
//initiate to run cli arguments
new CLIArgument(args);

// Load native library to implement OpenCV Java
nu.pattern.OpenCV.loadLocally();

VertxOptions vertxOptions = new VertxOptions();

vertxOptions.setMaxEventLoopExecuteTimeUnit(TimeUnit.SECONDS);
Expand Down
4 changes: 4 additions & 0 deletions classifai-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@
<groupId>com.drewnoakes</groupId>
<artifactId>metadata-extractor</artifactId>
</dependency>
<dependency>
<groupId>org.openpnp</groupId>
<artifactId>opencv</artifactId>
</dependency>
</dependencies>


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,66 @@
*/
public class BmpImageData extends ImageData
{
public BmpImageData(Metadata metadata)
{
super(metadata);
private final int S_RGB_COLOR_SPACE = 1934772034;

protected BmpImageData(Metadata metadata) {
super(metadata, BmpHeaderDirectory.class);
}

@Override
public int getWidth() throws MetadataException
public int getOrientation() {
// same as original image
return 1;
}

@Override
public int getWidth()
{
return metadata.getFirstDirectoryOfType(BmpHeaderDirectory.class).getInt(BmpHeaderDirectory.TAG_IMAGE_WIDTH);
try
{
return directory.getInt(BmpHeaderDirectory.TAG_IMAGE_WIDTH);
}
catch (MetadataException e)
{
logMetadataError();
return 0;
}
}

@Override
public int getHeight() throws MetadataException
public int getHeight()
{
return metadata.getFirstDirectoryOfType(BmpHeaderDirectory.class).getInt(BmpHeaderDirectory.TAG_IMAGE_HEIGHT);
try {
return directory.getInt(BmpHeaderDirectory.TAG_IMAGE_HEIGHT);
}
catch (MetadataException e)
{
logMetadataError();
return 0;
}
}

/**
* color space type: [1934772034: sRGB, null: BnW]
* @return number of channels 1 or 3
*/
@Override
public int getDepth() {
try {
int colorSpaceType = directory.getInt(BmpHeaderDirectory.TAG_COLOR_SPACE_TYPE);
if (colorSpaceType == S_RGB_COLOR_SPACE)
{
return 3;
}
}
catch (MetadataException ignored){}

return 1;
}

@Override
public String getMimeType() {
return "image/bmp";
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,110 @@
*/
package ai.classifai.data.type.image;

import ai.classifai.util.exception.NotSupportedImageTypeException;
import com.drew.imaging.ImageMetadataReader;
import com.drew.imaging.ImageProcessingException;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.MetadataException;
import com.drew.metadata.bmp.BmpHeaderDirectory;
import com.drew.metadata.jpeg.JpegDirectory;
import com.drew.metadata.png.PngDirectory;
import com.drew.metadata.webp.WebpDirectory;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;

/**
* ImageData provides metadata of images
*
* @author YCCertifai
*/
@Slf4j
public abstract class ImageData
{
@Getter
protected Metadata metadata;
protected Directory directory;

protected ImageData(Metadata metadata)
{
protected <T extends Directory> ImageData(Metadata metadata, Class<T> directoryClass) {
this.metadata = metadata;
this.directory = metadata.getFirstDirectoryOfType(directoryClass);
}

public abstract int getOrientation();

public abstract int getWidth();

public abstract int getHeight();

public abstract int getDepth();

public abstract String getMimeType();

protected void logMetadataError() {
log.error("Unhandled metadata error, this should be protected by ImageFactory");
}

public static ImageData getImageData(File filePath)
{
try
{
Metadata metadata = ImageMetadataReader.readMetadata(filePath);

return ImageDataFactory.getImageData(metadata);
}
catch (ImageProcessingException | IOException e)
{
log.debug(String.format("%s is not an image", filePath));
return null;
}
catch (NotSupportedImageTypeException e)
{
log.debug(String.format("%s is not supported \n %s", filePath, e.getMessage()));
return null;
}
}

public static ImageData getImageData(byte [] bytes){
try
{
Metadata metadata = ImageMetadataReader.readMetadata(new ByteArrayInputStream(bytes));

return ImageDataFactory.getImageData(metadata);
}
catch (ImageProcessingException | IOException e)
{
log.debug("byte array received is not an image");
return null;
}
catch (NotSupportedImageTypeException e)
{
log.debug("byte array received is not supported");
return null;
}

}

public static class ImageDataFactory {
private ImageDataFactory() {
}

public static ImageData getImageData(Metadata metadata) throws NotSupportedImageTypeException {
if (metadata.containsDirectoryOfType(JpegDirectory.class)) {
return new JpegImageData(metadata);
} else if (metadata.containsDirectoryOfType(PngDirectory.class)) {
return new PngImageData(metadata);
} else if (metadata.containsDirectoryOfType(BmpHeaderDirectory.class)) {
return new BmpImageData(metadata);
} else if (metadata.containsDirectoryOfType(WebpDirectory.class)) {
return new WebpImageData(metadata);
} else {
yipkhenlai marked this conversation as resolved.
Show resolved Hide resolved
throw new NotSupportedImageTypeException(String.format("%s type not supported", metadata.getDirectories()));
}
}
}

public abstract int getWidth() throws MetadataException;
public abstract int getHeight() throws MetadataException;
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class ImageFileType {

private static final Map BASE_64_HEADER;

private static final String[] ALLOWED_FILE_TYPES = new String[]{"jpg", "png", "jpeg", "bmp", "JPG", "PNG", "JPEG"}; //{"jpg", "png", "jpeg", "pdf", "bmp", "JPG", "PNG", "JPEG"};
private static final String[] ALLOWED_FILE_TYPES = new String[]{"jpg", "png", "jpeg", "bmp", "JPG", "PNG", "JPEG", "webp"}; //{"jpg", "png", "jpeg", "pdf", "bmp", "JPG", "PNG", "JPEG"};

static
{
Expand All @@ -49,6 +49,7 @@ public class ImageFileType {
BASE_64_HEADER.put("png", "data:image/jpeg;base64,");
BASE_64_HEADER.put("PNG", "data:image/png;base64,");
BASE_64_HEADER.put("bmp", "data:image/bmp;base64,");
BASE_64_HEADER.put("webp", "data:image/bmp;base64,");
}

public static String[] getImageFileTypes()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import com.drew.metadata.Metadata;
import com.drew.metadata.MetadataException;
import com.drew.metadata.exif.ExifIFD0Directory;
import com.drew.metadata.jpeg.JpegDirectory;

/**
Expand All @@ -26,18 +27,89 @@
*/
public class JpegImageData extends ImageData
{
public JpegImageData(Metadata metadata)
{
super(metadata);
protected JpegImageData(Metadata metadata) {
super(metadata, JpegDirectory.class);
}

private int getRawWidth() {
try {
return metadata.getFirstDirectoryOfType(JpegDirectory.class).getInt(JpegDirectory.TAG_IMAGE_WIDTH);
} catch (MetadataException e) {
logMetadataError();
return 0;
}
}

private int getRawHeight() {
try {
return metadata.getFirstDirectoryOfType(JpegDirectory.class).getInt(JpegDirectory.TAG_IMAGE_HEIGHT);
} catch (MetadataException e) {
logMetadataError();
return 0;
}
}

/**
* get Exif orientation from metatdata
* orientation value: [1: 0 deg, 8: 270 deg, 3: 180 deg, 6: 90 deg]
* ref: https://www.impulseadventure.com/photo/exif-orientation.html
*
* 1 = 0 degree (Horizontal, normal)
* 2 = 0 degree,mirrored (Mirror horizontally)
* 3 = 180 degree (Rotate 180 degree)
* 4 = 180 degree,mirrored (Mirror vertically)
* 5 = 90 degree, mirrored (Mirror horizontal and rotate 270 degree clockwise)
* 6 = 90 degree CW (Rotate 90 degree clockwise)
* 7 = 270 degree, mirrored (Mirror horizontal and rotate 90 degree clockwise)
* 8 = 270 degree CW (Rotate 270 degree clockwise)
*
* @return orientation
*/

@Override
public int getWidth() throws MetadataException {
return metadata.getFirstDirectoryOfType(JpegDirectory.class).getInt(JpegDirectory.TAG_IMAGE_WIDTH);
public int getOrientation() {
try {
return metadata.getFirstDirectoryOfType(ExifIFD0Directory.class).getInt(ExifIFD0Directory.TAG_ORIENTATION);
} catch (Exception ignored) {
// if can't find orientation set as 0 deg
return 1;
}
}

@Override
public int getHeight() throws MetadataException {
return metadata.getFirstDirectoryOfType(JpegDirectory.class).getInt(JpegDirectory.TAG_IMAGE_HEIGHT);
public int getWidth() {
int orientation = getOrientation();

if (orientation == 8 || orientation == 6) {
return getRawHeight();
}

return getRawWidth();
}

@Override
public int getHeight() {
int orientation = getOrientation();

if (orientation == 8 || orientation == 6) {
return getRawWidth();
}

return getRawHeight();
}

@Override
public int getDepth() {
try {
return directory.getInt(JpegDirectory.TAG_NUMBER_OF_COMPONENTS);
} catch (Exception ignored) {
return 3;
}
}

@Override
public String getMimeType() {
return "image/jpg";
}

}
Loading