package net.hearthstats.game.ocr;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import net.hearthstats.game.ScreenConfig;
import net.sourceforge.tess4j.TessAPI;
import org.apache.commons.lang3.StringUtils;
public class RankLevelOcr extends OcrBase {
public Integer processNumber(BufferedImage bufferedImage) throws OcrException {
String result = process(bufferedImage);
try {
if (StringUtils.isEmpty(result)) {
return null;
} else {
int rank = Integer.valueOf(result);
if (rank < 0 || rank > 25) {
debugLog.debug("Rank {} is invalid, rank detection has failed");
return null;
} else {
return rank;
}
}
} catch (NumberFormatException e) {
debugLog.debug("Ignoring NumberFormatException parsing " + result);
return null;
}
}
@Override
protected BufferedImage crop(BufferedImage image, int iteration) {
debugLog.debug("rank detection try #" + (iteration + 1));
float ratio = ScreenConfig.getRatio(image);
int xOffset = ScreenConfig.getXOffset(image, ratio);
int retryOffset = (iteration - 1) % 3;
int x = (int) ((1369 + retryOffset) * ratio + xOffset);
int y = (int) (250 * ratio);
int width = (int) (52 * ratio);
int height = (int) (38 * ratio);
return image.getSubimage(x, y, width, height);
}
@Override
protected String parseString(String input, int iteration) {
if (input == null) {
return null;
} else {
// Change easily-mistaken letters into numbers
String output = StringUtils.replaceChars(input, "lIiSsOo", "1115500");
// Remove all other unknown letters
output = output.replaceAll("[^\\d]", "");
debugLog.debug("Parse of rank \"{}\" is \"{}\"", input, output);
return output;
}
}
@Override
protected boolean tryProcessingAgain(String ocrResult, int iteration) {
try {
if (ocrResult != null && !ocrResult.isEmpty() && Integer.parseInt(ocrResult) > 0
&& Integer.parseInt(ocrResult) < 26) {
// A valid rank number has been found
return false;
}
} catch (NumberFormatException e) {
debugLog.debug("Ignoring NumberFormatException parsing " + ocrResult);
}
if (iteration <= 5) {
// Rank level needs multiple iterations to try slightly shifted each time,
// so try five times
return true;
} else {
// Hasn't matched after five tries, so give up
return false;
}
}
@Override
protected BufferedImage filter(BufferedImage image, int iteration) throws OcrException {
int width = image.getWidth();
int height = image.getHeight();
int bigWidth = width * 2;
int bigHeight = height * 2;
// Extract only the black & white parts of the image, removing any coloured
// parts. This results in just the
// rank number being left... all the background magically disappears.
// Note that this change is being done directly on the source image for
// efficiency, which is OK because the
// source image is thrown out. However if the caller needs to reuse the
// source image then the following code
// should be changed to work on a copy of the image instead of the original.
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
// Get the individual components of this pixel
int inputRgba = image.getRGB(x, y);
int red = (inputRgba >> 16) & 0xFF;
int green = (inputRgba >> 8) & 0xFF;
int blue = (inputRgba >> 0) & 0xFF;
// If the pixel is coloured, set it to white instead
int invertedGatedLevel;
if (Math.abs(red - green) > 3 || Math.abs(red - blue) > 3 || Math.abs(green - blue) > 3) {
invertedGatedLevel = 255;
} else {
invertedGatedLevel = 255 - blue;
}
// Replace the pixel with the new value
int outputRgba = ((255 & 0xFF) << 24) | ((invertedGatedLevel & 0xFF) << 16)
| ((invertedGatedLevel & 0xFF) << 8) | ((invertedGatedLevel & 0xFF) << 0);
image.setRGB(x, y, outputRgba);
}
}
// blow it up for ocr
BufferedImage newImage = new BufferedImage(bigWidth, bigHeight, BufferedImage.TYPE_INT_RGB);
Graphics g = newImage.createGraphics();
g.drawImage(image, 0, 0, bigWidth, bigHeight, null);
g.dispose();
return newImage;
}
@Override
protected String getFilename() {
return "ranklevel";
}
@Override
protected int getTesseractPageSegMode(int iteration) {
return TessAPI.TessPageSegMode.PSM_SINGLE_WORD;
}
}