Skip to content

Commit

Permalink
Merge pull request #45 from Mazawrath/GoogleVision
Browse files Browse the repository at this point in the history
Google vision
  • Loading branch information
Mazawrath authored Jan 11, 2019
2 parents 6eb0483 + 63f0e6f commit 0875c81
Show file tree
Hide file tree
Showing 7 changed files with 309 additions and 17 deletions.
5 changes: 4 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ dependencies {

'org.toile-libre.libe:curl:0.0.23',

'com.google.api-client:google-api-client:1.24.1'
'com.google.api-client:google-api-client:1.24.1',

'com.google.cloud:google-cloud-vision:1.55.0',
'com.google.cloud:google-cloud-storage:1.55.0'
)

runtime(
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/com/mazawrath/beanbot/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.mazawrath.beanbot.commands.copypasta.GiveModCommand;
import com.mazawrath.beanbot.commands.beancoin.*;
import com.mazawrath.beanbot.commands.copypasta.*;
import com.mazawrath.beanbot.commands.googlevision.AnalyzeCommand;
import com.mazawrath.beanbot.utilities.*;
import com.mazawrath.beanbot.commands.admin.*;
import com.mazawrath.beanbot.utilities.jersey.RestServer;
Expand All @@ -27,6 +28,8 @@ public static void main(String[] args) {
// Enable debugging, if no slf4j logger was found
//FallbackLoggerConfiguration.setDebug(false);

GoogleCloudVision cloudVision = new GoogleCloudVision();

Connection conn = r.connection().hostname("localhost").port(28015).connect();

Points points = new Points(conn);
Expand Down Expand Up @@ -68,6 +71,8 @@ public static void main(String[] args) {
cmdHandler.registerCommand(new BeanInvestCommand(points, stockMarket));
// Bean Lottery Commands
cmdHandler.registerCommand(new BeanLotteryCommand(points, lottery));
// Google Vision Commands
cmdHandler.registerCommand(new AnalyzeCommand(points, cloudVision));
// Admin commands
cmdHandler.registerCommand(new AdminPostChangeLogCommand());
cmdHandler.registerCommand(new AdminDeleteMessageCommand());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,16 @@ public void onCommand(String[] args, DiscordApi api, ServerTextChannel serverTex

private String getRecentChangeLog() {
return "**New beanBOT update released.**\n" +
"Release can be found on https://github.com/Mazawrath/beanBOT/releases/tag/v3.1.1\n" +
"Detailed changelog can be found on https://github.com/Mazawrath/beanBOT/compare/v3.1.0...v3.1.1\n" +
"Release can be found on https://github.com/Mazawrath/beanBOT/releases/tag/v3.2.0\n" +
"Detailed changelog can be found on https://github.com/Mazawrath/beanBOT/compare/v3.1.1...v3.2.0\n" +
"\n" +
"**v3.1.1**\n" +
// "**New**\n" +
// "\t- Twitch Update\n" +
// "\t\t- beanBOT can now notify servers when a Twitch channel goes live.\n" +
"**v3.2.0**\n" +
"**New**\n" +
"\t- Added `.analyze`.\n" +
"\t\t- Using Google Cloud Vision, beanBOT can now examine a photo for objects, faces, emotions, and more.\n" +
// "\t\t- Server owners can now type `.admintwitch add [twitch channel name]` to subscribe to live notifications for a twitch channel.\n" +
"**Changes**\n" +
"\t- Set up Twitch to only notify servers if streamer was previously offline and has not been online within the past 10 minutes.\n" +
"\t- Changed price of `.beanlottery draw` from 400 beanCoin to 20,000 beanCoin.\n" +
"\t- Lowered time of bean lottery drawing from 7 days to 3 days.";
"\t- Disabled `.beanlottery draw`.";
// "**Bug Fixes**\n" +
// "\t- Fixed an issue with `.beanlottery` not giving help information.\n";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ public void onCommand(String[] args, DiscordApi api, ServerTextChannel serverTex
lottery.scheduleWeeklyDrawing(points, server, api, serverTextChannel);
return;
}
} else if (args[0].equalsIgnoreCase("draw")) {
if (points.removePoints(author.getIdAsString(), api.getYourself().getIdAsString(), server.getIdAsString(), Points.LOTTERY_DRAWING_COST)) {
lottery.drawNumbers(points, server, api, serverTextChannel);
} else
serverTextChannel.sendMessage("You do not have enough beanCoin for this command");
return;
}
} //else if (args[0].equalsIgnoreCase("draw")) {
// if (points.removePoints(author.getIdAsString(), api.getYourself().getIdAsString(), server.getIdAsString(), Points.LOTTERY_DRAWING_COST)) {
// lottery.drawNumbers(points, server, api, serverTextChannel);
// } else
// serverTextChannel.sendMessage("You do not have enough beanCoin for this command");
// return;
// }
if (Integer.parseInt(args[0]) > 200) {
serverTextChannel.sendMessage("You can only buy 200 tickets at a time.");
return;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package com.mazawrath.beanbot.commands.googlevision;

import com.google.cloud.vision.v1.AnnotateImageResponse;
import com.google.cloud.vision.v1.EntityAnnotation;
import com.google.cloud.vision.v1.SafeSearchAnnotation;
import com.google.cloud.vision.v1.WebDetection;
import com.mazawrath.beanbot.utilities.GoogleCloudVision;
import com.mazawrath.beanbot.utilities.Points;
import de.btobastian.sdcf4j.Command;
import de.btobastian.sdcf4j.CommandExecutor;
import org.apache.commons.lang3.text.WordUtils;
import org.javacord.api.DiscordApi;
import org.javacord.api.entity.channel.ServerTextChannel;
import org.javacord.api.entity.message.Message;
import org.javacord.api.entity.message.embed.EmbedBuilder;
import org.javacord.api.entity.server.Server;
import org.javacord.api.entity.user.User;

import javax.activation.MimetypesFileTypeMap;
import java.awt.*;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;

public class AnalyzeCommand implements CommandExecutor {
private Points points;
private GoogleCloudVision cloudVision;

public AnalyzeCommand(Points points, GoogleCloudVision cloudVision) {
this.points = points;
this.cloudVision = cloudVision;
}

@Command(
aliases = {"analyze"},
usage = "cloudVision",
privateMessages = false,
async = true
)

public void onCommand(String[] args, Message message, DiscordApi api, ServerTextChannel serverTextChannel, User author, Server server) {
URL url;
if (message.getAttachments().size() != 0)
url = message.getAttachments().get(0).getUrl();
else if (args.length > 0) {
try {
url = new URL(args[0]);
} catch (MalformedURLException e) {
e.printStackTrace();
serverTextChannel.sendMessage("URL is not valid.");
return;
}
} else {
serverTextChannel.sendMessage("You must either have a URL in your message or an attachment.");
return;
}

if (!points.removePoints(author.getIdAsString(), api.getYourself().getIdAsString(), server.getIdAsString(), Points.GOOGLE_VISION_COST)) {
serverTextChannel.sendMessage("You do not have enough beanCoin for this command");
return;
}

serverTextChannel.sendMessage("Analyzing image...");

List<EntityAnnotation> labelAnnotation;
AnnotateImageResponse faceDetection;
SafeSearchAnnotation safeSearchAnnotation;
WebDetection webDetection;

if (urlContainsImage(url)) {
try {
labelAnnotation = cloudVision.getLabelDetection(url);
faceDetection = cloudVision.getFaceDetection(url);
safeSearchAnnotation = cloudVision.detectSafeSearch(url);
webDetection = cloudVision.getWebDetection(url);
} catch (Exception e) {
e.printStackTrace();
serverTextChannel.sendMessage("Something went wrong.");
return;
}
} else {
serverTextChannel.sendMessage("URL must be an image.");
return;
}

EmbedBuilder embed = new EmbedBuilder()
.setTitle("Image Analysis")
.setColor(Color.BLUE);

StringBuilder labels = new StringBuilder();
for (int i = 0; i < labelAnnotation.size(); i++) {
if (i != labelAnnotation.size() - 1) {
labels.append(labelAnnotation.get(i).getDescription()).append(" (").append(Math.round(labelAnnotation.get(0).getScore() * 100)).append("%), ");
} else
labels.append(labelAnnotation.get(i).getDescription()).append(" (").append(Math.round(labelAnnotation.get(0).getScore() * 100)).append("%)");
}

embed.addField("Things I See", labels.toString());

embed.addInlineField("Faces I See", String.valueOf(faceDetection.getFaceAnnotationsCount()));

for (int i = 0; i < faceDetection.getFaceAnnotationsCount(); i++) {
StringBuilder emotionsSeen = new StringBuilder();
if (faceDetection.getFaceAnnotations(i).getJoyLikelihoodValue() > 1)
emotionsSeen.append("joy, ");
if (faceDetection.getFaceAnnotations(i).getSorrowLikelihoodValue() > 1)
emotionsSeen.append("sorrow, ");
if (faceDetection.getFaceAnnotations(i).getAngerLikelihoodValue() > 1)
emotionsSeen.append("anger, ");
if (faceDetection.getFaceAnnotations(i).getSurpriseLikelihoodValue() > 1)
emotionsSeen.append("surprise, ");

if (emotionsSeen.length() != 0)
embed.addInlineField("Face " + (i + 1) + "'s Possible Emotions", WordUtils.capitalizeFully(emotionsSeen.substring(0, emotionsSeen.length() - 2)));
else
embed.addInlineField("Face " + (i + 1) + "'s Possible Emotions", "none");
}
embed.addInlineField("Best Guess", webDetection.getBestGuessLabels(0).getLabel());

StringBuilder webLabels = new StringBuilder();
for (int i = 0; i < webDetection.getWebEntitiesCount(); i++) {
if (i != webDetection.getWebEntitiesCount() - 1)
webLabels.append(webDetection.getWebEntities(i).getDescription()).append(" (").append(Math.round(webDetection.getWebEntities(i).getScore() * 100)).append("%), ");
else
webLabels.append(webDetection.getWebEntities(i).getDescription()).append(" (").append(Math.round(webDetection.getWebEntities(i).getScore() * 100)).append("%)");
}
embed.addField("Things I Think This Is", webLabels.toString());

if (safeSearchAnnotation.getAdultValue() > 2)
embed.addInlineField("Adult Content", WordUtils.capitalizeFully(safeSearchAnnotation.getAdult().name().replaceAll("_", " ")));
if (safeSearchAnnotation.getSpoofValue() > 2)
embed.addInlineField("Spoof / Edited Photo", WordUtils.capitalizeFully(safeSearchAnnotation.getSpoof().name().replaceAll("_", " ")));
if (safeSearchAnnotation.getMedicalValue() > 2)
embed.addInlineField("Medical / Surgery", WordUtils.capitalizeFully(safeSearchAnnotation.getMedical().name().replaceAll("_", " ")));
if (safeSearchAnnotation.getViolenceValue() > 2)
embed.addInlineField("Violence / Blood / Gore", WordUtils.capitalizeFully(safeSearchAnnotation.getViolence().name().replaceAll("_", " ")));
if (safeSearchAnnotation.getRacyValue() > 2)
embed.addInlineField("Skimpy / Nudity", WordUtils.capitalizeFully(safeSearchAnnotation.getRacy().name().replaceAll("_", " ")));

serverTextChannel.sendMessage(embed);
}

private boolean urlContainsImage(URL url) {
File f = new File(url.toString());
String mimetype = new MimetypesFileTypeMap().getContentType(f);
String type = mimetype.split("/")[0];
return type.equals("image");
}
}
135 changes: 135 additions & 0 deletions src/main/java/com/mazawrath/beanbot/utilities/GoogleCloudVision.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package com.mazawrath.beanbot.utilities;

import com.google.cloud.vision.v1.*;
import com.google.cloud.vision.v1.Feature.Type;
import com.google.protobuf.ByteString;
import org.apache.commons.io.IOUtils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class GoogleCloudVision {

public GoogleCloudVision() {
System.setProperty("http.agent", "Chrome");
}

public List<EntityAnnotation> getLabelDetection(URL image) {
try (ImageAnnotatorClient vision = ImageAnnotatorClient.create()) {

ByteString imgBytes = ByteString.copyFrom(Objects.requireNonNull(downloadFile(image)));

// Builds the image annotation request
List<AnnotateImageRequest> requests = new ArrayList<>();
Image img = Image.newBuilder().setContent(imgBytes).build();
Feature feat = Feature.newBuilder().setType(Type.LABEL_DETECTION).build();
AnnotateImageRequest request = AnnotateImageRequest.newBuilder()
.addFeatures(feat)
.setImage(img)
.build();
requests.add(request);

// Performs label detection on the image file
BatchAnnotateImagesResponse response = vision.batchAnnotateImages(requests);
List<AnnotateImageResponse> responses = response.getResponsesList();

for (AnnotateImageResponse res : responses) {
if (res.hasError()) {
System.out.printf("Error: %s\n", res.getError().getMessage());
return null;
}

return res.getLabelAnnotationsList();
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}

public SafeSearchAnnotation detectSafeSearch(URL image) throws IOException {
List<AnnotateImageRequest> requests = new ArrayList<>();

ByteString imgBytes = ByteString.copyFrom(Objects.requireNonNull(downloadFile(image)));

Image img = Image.newBuilder().setContent(imgBytes).build();
Feature feat = Feature.newBuilder().setType(Type.SAFE_SEARCH_DETECTION).build();
AnnotateImageRequest request =
AnnotateImageRequest.newBuilder().addFeatures(feat).setImage(img).build();
requests.add(request);

try (ImageAnnotatorClient client = ImageAnnotatorClient.create()) {
BatchAnnotateImagesResponse response = client.batchAnnotateImages(requests);
List<AnnotateImageResponse> responses = response.getResponsesList();

for (AnnotateImageResponse res : responses) {
if (res.hasError()) {
return null;
}

// For full list of available annotations, see http://g.co/cloud/vision/docs
return res.getSafeSearchAnnotation();
}
}
return null;
}

public AnnotateImageResponse getFaceDetection(URL image) throws Exception, IOException {
List<AnnotateImageRequest> requests = new ArrayList<>();

ByteString imgBytes = ByteString.copyFrom(Objects.requireNonNull(downloadFile(image)));

Image img = Image.newBuilder().setContent(imgBytes).build();
Feature feat = Feature.newBuilder().setType(Type.FACE_DETECTION).build();
AnnotateImageRequest request =
AnnotateImageRequest.newBuilder().addFeatures(feat).setImage(img).build();
requests.add(request);

try (ImageAnnotatorClient client = ImageAnnotatorClient.create()) {
BatchAnnotateImagesResponse response = client.batchAnnotateImages(requests);
return response.getResponsesList().get(0);
}
}

public WebDetection getWebDetection(URL image) throws Exception,
IOException {
List<AnnotateImageRequest> requests = new ArrayList<>();

ByteString imgBytes = ByteString.copyFrom(Objects.requireNonNull(downloadFile(image)));

Image img = Image.newBuilder().setContent(imgBytes).build();
Feature feat = Feature.newBuilder().setType(Type.WEB_DETECTION).build();
AnnotateImageRequest request =
AnnotateImageRequest.newBuilder().addFeatures(feat).setImage(img).build();
requests.add(request);

try (ImageAnnotatorClient client = ImageAnnotatorClient.create()) {
BatchAnnotateImagesResponse response = client.batchAnnotateImages(requests);

return response.getResponsesList().get(0).getWebDetection();
}
}


private static byte[] downloadFile(URL url) {
try {
URLConnection conn = url.openConnection();
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
conn.connect();

ByteArrayOutputStream baos = new ByteArrayOutputStream();
IOUtils.copy(conn.getInputStream(), baos);

return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
1 change: 1 addition & 0 deletions src/main/java/com/mazawrath/beanbot/utilities/Points.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class Points {
public static final BigDecimal COMMAND_COST_SPECIAL = new BigDecimal("10.00").setScale(SCALE, ROUNDING_MODE);
public static final BigDecimal LOTTERY_TICKET_COST = new BigDecimal("20.00").setScale(SCALE, ROUNDING_MODE);
public static final BigDecimal LOTTERY_DRAWING_COST = new BigDecimal("20000.00").setScale(SCALE, ROUNDING_MODE);
public static final BigDecimal GOOGLE_VISION_COST = new BigDecimal("50.00").setScale(SCALE, ROUNDING_MODE);
private Connection conn;

private static final BigDecimal NUMBER_TO_PERCENT = new BigDecimal(.01);
Expand Down

0 comments on commit 0875c81

Please sign in to comment.