Fixes #97: Added metadata reader for reading exif data (#2581)

* initial commit

* initial commit

* reading 3 exif directories

* minor changes

* fixed unit tests

* reviwied requested changes

* minor changes

* minor changes
This commit is contained in:
Vanshika Arora 2019-03-19 19:08:42 +05:30 committed by Ujjwal Agrawal
parent 8cd87ad148
commit 3417d713d2
5 changed files with 89 additions and 9 deletions

View file

@ -0,0 +1,60 @@
package fr.free.nrw.commons.upload;
import com.drew.imaging.ImageMetadataReader;
import com.drew.imaging.ImageProcessingException;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import java.io.File;
import java.io.IOException;
import javax.inject.Inject;
import javax.inject.Singleton;
import fr.free.nrw.commons.utils.ImageUtils;
import io.reactivex.Single;
import timber.log.Timber;
/**
* We try to avoid copyright violations in commons app.
* For doing that we read EXIF data using the library metadata-reader
* If an image doesn't have any EXIF Directoris in it's metadata then the image is an
* internet download image(and not the one taken using phone's camera) */
@Singleton
public class EXIFReader {
@Inject
public EXIFReader() {
//Empty
}
/**
* The method takes in path of the image and reads metadata using the library metadata-extractor
* And the checks for the presence of EXIF Directories in metadata object
* */
public Single<Integer> processMetadata(String path) {
Metadata readMetadata = null;
try {
readMetadata = ImageMetadataReader.readMetadata(new File(path));
} catch (ImageProcessingException e) {
Timber.d(e.toString());
} catch (IOException e) {
Timber.d(e.toString());
}
if (readMetadata != null) {
for (Directory directory : readMetadata.getDirectories()) {
// In case of internet downloaded image these three fields are not present
if (directory.getName().equals("Exif IFD0") //Contains information about the device capturing the photo
|| directory.getName().equals("Exif SubIFD") //contains information like date, time and pixels of the image
|| directory.getName().equals("Exif Thumbnail")) //contains information about image thumbnail like compression and reolution
{
Timber.d(directory.getName() + " Contains metadata");
return Single.just(ImageUtils.IMAGE_OK);
}
}
}
return Single.just(ImageUtils.FILE_NO_EXIF);
}
}

View file

@ -34,17 +34,19 @@ public class ImageProcessingService {
private final ImageUtilsWrapper imageUtilsWrapper;
private final MediaWikiApi mwApi;
private final ReadFBMD readFBMD;
private final EXIFReader EXIFReader;
@Inject
public ImageProcessingService(FileUtilsWrapper fileUtilsWrapper,
BitmapRegionDecoderWrapper bitmapRegionDecoderWrapper,
ImageUtilsWrapper imageUtilsWrapper,
MediaWikiApi mwApi, ReadFBMD readFBMD) {
MediaWikiApi mwApi, ReadFBMD readFBMD, EXIFReader EXIFReader) {
this.fileUtilsWrapper = fileUtilsWrapper;
this.bitmapRegionDecoderWrapper = bitmapRegionDecoderWrapper;
this.imageUtilsWrapper = imageUtilsWrapper;
this.mwApi = mwApi;
this.readFBMD = readFBMD;
this.EXIFReader = EXIFReader;
}
/**
@ -69,16 +71,15 @@ public class ImageProcessingService {
Single<Integer> darkImage = checkDarkImage(filePath);
Single<Integer> itemTitle = checkTitle ? validateItemTitle(uploadItem) : Single.just(ImageUtils.IMAGE_OK);
Single<Integer> checkFBMD = checkFBMD(context,contentUri);
Single<Integer> checkEXIF = checkEXIF(filePath);
Single<Integer> zipResult = Single.zip(duplicateImage, wrongGeoLocation, darkImage, itemTitle,
(duplicate, wrongGeo, dark, title) -> {
Timber.d("Result for duplicate: %d, geo: %d, dark: %d, title: %d", duplicate, wrongGeo, dark, title);
return duplicate | wrongGeo | dark | title;
});
return Single.zip(zipResult, checkFBMD, (zip, fbmd) -> {
Timber.d("zip:" + zip + "fbmd:" + fbmd);
return zip | fbmd;
return Single.zip(zipResult, checkFBMD , checkEXIF , (zip , fbmd , exif)->{
Timber.d("zip:" + zip + "fbmd:" + fbmd + "exif:" + exif);
return zip | fbmd | exif;
});
}
@ -100,6 +101,17 @@ public class ImageProcessingService {
}
}
/**
* To avoid copyright we check for EXIF data in any image.
* Images that are downloaded from internet generally don't have any EXIF data in them
* while images taken via camera or screenshots in phone have EXIF data with them.
* So we check if the image has no EXIF data then we display a warning to the user
* * */
public Single<Integer> checkEXIF(String filepath){
return EXIFReader.processMetadata(filepath);
}
/**
* Checks item title

View file

@ -55,7 +55,11 @@ public class ImageUtils {
* ie. 10000
*/
public static final int FILE_FBMD = 1 << 4;
/**
* The parameter FILE_NO_EXIF is returned from the class EXIFReader if the uploaded image does not contains EXIF data else returns IMAGE_OK
* ie. 100000
*/
public static final int FILE_NO_EXIF = 1 << 5;
public static final int IMAGE_OK = 0;
public static final int IMAGE_KEEP = -1;
public static final int IMAGE_WAIT = -2;

View file

@ -30,6 +30,8 @@ class u {
internal var mwApi: MediaWikiApi? = null
@Mock
internal var readFBMD: ReadFBMD?=null
@Mock
internal var readEXIF: EXIFReader?=null
@InjectMocks
var imageProcessingService: ImageProcessingService? = null
@ -84,6 +86,8 @@ class u {
.thenReturn(false)
`when`(readFBMD?.processMetadata(ArgumentMatchers.any(),ArgumentMatchers.any()))
.thenReturn(Single.just(ImageUtils.IMAGE_OK))
`when`(readEXIF?.processMetadata(ArgumentMatchers.anyString()))
.thenReturn(Single.just(ImageUtils.IMAGE_OK))
}
@Test