Java tutorial
/* * * Apache License * Version 2.0, January 2004 * http://www.apache.org/licenses/ * * TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION * * 1. Definitions. * * "License" shall mean the terms and conditions for use, reproduction, * and distribution as defined by Sections 1 through 9 of this document. * * "Licensor" shall mean the copyright owner or entity authorized by * the copyright owner that is granting the License. * * "Legal Entity" shall mean the union of the acting entity and all * other entities that control, are controlled by, or are under common * control with that entity. For the purposes of this definition, * "control" means (i) the power, direct or indirect, to cause the * direction or management of such entity, whether by contract or * otherwise, or (ii) ownership of fifty percent (50%) or more of the * outstanding shares, or (iii) beneficial ownership of such entity. * * "You" (or "Your") shall mean an individual or Legal Entity * exercising permissions granted by this License. * * "Source" form shall mean the preferred form for making modifications, * including but not limited to software source code, documentation * source, and configuration files. * * "Object" form shall mean any form resulting from mechanical * transformation or translation of a Source form, including but * not limited to compiled object code, generated documentation, * and conversions to other media types. * * "Work" shall mean the work of authorship, whether in Source or * Object form, made available under the License, as indicated by a * copyright notice that is included in or attached to the work * (an example is provided in the Appendix below). * * "Derivative Works" shall mean any work, whether in Source or Object * form, that is based on (or derived from) the Work and for which the * editorial revisions, annotations, elaborations, or other modifications * represent, as a whole, an original work of authorship. For the purposes * of this License, Derivative Works shall not include works that remain * separable from, or merely link (or bind by name) to the interfaces of, * the Work and Derivative Works thereof. * * "Contribution" shall mean any work of authorship, including * the original version of the Work and any modifications or additions * to that Work or Derivative Works thereof, that is intentionally * submitted to Licensor for inclusion in the Work by the copyright owner * or by an individual or Legal Entity authorized to submit on behalf of * the copyright owner. For the purposes of this definition, "submitted" * means any form of electronic, verbal, or written communication sent * to the Licensor or its representatives, including but not limited to * communication on electronic mailing lists, source code control systems, * and issue tracking systems that are managed by, or on behalf of, the * Licensor for the purpose of discussing and improving the Work, but * excluding communication that is conspicuously marked or otherwise * designated in writing by the copyright owner as "Not a Contribution." * * "Contributor" shall mean Licensor and any individual or Legal Entity * on behalf of whom a Contribution has been received by Licensor and * subsequently incorporated within the Work. * * 2. Grant of Copyright License. Subject to the terms and conditions of * this License, each Contributor hereby grants to You a perpetual, * worldwide, non-exclusive, no-charge, royalty-free, irrevocable * copyright license to reproduce, prepare Derivative Works of, * publicly display, publicly perform, sublicense, and distribute the * Work and such Derivative Works in Source or Object form. * * 3. Grant of Patent License. Subject to the terms and conditions of * this License, each Contributor hereby grants to You a perpetual, * worldwide, non-exclusive, no-charge, royalty-free, irrevocable * (except as stated in this section) patent license to make, have made, * use, offer to sell, sell, import, and otherwise transfer the Work, * where such license applies only to those patent claims licensable * by such Contributor that are necessarily infringed by their * Contribution(s) alone or by combination of their Contribution(s) * with the Work to which such Contribution(s) was submitted. If You * institute patent litigation against any entity (including a * cross-claim or counterclaim in a lawsuit) alleging that the Work * or a Contribution incorporated within the Work constitutes direct * or contributory patent infringement, then any patent licenses * granted to You under this License for that Work shall terminate * as of the date such litigation is filed. * * 4. Redistribution. You may reproduce and distribute copies of the * Work or Derivative Works thereof in any medium, with or without * modifications, and in Source or Object form, provided that You * meet the following conditions: * * (a) You must give any other recipients of the Work or * Derivative Works a copy of this License; and * * (b) You must cause any modified files to carry prominent notices * stating that You changed the files; and * * (c) You must retain, in the Source form of any Derivative Works * that You distribute, all copyright, patent, trademark, and * attribution notices from the Source form of the Work, * excluding those notices that do not pertain to any part of * the Derivative Works; and * * (d) If the Work includes a "NOTICE" text file as part of its * distribution, then any Derivative Works that You distribute must * include a readable copy of the attribution notices contained * within such NOTICE file, excluding those notices that do not * pertain to any part of the Derivative Works, in at least one * of the following places: within a NOTICE text file distributed * as part of the Derivative Works; within the Source form or * documentation, if provided along with the Derivative Works; or, * within a display generated by the Derivative Works, if and * wherever such third-party notices normally appear. The contents * of the NOTICE file are for informational purposes only and * do not modify the License. You may add Your own attribution * notices within Derivative Works that You distribute, alongside * or as an addendum to the NOTICE text from the Work, provided * that such additional attribution notices cannot be construed * as modifying the License. * * You may add Your own copyright statement to Your modifications and * may provide additional or different license terms and conditions * for use, reproduction, or distribution of Your modifications, or * for any such Derivative Works as a whole, provided Your use, * reproduction, and distribution of the Work otherwise complies with * the conditions stated in this License. * * 5. Submission of Contributions. Unless You explicitly state otherwise, * any Contribution intentionally submitted for inclusion in the Work * by You to the Licensor shall be under the terms and conditions of * this License, without any additional terms or conditions. * Notwithstanding the above, nothing herein shall supersede or modify * the terms of any separate license agreement you may have executed * with Licensor regarding such Contributions. * * 6. Trademarks. This License does not grant permission to use the trade * names, trademarks, service marks, or product names of the Licensor, * except as required for reasonable and customary use in describing the * origin of the Work and reproducing the content of the NOTICE file. * * 7. Disclaimer of Warranty. Unless required by applicable law or * agreed to in writing, Licensor provides the Work (and each * Contributor provides its Contributions) on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied, including, without limitation, any warranties or conditions * of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A * PARTICULAR PURPOSE. You are solely responsible for determining the * appropriateness of using or redistributing the Work and assume any * risks associated with Your exercise of permissions under this License. * * 8. Limitation of Liability. In no event and under no legal theory, * whether in tort (including negligence), contract, or otherwise, * unless required by applicable law (such as deliberate and grossly * negligent acts) or agreed to in writing, shall any Contributor be * liable to You for damages, including any direct, indirect, special, * incidental, or consequential damages of any character arising as a * result of this License or out of the use or inability to use the * Work (including but not limited to damages for loss of goodwill, * work stoppage, computer failure or malfunction, or any and all * other commercial damages or losses), even if such Contributor * has been advised of the possibility of such damages. * * 9. Accepting Warranty or Additional Liability. While redistributing * the Work or Derivative Works thereof, You may choose to offer, * and charge a fee for, acceptance of support, warranty, indemnity, * or other liability obligations and/or rights consistent with this * License. However, in accepting such obligations, You may act only * on Your own behalf and on Your sole responsibility, not on behalf * of any other Contributor, and only if You agree to indemnify, * defend, and hold each Contributor harmless for any liability * incurred by, or claims asserted against, such Contributor by reason * of your accepting any such warranty or additional liability. * * END OF TERMS AND CONDITIONS * * APPENDIX: How to apply the Apache License to your work. * * To apply the Apache License to your work, attach the following * boilerplate notice, with the fields enclosed by brackets "[]" * replaced with your own identifying information. (Don't include * the brackets!) The text should be enclosed in the appropriate * comment syntax for the file format. We also recommend that a * file or class name and description of purpose be included on the * same "printed page" as the copyright notice for easier * identification within third-party archives. * * Copyright 2016 Ian Kelly * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package cognitivej.vision.overlay.builder; import cognitivej.core.Utils; import cognitivej.core.error.exceptions.CognitiveException; import cognitivej.vision.computervision.ComputerVisionString; import cognitivej.vision.computervision.ImageDescription; import cognitivej.vision.computervision.OCRResult; import cognitivej.vision.emotion.Emotion; import cognitivej.vision.emotion.EmotionStringBuilder; import cognitivej.vision.face.FaceStringBuilder; import cognitivej.vision.face.scenario.IdentificationSet; import cognitivej.vision.face.scenario.VerificationSet; import cognitivej.vision.face.task.Face; import cognitivej.vision.face.task.FaceAttributes; import cognitivej.vision.face.task.Verification; import cognitivej.vision.overlay.*; import cognitivej.vision.overlay.filter.*; import org.apache.commons.lang3.text.WordUtils; import org.jetbrains.annotations.NotNull; import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** * Utility class to overlay various layers on the base image and to manipulate/enhance the base image */ public class ImageOverlayBuilder { public static final BorderWeight DEFAULT_BORDER_WEIGHT = BorderWeight.THIN; public static final Font DEFAULT_TEXT_FONT = new Font("Noto Sans", Font.PLAIN, 40); public static final Insets SMALL_PADDING = new Insets(10, 10, 10, 10); private BufferedImage bufferedImage; private ImageOverlayBuilder(@NotNull BufferedImage bufferedImage) { this.bufferedImage = bufferedImage; try { GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); ge.registerFont(Font.createFont(Font.TRUETYPE_FONT, new File("src/main/resources/font/notosans/NotoSans-Bold.ttf"))); } catch (IOException | FontFormatException ignored) { } } /** * Outlines a face on the base image with defaults; * ({@link ImageOverlayBuilder#outlineFaceOnImage(Face, RectangleType, BorderWeight, CognitiveJColourPalette)} * * @param face - the face. * @return this */ @NotNull public ImageOverlayBuilder outlineFaceOnImage(@NotNull Face face) { return outlineFaceOnImage(face, RectangleType.CORNERED); } /** * Draws outlines on any OCR text. * * @param ocrResult the result to paint. * @return this */ @NotNull public ImageOverlayBuilder ocrImage(@NotNull OCRResult ocrResult) { ocrResult.regions.forEach(this::outlineOCRRegion); return this; } /** * outlines a region with text. * * @param region the region * @return this */ @NotNull private ImageOverlayBuilder outlineOCRRegion(@NotNull OCRResult.Region region) { region.lines.forEach(this::outlineOCRLine); return this; } @NotNull private ImageOverlayBuilder outlineOCRLine(OCRResult.Line line) { OverlayRectangleFilter overlayRectangleFilter = new OverlayRectangleFilter(line.boundingBoxAsAwtRectangle(), RectangleType.FULL, BorderWeight.THINNER, CognitiveJColourPalette.randomColour()); bufferedImage = overlayRectangleFilter.applyFilter(bufferedImage); line.words.forEach(this::outlineOCRWord); return this; } @NotNull private ImageOverlayBuilder outlineOCRWord(@NotNull OCRResult.Word word) { OverlayRectangleFilter overlayRectangleFilter = new OverlayRectangleFilter(word.boundingBoxAsAwtRectangle(), RectangleType.FULL, BorderWeight.THINNER, CognitiveJColourPalette.randomColour()); TextOnRectangleFilter textOnRectangleFilter = new TextOnRectangleFilter( new Rectangle(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight()), word.boundingBoxAsAwtRectangle(), new Insets(0, 0, 0, 0), new Font("Noto Sans", Font.PLAIN, 12), CognitiveJColourPalette.TRANS_GRAY, RectangleTextPosition.BOTTOM_OF, word.text); bufferedImage = textOnRectangleFilter.applyFilter(overlayRectangleFilter.applyFilter(bufferedImage)); return this; } /** * Outlines a face on the base image with defaults; * ({@link ImageOverlayBuilder#outlineFaceOnImage(Face, RectangleType, BorderWeight, CognitiveJColourPalette)} * * @param face - the face. * @return this */ @NotNull public ImageOverlayBuilder pixelateFaceOnImage(@NotNull Face face) { PixelatedImageSectionFilter pixelatedImageSectionFilter = new PixelatedImageSectionFilter( face.faceRectangle.asAwtRectangle()); bufferedImage = pixelatedImageSectionFilter.applyFilter(bufferedImage); return this; } /** * Outlines face(s) on the base image with defaults; * ({@link ImageOverlayBuilder#outlineFaceOnImage(Face, RectangleType, BorderWeight, CognitiveJColourPalette)} * * @param faces the faces to outline. * @return this */ @NotNull public ImageOverlayBuilder outlineFacesOnImage(@NotNull List<Face> faces) { faces.forEach(face -> outlineFaceOnImage(face, RectangleType.FULL, DEFAULT_BORDER_WEIGHT, CognitiveJColourPalette.randomColour())); return this; } /** * Outlines face(s) on the base image with defaults; * ({@link ImageOverlayBuilder#outlineFaceOnImage(Face, RectangleType, BorderWeight, CognitiveJColourPalette)} * * @param faces the faces. * @param rectangleType the type of border * @param colourPalette the colour of the border * @return this */ @NotNull public ImageOverlayBuilder outlineFacesOnImage(@NotNull List<Face> faces, @NotNull RectangleType rectangleType, @NotNull CognitiveJColourPalette colourPalette) { faces.forEach(face -> outlineFaceOnImage(face, rectangleType, DEFAULT_BORDER_WEIGHT, colourPalette)); return this; } /** * Outlines face on the base image with defaults; * ({@link ImageOverlayBuilder#outlineFaceOnImage(Face, RectangleType, BorderWeight, CognitiveJColourPalette)} * * @param face - the faces. * @param rectangleType - the rectangle type. * @return this */ @NotNull public ImageOverlayBuilder outlineFaceOnImage(@NotNull Face face, @NotNull RectangleType rectangleType) { return outlineFaceOnImage(face, rectangleType, DEFAULT_BORDER_WEIGHT); } /** * Outlines face on the base image with defaults; * ({@link ImageOverlayBuilder#outlineFaceOnImage(Face, RectangleType, BorderWeight, CognitiveJColourPalette)} * * @param face - the faces. * @param rectangleType - the rectangle type. * @param borderWeight - the weight of the border * @return this */ @NotNull public ImageOverlayBuilder outlineFaceOnImage(@NotNull Face face, @NotNull RectangleType rectangleType, @NotNull BorderWeight borderWeight) { return outlineFaceOnImage(face, rectangleType, borderWeight, CognitiveJColourPalette.randomColour()); } @NotNull public ImageOverlayBuilder outlineFaceOnImage(@NotNull Face face, @NotNull RectangleType rectangleType, @NotNull BorderWeight borderWeight, @NotNull CognitiveJColourPalette colourPalette) { OverlayRectangleFilter overlayRectangleFilter = new OverlayRectangleFilter( face.faceRectangle.asAwtRectangle(), rectangleType, borderWeight, colourPalette); bufferedImage = overlayRectangleFilter.applyFilter(bufferedImage); return this; } /** * write out the faces age (formatted 'Age %.1f') to the top of the face rectangle. * ({@link ImageOverlayBuilder#outlineFaceOnImage(Face, RectangleType, BorderWeight, CognitiveJColourPalette)} * * @param face - the face. * @return this */ @NotNull public ImageOverlayBuilder writeAge(@NotNull Face face) { return writeAge(face, RectangleTextPosition.TOP_OF); } /** * write out the faces age (formatted 'Age %.1f') to the top of the face rectangle. * ({@link ImageOverlayBuilder#outlineFaceOnImage(Face, RectangleType, BorderWeight, CognitiveJColourPalette)} * * @param face the face. * @param textPosition location of the text boundary * @return this */ @NotNull public ImageOverlayBuilder writeAge(@NotNull Face face, @NotNull RectangleTextPosition textPosition) { return writeAge(face, CognitiveJColourPalette.randomColour(), textPosition); } /** * write out the faces age (formatted 'Age %.1f') to the top of the face rectangle. * ({@link ImageOverlayBuilder#outlineFaceOnImage(Face, RectangleType, BorderWeight, CognitiveJColourPalette)} * * @param face the face. * @param colourPalette the colour to use * @param textPosition location of the text boundary * @return this */ @NotNull public ImageOverlayBuilder writeAge(@NotNull Face face, @NotNull CognitiveJColourPalette colourPalette, @NotNull RectangleTextPosition textPosition) { TextOnRectangleFilter textOnRectangleFilter = new TextOnRectangleFilter( new Rectangle(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight()), face.faceRectangle.asAwtRectangle(), new Insets(0, 0, 0, 0), DEFAULT_TEXT_FONT, colourPalette, textPosition, String.format("Age %.2f", face.faceAttributesResp.age)); bufferedImage = textOnRectangleFilter.applyFilter(bufferedImage); return this; } /** * write out the faces gender and age (formatted '%Gender, Age %.1f') to the top of the face rectangle. * ({@link ImageOverlayBuilder#outlineFaceOnImage(Face, RectangleType, BorderWeight, CognitiveJColourPalette)} * * @param face - the face. * @return this */ @NotNull public ImageOverlayBuilder writeGenderAndAge(@NotNull Face face) { return writeGenderAndAge(face, CognitiveJColourPalette.randomColour()); } /** * write out the faces gender and age (formatted '%Gender, Age %.1f') to the top of the face rectangle. * ({@link ImageOverlayBuilder#outlineFaceOnImage(Face, RectangleType, BorderWeight, CognitiveJColourPalette)} * * @param face the face. * @param colourPalette the color to paint the text * @return this */ @NotNull public ImageOverlayBuilder writeGenderAndAge(@NotNull Face face, @NotNull CognitiveJColourPalette colourPalette) { return writeGenderAndAge(face, colourPalette, DEFAULT_BORDER_WEIGHT); } /** * write out the faces gender and age (formatted '%Gender, Age %.1f') to the top of the face rectangle. * ({@link ImageOverlayBuilder#outlineFaceOnImage(Face, RectangleType, BorderWeight, CognitiveJColourPalette)} * * @param face the face. * @param colourPalette the color to paint the text * @param borderWeight borderWeight to use * @return this */ @NotNull public ImageOverlayBuilder writeGenderAndAge(@NotNull Face face, @NotNull CognitiveJColourPalette colourPalette, @NotNull BorderWeight borderWeight) { return writeGenderAndAge(face, colourPalette, borderWeight.insets()); } /** * write out the faces gender and age (formatted '%Gender, Age %.1f') * ({@link ImageOverlayBuilder#outlineFaceOnImage(Face, RectangleType, BorderWeight, CognitiveJColourPalette)} * * @param face the face. * @param colourPalette the colors to use * @param insets any Insets to take into consideration(e.g. border thickness). * @return this */ public ImageOverlayBuilder writeGenderAndAge(Face face, CognitiveJColourPalette colourPalette, @NotNull Insets insets) { return writeGenderAndAge(face, colourPalette, insets, RectangleTextPosition.TOP_OF); } /** * write out the faces gender and age (formatted '%Gender, Age %.1f') * ({@link ImageOverlayBuilder#outlineFaceOnImage(Face, RectangleType, BorderWeight, CognitiveJColourPalette)} * * @param face the face. * @param colourPalette the colors to use * @param insets any Insets to take into consideration(e.g. border thickness). * @param textPosition where to position the text container * @return this */ public ImageOverlayBuilder writeGenderAndAge(Face face, CognitiveJColourPalette colourPalette, @NotNull Insets insets, RectangleTextPosition textPosition) { TextOnRectangleFilter textOnRectangleFilter = new TextOnRectangleFilter( new Rectangle(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight()), face.faceRectangle.asAwtRectangle(), insets, DEFAULT_TEXT_FONT, colourPalette, textPosition, String.format("%s, Age %.1f", WordUtils.capitalize(face.faceAttributesResp.gender.name()), face.faceAttributesResp.age)); bufferedImage = textOnRectangleFilter.applyFilter(bufferedImage); return this; } /** * write out the faces' {@link FaceAttributes} * * @param colourPalette the colour options for the text * @param faces - the faces to write attributes for. * @return this */ public ImageOverlayBuilder writeFaceAttributes(@NotNull List<Face> faces, @NotNull CognitiveJColourPalette colourPalette) { return writeFaceAttributes(faces, colourPalette, RectangleTextPosition.RIGHT_OF); } /** * write out the faces' {@link FaceAttributes} * * @param colourPalette the colour options for the text * @param textPosition Where to position text * @param faces the faces to write attributes for. * @return this */ public ImageOverlayBuilder writeFaceAttributes(@NotNull List<Face> faces, @NotNull CognitiveJColourPalette colourPalette, @NotNull RectangleTextPosition textPosition) { faces.forEach(face -> writeFaceAttributes(face, colourPalette, textPosition)); return this; } /** * write out the faces' {@link FaceAttributes} * * @param face - the face. * @return this */ @NotNull public ImageOverlayBuilder writeFaceAttributes(@NotNull Face face) { return writeFaceAttributes(face, CognitiveJColourPalette.GRAY); } /** * write out the faces' {@link FaceAttributes} * * @param face the face. * @param colourPalette the colour to use * @return this */ @NotNull public ImageOverlayBuilder writeFaceAttributes(@NotNull Face face, @NotNull CognitiveJColourPalette colourPalette) { return writeFaceAttributes(face, colourPalette, RectangleTextPosition.RIGHT_OF); } /** * write out the faces' {@link FaceAttributes} * * @param face the face. * @param colourPalette the colour to use * @param textPosition position to locate the text * @return this */ @NotNull public ImageOverlayBuilder writeFaceAttributes(@NotNull Face face, @NotNull CognitiveJColourPalette colourPalette, RectangleTextPosition textPosition) { TextOnRectangleFilter textOnRectangleFilter = new TextOnRectangleFilter( new Rectangle(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight()), face.faceRectangle.asAwtRectangle(), new Insets(0, 0, 0, 0), DEFAULT_TEXT_FONT, colourPalette, textPosition, FaceStringBuilder.buildStringFor(face, FaceAttributes.ALL)); bufferedImage = textOnRectangleFilter.applyFilter(bufferedImage); return this; } @NotNull public ImageOverlayBuilder outFaceLandmarksOnImage(@NotNull Face face) { return outFaceLandmarksOnImage(face, BorderWeight.THICK); } @NotNull public ImageOverlayBuilder outFaceLandmarksOnImage(@NotNull Face face, @NotNull BorderWeight landmarkWeight) { java.util.List<java.awt.Point> values = face.faceLandmarks.landmarks().values().stream() .map(point -> point != null ? point.asAwtPoint() : null).collect(Collectors.toList()); OverlayPointsFilter overlayPointsFilter = new OverlayPointsFilter(values, landmarkWeight, CognitiveJColourPalette.STRAWBERRY); bufferedImage = overlayPointsFilter.applyFilter(bufferedImage); return this; } /** * Draw the rectangle and outline the dominant @{@link Emotion} * * @param emotion the emotion * @return this */ @NotNull public ImageOverlayBuilder outlineDominantEmotionOnImage(@NotNull Emotion emotion) { return outlineDominantEmotionOnImage(emotion, RectangleTextPosition.TOP_OF); } /** * Draw the rectangle and outline the dominant @{@link Emotion} * * @param emotion the emotion * @param textPosition where the text is to be positioned * @return this */ @NotNull public ImageOverlayBuilder outlineDominantEmotionOnImage(@NotNull Emotion emotion, @NotNull RectangleTextPosition textPosition) { OverlayRectangleFilter overlayRectangleFilter = new OverlayRectangleFilter( emotion.faceRectangle.asAwtRectangle(), RectangleType.CORNERED, DEFAULT_BORDER_WEIGHT, CognitiveJColourPalette.STRAWBERRY); TextOnRectangleFilter textOnRectangleFilter = new TextOnRectangleFilter( new Rectangle(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight()), emotion.faceRectangle.asAwtRectangle(), new Insets(0, 0, 0, 0), DEFAULT_TEXT_FONT, CognitiveJColourPalette.STRAWBERRY, textPosition, EmotionStringBuilder.dominantEmotion(emotion)); bufferedImage = textOnRectangleFilter.applyFilter(overlayRectangleFilter.applyFilter(bufferedImage)); return this; } /** * Draw the rectangle and outline the dominant @{@link Emotion} for all faces. * * @param emotion the emotion * @param textPosition the location of the text relative to the face location * @return this */ @NotNull public ImageOverlayBuilder outlineDominantEmotionsOnImage(@NotNull List<Emotion> emotion, @NotNull RectangleTextPosition textPosition) { emotion.forEach((emotion1) -> outlineDominantEmotionOnImage(emotion1, textPosition)); return this; } /** * Draw the rectangle and displays all @{@link Emotion} for all faces. * * @param emotionFaces all found emotions. * @return this */ @NotNull public ImageOverlayBuilder outlineEmotionsOnImage(@NotNull List<Emotion> emotionFaces) { emotionFaces.forEach(this::outlineEmotionOnImage); return this; } /** * Draw the rectangle and displays all @{@link Emotion} for the face. * * @param emotion all found emotions. * @return this */ @NotNull public ImageOverlayBuilder outlineEmotionOnImage(@NotNull Emotion emotion) { return outlineEmotionOnImage(emotion, RectangleTextPosition.RIGHT_OF); } /** * Draw the rectangle and displays all @{@link Emotion} for the face. * * @param emotion all found emotions. * @param textPosition where to position the emotion * @return this */ @NotNull public ImageOverlayBuilder outlineEmotionOnImage(@NotNull Emotion emotion, @NotNull RectangleTextPosition textPosition) { OverlayRectangleFilter overlayRectangleFilter = new OverlayRectangleFilter( emotion.faceRectangle.asAwtRectangle(), RectangleType.CORNERED, DEFAULT_BORDER_WEIGHT, CognitiveJColourPalette.randomColour()); TextOnRectangleFilter textOnRectangleFilter = new TextOnRectangleFilter( new Rectangle(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight()), emotion.faceRectangle.asAwtRectangle(), new Insets(0, 0, 0, 0), new Font("Noto Sans", Font.PLAIN, 40), CognitiveJColourPalette.GRAY, textPosition, EmotionStringBuilder.listAllEmotions(emotion)); bufferedImage = textOnRectangleFilter.applyFilter(overlayRectangleFilter.applyFilter(bufferedImage)); return this; } /** * Saves the base image to the disk. * * @param file - the file where the image will be saved * @return this */ @NotNull public ImageOverlayBuilder toDisk(@NotNull File file) { try { ImageIO.write(copy(bufferedImage, BufferedImage.TYPE_INT_ARGB), "png", file); } catch (IOException e) { throw new CognitiveException("Could not write to disk", e); } return this; } /** * Saves the image to the disk and launches the default viewer for images. * * @param file - the file where the image will be saved * @return this */ @NotNull public ImageOverlayBuilder toDiskAndLaunchViewer(@NotNull File file) { toDisk(file); try { Desktop.getDesktop().open(file); Utils.waitFor(3, TimeUnit.SECONDS); } catch (IOException e) { throw new CognitiveException("Could not open image", e); } return this; } /** * creates a temp file and launches the default image viewer (if available) - {@link Desktop#open(File)} * Note: the temp file is deleted on the exit of the JVM see: ({@link File#deleteOnExit()}. * * @return this */ @NotNull public ImageOverlayBuilder launchViewer() { try { File file = File.createTempFile("cognitivej", "jpg"); toDiskAndLaunchViewer(file); file.deleteOnExit(); } catch (IOException e) { throw new CognitiveException("Could not open image", e); } return this; } /** * Writes the base image to the systems clipboard. * * @return this. */ @NotNull public ImageOverlayBuilder toClipboard() { new CopyImagetoClipBoard().copyImage(bufferedImage); return this; } /** * write the image description. see {@link cognitivej.vision.computervision.ComputerVisionScenario#describeImage(String)} * to the bottom of the image. * * @param imageDescription the image description. * @return this */ @NotNull public ImageOverlayBuilder describeImage(@NotNull ImageDescription imageDescription) { return titleImage(PointLocations.BOTTOM_CENTER, DEFAULT_TEXT_FONT.deriveFont(80f), CognitiveJColourPalette.WHITE, ComputerVisionString.describe(imageDescription)); } /** * Draws a verification graphic for the base image and the passed image. * * @param candidateImageAsUrl - the passed image as an URL. * @param verificationSet - a verification set to be drawn. * @return this * @throws ImageNotFoundException if the image can't be downloaded */ @NotNull public ImageOverlayBuilder verify(@NotNull String candidateImageAsUrl, @NotNull VerificationSet verificationSet) { try { verify(ImageIO.read(new URL(candidateImageAsUrl)), verificationSet); } catch (IOException e) { throw new ImageNotFoundException(candidateImageAsUrl); } return this; } /** * Draws a verification graphic for the base image and the passed image. * * @param candidateImage - the passed image. * @param verificationSet - a verification set to be drawn. * @return this */ @NotNull public ImageOverlayBuilder verify(@NotNull BufferedImage candidateImage, @NotNull VerificationSet verificationSet) { CognitiveJColourPalette verificationColor = verificationSet.getVerification().isIdentical ? CognitiveJColourPalette.GREEN : CognitiveJColourPalette.RED; outlineFaceOnImage(verificationSet.getFirstFace(), RectangleType.CORNERED, BorderWeight.THICK, verificationColor); OverlayRectangleFilter overlayRectangleFilter = new OverlayRectangleFilter( verificationSet.getSecondFace().faceRectangle.asAwtRectangle(), RectangleType.CORNERED, BorderWeight.THICK, verificationColor); MergeImagesFilter mergeImagesFilter = new MergeImagesFilter( overlayRectangleFilter.applyFilter(candidateImage), SMALL_PADDING, MergeImagesFilter.Position.RIGHT); LineJoinRectangleFilter lineJoinRectangleFilter = new LineJoinRectangleFilter( verificationSet.getFirstFace().faceRectangle.asAwtRectangle(), verificationSet.getSecondFace().faceRectangle.asAwtRectangle(), BorderWeight.THICK, verificationColor, bufferedImage.getWidth(), SMALL_PADDING); Verification verification = verificationSet.getVerification(); ApplyCaptionOutsideImageFilter applyCaptionOutsideImageFilter = new ApplyCaptionOutsideImageFilter( PointLocations.BOTTOM_CENTER, DEFAULT_TEXT_FONT.deriveFont(80f).deriveFont(80f), verificationColor, verification.isIdentical ? String.format("Match (confidence %.3f)", verification.confidence) : String.format("No Match (confidence %.3f)", verification.confidence)); bufferedImage = applyCaptionOutsideImageFilter .applyFilter(lineJoinRectangleFilter.applyFilter(mergeImagesFilter.applyFilter(bufferedImage))); return this; } /** * tags identified faces. * * @param identificationSet a set of identifications. * @return this */ @NotNull public ImageOverlayBuilder identify(@NotNull IdentificationSet identificationSet) { return identify(identificationSet, RectangleTextPosition.TOP_OF); } /** * tags identified faces. * * @param identificationSet a set of identifications. * @param textPosition * @return this */ @NotNull public ImageOverlayBuilder identify(@NotNull IdentificationSet identificationSet, @NotNull RectangleTextPosition textPosition) { CognitiveJColourPalette identificationColor = identificationSet.isIdentified() ? CognitiveJColourPalette.GREEN : CognitiveJColourPalette.RED; Face candidateFace = identificationSet.getCandidateFace(); outlineFaceOnImage(candidateFace, RectangleType.CORNERED, DEFAULT_BORDER_WEIGHT, identificationColor); TextOnRectangleFilter textOnRectangleFilter = new TextOnRectangleFilter( new Rectangle(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight()), candidateFace.faceRectangle.asAwtRectangle(), new Insets(DEFAULT_BORDER_WEIGHT.thickness(), DEFAULT_BORDER_WEIGHT.thickness(), DEFAULT_BORDER_WEIGHT.thickness(), DEFAULT_BORDER_WEIGHT.thickness()), DEFAULT_TEXT_FONT, identificationColor, textPosition, identificationSet.isIdentified() ? identificationSet.getIdentifiedPerson().name : "Unknown"); bufferedImage = textOnRectangleFilter.applyFilter(bufferedImage); return this; } /** * tags all isIdentified faces. * * @param identificationSets a set of identifications. */ public void identify(@NotNull List<IdentificationSet> identificationSets) { identificationSets.forEach(this::identify); } /** * Merges another image onto the base image (to the right) * * @param mergeImage the image to be merged * @return this */ @NotNull public ImageOverlayBuilder mergeImage(@NotNull BufferedImage mergeImage) { return mergeImage(mergeImage, MergeImagesFilter.Position.RIGHT); } /** * Merges another image onto the base image (to the right) * * @param mergeImage the image to be merged * @param position where to locate the newly merged image * @return this */ @NotNull public ImageOverlayBuilder mergeImage(@NotNull BufferedImage mergeImage, @NotNull MergeImagesFilter.Position position) { MergeImagesFilter mergeImagesFilter = new MergeImagesFilter(mergeImage, SMALL_PADDING, position); bufferedImage = mergeImagesFilter.applyFilter(bufferedImage); return this; } /** * Apply a title to the base image. * * @param location where to locate the title. * @param font the font to use. * @param color the color * @param text the title * @return this */ @NotNull public ImageOverlayBuilder titleImage(PointLocations location, Font font, @NotNull CognitiveJColourPalette color, @NotNull String text) { ApplyCaptionOutsideImageFilter applyCaptionOutsideImageFilter = new ApplyCaptionOutsideImageFilter(location, font, color, text); bufferedImage = applyCaptionOutsideImageFilter.applyFilter(bufferedImage); return this; } /** * @param image - the image (as a {@link BufferedImage}) * @return ImageOverlayBuilder * @see ImageOverlayBuilder#builder(String) */ @NotNull public static ImageOverlayBuilder builder(@NotNull BufferedImage image) { return new ImageOverlayBuilder(image); } /** * @param image - the image (as a {@link File}) * @return ImageOverlayBuilder * @see ImageOverlayBuilder#builder(String) */ @NotNull public static ImageOverlayBuilder builder(@NotNull File image) { ImageOverlayBuilder imageOverlayBuilder; try { imageOverlayBuilder = new ImageOverlayBuilder(ImageIO.read(image)); } catch (IOException e) { throw new ImageNotFoundException(image); } return imageOverlayBuilder; } /** * Create a new {@link ImageOverlayBuilder} from the base Image attached. * * @param image - the image (as a url {@link String} ) * @return a new {@link ImageOverlayBuilder} * @throws ImageNotFoundException - if the image can't be found. */ @NotNull public static ImageOverlayBuilder builder(@NotNull String image) { try { return new ImageOverlayBuilder(ImageIO.read(new URL(image))); } catch (IOException e) { throw new ImageNotFoundException(image); } } @NotNull private static BufferedImage copy(@NotNull BufferedImage img, int imageType) { int width = img.getWidth(); int height = img.getHeight(); BufferedImage newImage = new BufferedImage(width, height, imageType); Graphics g = newImage.createGraphics(); g.drawImage(img, 0, 0, null); g.dispose(); return newImage; } }