diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..f777984 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,23 @@ +name: CI + +on: + push: + branches: + - develop + pull_request: + branches: + - develop + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK + uses: actions/setup-java@v2 + with: + java-version: '8' + distribution: 'adopt' + - name: Build + run: mvn --batch-mode --update-snapshots verify diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 9e0e981..0000000 --- a/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -language: java - -jdk: - - oraclejdk8 - -notifications: - email: false - -# whitelist -branches: - only: - - master - - develop diff --git a/README.md b/README.md index d5fc6b9..c84dd3e 100755 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # JNBIS Java Implementation of NIST Biometric Image Software (NBIS) -[![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](http://www.apache.org/licenses/LICENSE-2.0) -* master [![Build Status](https://travis-ci.org/kareez/jnbis.svg?branch=master)](https://travis-ci.org/kareez/jnbis) -* develop [![Build Status](https://travis-ci.org/kareez/jnbis.svg?branch=develop)](https://travis-ci.org/kareez/jnbis) +![CI](https://github.com/mhshams/jnbis/actions/workflows/ci.yaml/badge.svg) +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.mhshams/jnbis/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.mhshams/jnbis) +[![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](http://www.apache.org/licenses/LICENSE-2.0) ### About JNBIS @@ -21,7 +21,7 @@ if you are using maven, add it to project dependencies. ```xml - jnbis + com.github.mhshams jnbis 2.x.x @@ -80,7 +80,7 @@ Nist nist = Jnbis.nist().decode("/path/to/nist/file")); Decode a NIST file with given **File** instance ```Java -Nist nist = Jnbis.nist().decode(new File("/path/to/nist/file"))); +Nist nist = Jnbis.nist().decode(new File("/path/to/nist/file")); ``` Decode a NIST file with given **InputStream** instance @@ -91,7 +91,7 @@ Nist nist = Jnbis.nist().decode(nistInputStream)); **Nist** instance contains different types of data, depending on file type. Here is a sample code that extract all fingerprints and save them in individual files. ```Java -Nist nist = Jnbis.nist().decode(new File("/path/to/nist/file"))); +Nist nist = Jnbis.nist().decode(new File("/path/to/nist/file")); for (HighResolutionGrayscaleFingerprint fp : nist.getHiResGrayscaleFingerprints()) { Jnbis.wsq() diff --git a/pom.xml b/pom.xml index e7d335d..7ec0fc3 100755 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ com.github.mhshams jnbis - 2.0.2 + 2.0.3 jar JNBIS @@ -45,8 +45,8 @@ 1.8 - 4.12 - 2.5.4 + 4.13.2 + 2.13.1 3.3 2.4 2.10.3 diff --git a/src/main/java/org/jnbis/api/model/Nist.java b/src/main/java/org/jnbis/api/model/Nist.java index e0e801c..48d0a1f 100755 --- a/src/main/java/org/jnbis/api/model/Nist.java +++ b/src/main/java/org/jnbis/api/model/Nist.java @@ -37,5 +37,7 @@ public abstract class Nist { public abstract List getVariableResPalmprints(); + public abstract List getUserDefinedTestingImages(); + public abstract List getIrisImages(); } diff --git a/src/main/java/org/jnbis/api/model/record/UserDefinedTestingImage.java b/src/main/java/org/jnbis/api/model/record/UserDefinedTestingImage.java new file mode 100644 index 0000000..ca48e4b --- /dev/null +++ b/src/main/java/org/jnbis/api/model/record/UserDefinedTestingImage.java @@ -0,0 +1,81 @@ +package org.jnbis.api.model.record; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.jnbis.internal.record.BaseImageRecord; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author TeeSofteis + */ +public class UserDefinedTestingImage extends BaseImageRecord { + + // 16.003 - 16.005 and 16.013 - 16.998 + @JsonProperty("user_defined_fields") + private Map userDefinedFields; + + public Map getUserDefinedFields() { + return userDefinedFields; + } + + public void setUserDefinedFields(Map userDefinedFields) { + this.userDefinedFields = userDefinedFields; + } + + public void addUserDefinedField(int key, String value) { + if (userDefinedFields == null) { + userDefinedFields = new HashMap<>(); + } + userDefinedFields.put(key, value); + } + + // 16.008 - SLC + @JsonProperty("scale_units") + private String scaleUnits; + + // 16.009 - HPS + @JsonProperty("horizontal_pixel_scale") + private String horizontalPixelScale; + + // 16.010 - VPS + @JsonProperty("vertical_pixel_scale") + private String verticalPixelScale; + + // 16.012 - BPX + @JsonProperty("bits_per_pixel") + private String bitsPerPixel; + + public String getScaleUnits() { + return scaleUnits; + } + + public void setScaleUnits(String scaleUnits) { + this.scaleUnits = scaleUnits; + } + + public String getHorizontalPixelScale() { + return horizontalPixelScale; + } + + public void setHorizontalPixelScale(String horizontalPixelScale) { + this.horizontalPixelScale = horizontalPixelScale; + } + + public String getVerticalPixelScale() { + return verticalPixelScale; + } + + public void setVerticalPixelScale(String verticalPixelScale) { + this.verticalPixelScale = verticalPixelScale; + } + + public String getBitsPerPixel() { + return bitsPerPixel; + } + + public void setBitsPerPixel(String bitsPerPixel) { + this.bitsPerPixel = bitsPerPixel; + } + +} diff --git a/src/main/java/org/jnbis/internal/InternalNist.java b/src/main/java/org/jnbis/internal/InternalNist.java index 00f9bea..ead77bf 100755 --- a/src/main/java/org/jnbis/internal/InternalNist.java +++ b/src/main/java/org/jnbis/internal/InternalNist.java @@ -26,6 +26,7 @@ public class InternalNist extends Nist { private final List variableResolutionLatentImages; private final List variableResolutionFingerprints; private final List variableResolutionPalmprints; + private final List userDefinedTestingImages; private final List irisImages; public InternalNist() { @@ -41,6 +42,7 @@ public InternalNist() { variableResolutionLatentImages = new ArrayList<>(); variableResolutionFingerprints = new ArrayList<>(); variableResolutionPalmprints = new ArrayList<>(); + userDefinedTestingImages = new ArrayList<>(); irisImages = new ArrayList<>(); } @@ -96,6 +98,9 @@ void addVariableResPalmprint(VariableResolutionPalmprint palmprint) { variableResolutionPalmprints.add(palmprint); } + void addUserDefinedTestingImage(UserDefinedTestingImage image) { + userDefinedTestingImages.add(image); + } void addIrisImage(IrisImage image) { irisImages.add(image); } @@ -152,6 +157,10 @@ public List getVariableResPalmprints() { return variableResolutionPalmprints; } + public List getUserDefinedTestingImages() { + return userDefinedTestingImages; + } + public List getIrisImages() { return irisImages; } diff --git a/src/main/java/org/jnbis/internal/NistDecoder.java b/src/main/java/org/jnbis/internal/NistDecoder.java index 4d033d5..f7ea02b 100755 --- a/src/main/java/org/jnbis/internal/NistDecoder.java +++ b/src/main/java/org/jnbis/internal/NistDecoder.java @@ -71,6 +71,9 @@ record = readerFactory.read(token); } else if (record instanceof VariableResolutionPalmprint) { decoded.addVariableResPalmprint((VariableResolutionPalmprint) record); + } else if (record instanceof UserDefinedTestingImage) { + decoded.addUserDefinedTestingImage((UserDefinedTestingImage) record); + } else if (record instanceof IrisImage) { decoded.addIrisImage((IrisImage) record); } diff --git a/src/main/java/org/jnbis/internal/NistHelper.java b/src/main/java/org/jnbis/internal/NistHelper.java index 7ba9fd6..67f948f 100755 --- a/src/main/java/org/jnbis/internal/NistHelper.java +++ b/src/main/java/org/jnbis/internal/NistHelper.java @@ -28,6 +28,7 @@ public class NistHelper { public static final int RT_VR_LATENT_IMAGE = 13; public static final int RT_VR_FINGERPRINT = 14; public static final int RT_VR_PALMPRINT = 15; + public static final int RT_USER_DEFINED_TESTING_IMAGE = 16; public static final int RT_IRIS_IMAGE = 17; // Information separators diff --git a/src/main/java/org/jnbis/internal/record/reader/RecordReaderFactory.java b/src/main/java/org/jnbis/internal/record/reader/RecordReaderFactory.java index 3389473..134a8be 100644 --- a/src/main/java/org/jnbis/internal/record/reader/RecordReaderFactory.java +++ b/src/main/java/org/jnbis/internal/record/reader/RecordReaderFactory.java @@ -1,6 +1,7 @@ package org.jnbis.internal.record.reader; +import org.jnbis.api.model.record.UserDefinedTestingImage; import org.jnbis.internal.NistHelper; import org.jnbis.internal.record.BaseRecord; @@ -34,7 +35,7 @@ public BaseRecord read(NistHelper.Token token) { READERS[NistHelper.RT_VR_LATENT_IMAGE] = new VariableResolutionLatentImageReader(); READERS[NistHelper.RT_VR_FINGERPRINT] = new VariableResolutionFingerprintReader(); READERS[NistHelper.RT_VR_PALMPRINT] = new VariableResolutionPalmprintReader(); - READERS[16] = NOT_SUPPORTED; + READERS[NistHelper.RT_USER_DEFINED_TESTING_IMAGE] = new UserDefinedTestingImageReader(); READERS[NistHelper.RT_IRIS_IMAGE] = new IrisImageReader(); } diff --git a/src/main/java/org/jnbis/internal/record/reader/UserDefinedTestingImageReader.java b/src/main/java/org/jnbis/internal/record/reader/UserDefinedTestingImageReader.java new file mode 100644 index 0000000..ae0022a --- /dev/null +++ b/src/main/java/org/jnbis/internal/record/reader/UserDefinedTestingImageReader.java @@ -0,0 +1,85 @@ +package org.jnbis.internal.record.reader; + +import org.jnbis.internal.NistHelper; +import org.jnbis.api.model.record.UserDefinedTestingImage; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * @author TeeSofteis + */ +public class UserDefinedTestingImageReader extends RecordReader { + @Override + public UserDefinedTestingImage read(NistHelper.Token token) { + if (token.pos >= token.buffer.length) { + throw new RuntimeException("T16::NULL pointer to T16 record"); + } + + UserDefinedTestingImage userDefinedTestingImage = new UserDefinedTestingImage(); + + int start = token.pos; + + NistHelper.Tag tag = getTagInfo(token); + if (tag.field != 1) { + throw new RuntimeException("T16::Invalid Record type = " + tag.type); + } + + Integer length = Integer.parseInt(nextWord(token, NistHelper.TAG_SEP_GSFS, NistHelper.FIELD_MAX_LENGTH - 1, false)); + userDefinedTestingImage.setLogicalRecordLength(length.toString()); + + while (true) { + token.pos++; + tag = getTagInfo(token); + + if (tag.field == 999) { + byte[] data = new byte[length - (token.pos - start)]; + System.arraycopy(token.buffer, token.pos, data, 0, data.length); + token.pos = token.pos + data.length; + userDefinedTestingImage.setImageData(data); + break; + } + + String word = nextWord(token, NistHelper.TAG_SEP_GSFS, NistHelper.FIELD_MAX_LENGTH - 1, false); + switch (tag.field) { + case 1: + userDefinedTestingImage.setLogicalRecordLength(word); + break; + case 2: + userDefinedTestingImage.setImageDesignationCharacter(word); + break; + case 6: + userDefinedTestingImage.setHorizontalLineLength(word); + break; + case 7: + userDefinedTestingImage.setVerticalLineLength(word); + break; + case 8: + userDefinedTestingImage.setScaleUnits(word); + break; + case 9: + userDefinedTestingImage.setHorizontalPixelScale(word); + break; + case 10: + userDefinedTestingImage.setVerticalPixelScale(word); + break; + case 11: + userDefinedTestingImage.setCompressionAlgorithm(word); + break; + case 12: + userDefinedTestingImage.setBitsPerPixel(word); + break; + default: + if ((2 < tag.field && tag.field < 6) || (12 < tag.field && tag.field < 999)) { + // User defined fields could be found at tag 3-5 and 13-998. As the name implies, + // it is not obvious which format the data has. From my point of view, the best + // solution is to handle the data as an array of text, therefore split data into + // sub fields and items as you need. + userDefinedTestingImage.addUserDefinedField(tag.field, word); + } + break; + } + } + return userDefinedTestingImage; + } +} \ No newline at end of file diff --git a/src/test/java/org/jnbis/Record16Test.java b/src/test/java/org/jnbis/Record16Test.java new file mode 100644 index 0000000..3f3f8d1 --- /dev/null +++ b/src/test/java/org/jnbis/Record16Test.java @@ -0,0 +1,80 @@ +package org.jnbis; + +import org.jnbis.api.Jnbis; +import org.jnbis.api.model.Nist; +import org.jnbis.api.model.record.UserDefinedTestingImage; +import org.junit.Test; + +import java.io.IOException; +import java.util.Map; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * @author TeeSofteis + */ +public class Record16Test { + private static final String[] FILES = { + "samples/nist/rec01_rec02_rec16.nst" + }; + + @Test + public void type16() { + Nist decoded = decode(FILES[0]); + commonAssert(decoded); + + // Record 01: + assertEquals("194", decoded.getTransactionInfo().getLogicalRecordLength()); // 1.001 + assertEquals("ABC", decoded.getTransactionInfo().getTypeOfTransaction()); // 1.004 + assertEquals("Wallace", decoded.getTransactionInfo().getDestinationAgencyId()); // 1.007 + assertEquals("Gormit", decoded.getTransactionInfo().getOriginatingAgencyId()); // 1.008 + + // Record 16: + assertEquals("4733", decoded.getUserDefinedTestingImages().get(0).getLogicalRecordLength()); // 16.001 + assertEquals("01", decoded.getUserDefinedTestingImages().get(0).getImageDesignationCharacter()); // 16.002 + + List rec16s = decoded.getUserDefinedTestingImages(); + assertEquals(1, rec16s.size()); // only one record + + UserDefinedTestingImage userDefinedTestingImage = rec16s.get(0); + assertEquals("4733", userDefinedTestingImage.getLogicalRecordLength()); + assertEquals("1", userDefinedTestingImage.getScaleUnits()); + assertEquals("1", userDefinedTestingImage.getHorizontalPixelScale()); + assertEquals("1", userDefinedTestingImage.getVerticalPixelScale()); + assertEquals("24", userDefinedTestingImage.getBitsPerPixel()); + + Map userDefinedFields = userDefinedTestingImage.getUserDefinedFields(); + assertEquals(4, userDefinedFields.size()); + + // Tag 16.003 + assertEquals("Wallace\u001FGromit\u001FMcGraw", userDefinedFields.get(3)); + + // Tag 16.004 + assertEquals("Shaun\u001EPreston\u001EPiella Backleicht", userDefinedFields.get(4)); + + // Tag 16.005 + assertEquals("single value", userDefinedFields.get(5)); + + // Tag 16.013 + assertEquals("A1\u001FB1\u001FC1\u001EA2\u001FB2\u001FC2\u001EA3\u001FB3\u001FC3", userDefinedFields.get(13)); + } + + private void commonAssert(Nist decoded) { + assertNotNull(decoded.getTransactionInfo()); + assertEquals(1, decoded.getUserDefinedTexts().size()); + + Map userDefinedFields = decoded.getUserDefinedTexts().get(0).getUserDefinedFields(); + assertEquals("57", userDefinedFields.get(1)); + assertEquals("00", userDefinedFields.get(2)); + assertEquals("domain defined text place holder", userDefinedFields.get(3)); + } + + private Nist decode(String name) { + String fileName = FileUtils.absolute(name); + Nist nist = Jnbis.nist().decode(fileName); + assertNotNull(nist); + return nist; + } +} diff --git a/src/test/resources/samples/nist/rec01_rec02_rec16.nst b/src/test/resources/samples/nist/rec01_rec02_rec16.nst new file mode 100644 index 0000000..032ab94 Binary files /dev/null and b/src/test/resources/samples/nist/rec01_rec02_rec16.nst differ