mirror of
https://github.com/zxing/zxing.git
synced 2024-11-09 20:44:03 -08:00
CommandLineRunner can now handle URIs again; refactored some duplicated code
This commit is contained in:
parent
560cba85b0
commit
106fd2fc0e
|
@ -20,6 +20,7 @@ import com.google.zxing.BarcodeFormat;
|
|||
import com.google.zxing.DecodeHintType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
@ -27,7 +28,6 @@ import java.nio.file.Paths;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
@ -43,6 +43,7 @@ import java.util.regex.Pattern;
|
|||
* directories, summary statistics are also displayed.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class CommandLineRunner {
|
||||
|
||||
|
@ -58,7 +59,7 @@ public final class CommandLineRunner {
|
|||
}
|
||||
|
||||
Config config = new Config();
|
||||
Queue<Path> inputs = new ConcurrentLinkedQueue<>();
|
||||
Queue<URI> inputs = new ConcurrentLinkedQueue<>();
|
||||
|
||||
for (String arg : args) {
|
||||
String[] argValue = arg.split("=");
|
||||
|
@ -104,13 +105,25 @@ public final class CommandLineRunner {
|
|||
printUsage();
|
||||
return;
|
||||
}
|
||||
addArgumentToInputs(Paths.get(arg), config, inputs);
|
||||
URI argURI = URI.create(arg);
|
||||
if (argURI.getScheme() == null) {
|
||||
argURI = new URI("file", argURI.getSchemeSpecificPart(), argURI.getFragment());
|
||||
}
|
||||
addArgumentToInputs(argURI, config, inputs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int numInputs = inputs.size();
|
||||
if (numInputs == 0) {
|
||||
System.err.println("No inputs specified");
|
||||
printUsage();
|
||||
return;
|
||||
}
|
||||
|
||||
config.setHints(buildHints(config));
|
||||
|
||||
int numThreads = Math.min(inputs.size(), Runtime.getRuntime().availableProcessors());
|
||||
int numThreads = Math.min(numInputs, Runtime.getRuntime().availableProcessors());
|
||||
int successful = 0;
|
||||
if (numThreads > 1) {
|
||||
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
|
||||
|
@ -126,44 +139,38 @@ public final class CommandLineRunner {
|
|||
successful += new DecodeWorker(config, inputs).call();
|
||||
}
|
||||
|
||||
int total = inputs.size();
|
||||
if (total > 1) {
|
||||
System.out.println("\nDecoded " + successful + " files out of " + total +
|
||||
" successfully (" + (successful * 100 / total) + "%)\n");
|
||||
if (numInputs > 1) {
|
||||
System.out.println("\nDecoded " + successful + " files out of " + numInputs +
|
||||
" successfully (" + (successful * 100 / numInputs) + "%)\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Build all the inputs up front into a single flat list, so the threads can atomically pull
|
||||
// paths/URLs off the queue.
|
||||
private static void addArgumentToInputs(Path inputFile, Config config, Queue<Path> inputs) throws IOException {
|
||||
if (Files.isDirectory(inputFile)) {
|
||||
try (DirectoryStream<Path> paths = Files.newDirectoryStream(inputFile)) {
|
||||
for (Path singleFile : paths) {
|
||||
String filename = singleFile.getFileName().toString().toLowerCase(Locale.ENGLISH);
|
||||
/**
|
||||
* Build all the inputs up front into a single flat list, so the threads can atomically pull
|
||||
* paths/URLs off the queue.
|
||||
*/
|
||||
private static void addArgumentToInputs(URI input, Config config, Queue<URI> inputs) throws IOException {
|
||||
// Special case: a local directory
|
||||
if ("file".equals(input.getScheme()) && Files.isDirectory(Paths.get(input))) {
|
||||
try (DirectoryStream<Path> childPaths = Files.newDirectoryStream(Paths.get(input))) {
|
||||
for (Path childPath : childPaths) {
|
||||
Path realChildPath = childPath.toRealPath();
|
||||
// Skip hidden files and directories (e.g. svn stuff).
|
||||
if (filename.startsWith(".")) {
|
||||
continue;
|
||||
}
|
||||
// Recur on nested directories if requested, otherwise skip them.
|
||||
if (Files.isDirectory(singleFile)) {
|
||||
if (config.isRecursive()) {
|
||||
addArgumentToInputs(singleFile, config, inputs);
|
||||
if (!realChildPath.getFileName().toString().startsWith(".")) {
|
||||
// Recur on nested directories if requested, otherwise skip them.
|
||||
if (config.isRecursive() && Files.isDirectory(realChildPath)) {
|
||||
addArgumentToInputs(realChildPath.toUri(), config, inputs);
|
||||
} else {
|
||||
inputs.add(realChildPath.toUri());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Skip text files and the results of dumping the black point.
|
||||
if (filename.endsWith(".txt") || filename.contains(".mono.png")) {
|
||||
continue;
|
||||
}
|
||||
inputs.add(singleFile);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
inputs.add(inputFile);
|
||||
inputs.add(input);
|
||||
}
|
||||
}
|
||||
|
||||
// Manually turn on all formats, even those not yet considered production quality.
|
||||
private static Map<DecodeHintType,?> buildHints(Config config) {
|
||||
Collection<BarcodeFormat> possibleFormats = new ArrayList<>();
|
||||
String[] possibleFormatsNames = config.getPossibleFormats();
|
||||
|
|
|
@ -42,7 +42,6 @@ import java.nio.file.Paths;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.Callable;
|
||||
|
@ -59,9 +58,9 @@ final class DecodeWorker implements Callable<Integer> {
|
|||
private static final int WHITE = 0xFFFFFFFF;
|
||||
|
||||
private final Config config;
|
||||
private final Queue<Path> inputs;
|
||||
private final Queue<URI> inputs;
|
||||
|
||||
DecodeWorker(Config config, Queue<Path> inputs) {
|
||||
DecodeWorker(Config config, Queue<URI> inputs) {
|
||||
this.config = config;
|
||||
this.inputs = inputs;
|
||||
}
|
||||
|
@ -69,139 +68,101 @@ final class DecodeWorker implements Callable<Integer> {
|
|||
@Override
|
||||
public Integer call() throws IOException {
|
||||
int successful = 0;
|
||||
Path input;
|
||||
while ((input = inputs.poll()) != null) {
|
||||
if (Files.exists(input)) {
|
||||
if (config.isMulti()) {
|
||||
Result[] results = decodeMulti(input.toUri(), config.getHints());
|
||||
if (results != null) {
|
||||
successful++;
|
||||
if (config.isDumpResults()) {
|
||||
dumpResultMulti(input, results);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Result result = decode(input.toUri(), config.getHints());
|
||||
if (result != null) {
|
||||
successful++;
|
||||
if (config.isDumpResults()) {
|
||||
dumpResult(input, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (decode(input.toUri(), config.getHints()) != null) {
|
||||
successful++;
|
||||
for (URI input; (input = inputs.poll()) != null;) {
|
||||
Result[] results = decode(input, config.getHints());
|
||||
if (results != null) {
|
||||
successful++;
|
||||
if (config.isDumpResults()) {
|
||||
dumpResult(input, results);
|
||||
}
|
||||
}
|
||||
}
|
||||
return successful;
|
||||
}
|
||||
|
||||
private static void dumpResult(Path input, Result result) throws IOException {
|
||||
String name = input.getFileName().toString();
|
||||
int pos = name.lastIndexOf('.');
|
||||
if (pos > 0) {
|
||||
name = name.substring(0, pos) + ".txt";
|
||||
private static Path buildOutputPath(URI input, String suffix) throws IOException {
|
||||
Path outDir;
|
||||
String inputFileName;
|
||||
if ("file".equals(input.getScheme())) {
|
||||
Path inputPath = Paths.get(input);
|
||||
outDir = inputPath.getParent();
|
||||
inputFileName = inputPath.getFileName().toString();
|
||||
} else {
|
||||
outDir = Paths.get(".").toRealPath();
|
||||
String[] pathElements = input.getPath().split("/");
|
||||
inputFileName = pathElements[pathElements.length - 1];
|
||||
}
|
||||
Path dumpFile = input.getParent().resolve(name);
|
||||
Files.write(dumpFile, Collections.singleton(result.getText()), StandardCharsets.UTF_8);
|
||||
|
||||
// Replace/add extension
|
||||
int pos = inputFileName.lastIndexOf('.');
|
||||
if (pos > 0) {
|
||||
inputFileName = inputFileName.substring(0, pos) + suffix;
|
||||
} else {
|
||||
inputFileName += suffix;
|
||||
}
|
||||
|
||||
return outDir.resolve(inputFileName);
|
||||
}
|
||||
|
||||
private static void dumpResultMulti(Path input, Result[] results) throws IOException {
|
||||
String name = input.getFileName().toString();
|
||||
int pos = name.lastIndexOf('.');
|
||||
if (pos > 0) {
|
||||
name = name.substring(0, pos) + ".txt";
|
||||
}
|
||||
Path dumpFile = input.getParent().resolve(name);
|
||||
private static void dumpResult(URI input, Result... results) throws IOException {
|
||||
Collection<String> resultTexts = new ArrayList<>();
|
||||
for (Result result : results) {
|
||||
resultTexts.add(result.getText());
|
||||
}
|
||||
Files.write(dumpFile, resultTexts, StandardCharsets.UTF_8);
|
||||
Files.write(buildOutputPath(input, ".txt"), resultTexts, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private Result decode(URI uri, Map<DecodeHintType,?> hints) throws IOException {
|
||||
private Result[] decode(URI uri, Map<DecodeHintType,?> hints) throws IOException {
|
||||
BufferedImage image = ImageReader.readImage(uri);
|
||||
try {
|
||||
LuminanceSource source;
|
||||
if (config.getCrop() == null) {
|
||||
source = new BufferedImageLuminanceSource(image);
|
||||
} else {
|
||||
int[] crop = config.getCrop();
|
||||
source = new BufferedImageLuminanceSource(image, crop[0], crop[1], crop[2], crop[3]);
|
||||
}
|
||||
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
|
||||
if (config.isDumpBlackPoint()) {
|
||||
dumpBlackPoint(uri, image, bitmap);
|
||||
}
|
||||
Result result = new MultiFormatReader().decode(bitmap, hints);
|
||||
if (config.isBrief()) {
|
||||
System.out.println(uri + ": Success");
|
||||
} else {
|
||||
ParsedResult parsedResult = ResultParser.parseResult(result);
|
||||
System.out.println(uri + " (format: " + result.getBarcodeFormat() + ", type: " +
|
||||
parsedResult.getType() + "):\nRaw result:\n" + result.getText() + "\nParsed result:\n" +
|
||||
parsedResult.getDisplayResult());
|
||||
|
||||
LuminanceSource source;
|
||||
if (config.getCrop() == null) {
|
||||
source = new BufferedImageLuminanceSource(image);
|
||||
} else {
|
||||
int[] crop = config.getCrop();
|
||||
source = new BufferedImageLuminanceSource(image, crop[0], crop[1], crop[2], crop[3]);
|
||||
}
|
||||
|
||||
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
|
||||
if (config.isDumpBlackPoint()) {
|
||||
dumpBlackPoint(uri, image, bitmap);
|
||||
}
|
||||
|
||||
MultiFormatReader multiFormatReader = new MultiFormatReader();
|
||||
Result[] results;
|
||||
try {
|
||||
if (config.isMulti()) {
|
||||
MultipleBarcodeReader reader = new GenericMultipleBarcodeReader(multiFormatReader);
|
||||
results = reader.decodeMultiple(bitmap, hints);
|
||||
} else {
|
||||
results = new Result[]{multiFormatReader.decode(bitmap, hints)};
|
||||
}
|
||||
} catch (NotFoundException ignored) {
|
||||
System.out.println(uri + ": No barcode found");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (config.isBrief()) {
|
||||
System.out.println(uri + ": Success");
|
||||
} else {
|
||||
for (Result result : results) {
|
||||
ParsedResult parsedResult = ResultParser.parseResult(result);
|
||||
System.out.println(uri +
|
||||
" (format: " + result.getBarcodeFormat() +
|
||||
", type: " + parsedResult.getType() + "):\n" +
|
||||
"Raw result:\n" +
|
||||
result.getText() + "\n" +
|
||||
"Parsed result:\n" +
|
||||
parsedResult.getDisplayResult());
|
||||
System.out.println("Found " + result.getResultPoints().length + " result points.");
|
||||
for (int i = 0; i < result.getResultPoints().length; i++) {
|
||||
ResultPoint rp = result.getResultPoints()[i];
|
||||
if (rp != null) {
|
||||
System.out.println(" Point " + i + ": (" + rp.getX() + ',' + rp.getY() + ')');
|
||||
}
|
||||
System.out.println(" Point " + i + ": (" + rp.getX() + ',' + rp.getY() + ')');
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (NotFoundException ignored) {
|
||||
System.out.println(uri + ": No barcode found");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Result[] decodeMulti(URI uri, Map<DecodeHintType,?> hints) throws IOException {
|
||||
BufferedImage image = ImageReader.readImage(uri);
|
||||
try {
|
||||
LuminanceSource source;
|
||||
if (config.getCrop() == null) {
|
||||
source = new BufferedImageLuminanceSource(image);
|
||||
} else {
|
||||
int[] crop = config.getCrop();
|
||||
source = new BufferedImageLuminanceSource(image, crop[0], crop[1], crop[2], crop[3]);
|
||||
}
|
||||
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
|
||||
if (config.isDumpBlackPoint()) {
|
||||
dumpBlackPoint(uri, image, bitmap);
|
||||
}
|
||||
|
||||
MultiFormatReader multiFormatReader = new MultiFormatReader();
|
||||
MultipleBarcodeReader reader = new GenericMultipleBarcodeReader(multiFormatReader);
|
||||
Result[] results = reader.decodeMultiple(bitmap, hints);
|
||||
|
||||
if (config.isBrief()) {
|
||||
System.out.println(uri + ": Success");
|
||||
} else {
|
||||
for (Result result : results) {
|
||||
ParsedResult parsedResult = ResultParser.parseResult(result);
|
||||
System.out.println(uri + " (format: "
|
||||
+ result.getBarcodeFormat() + ", type: "
|
||||
+ parsedResult.getType() + "):\nRaw result:\n"
|
||||
+ result.getText() + "\nParsed result:\n"
|
||||
+ parsedResult.getDisplayResult());
|
||||
System.out.println("Found " + result.getResultPoints().length + " result points.");
|
||||
for (int i = 0; i < result.getResultPoints().length; i++) {
|
||||
ResultPoint rp = result.getResultPoints()[i];
|
||||
System.out.println(" Point " + i + ": (" + rp.getX() + ',' + rp.getY() + ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
return results;
|
||||
} catch (NotFoundException ignored) {
|
||||
System.out.println(uri + ": No barcode found");
|
||||
return null;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -209,11 +170,7 @@ final class DecodeWorker implements Callable<Integer> {
|
|||
* to right: the original image, the row sampling monochrome version, and the 2D sampling
|
||||
* monochrome version.
|
||||
*/
|
||||
private static void dumpBlackPoint(URI uri, BufferedImage image, BinaryBitmap bitmap) {
|
||||
if (uri.getPath().contains(".mono.png")) {
|
||||
return;
|
||||
}
|
||||
|
||||
private static void dumpBlackPoint(URI uri, BufferedImage image, BinaryBitmap bitmap) throws IOException {
|
||||
int width = bitmap.getWidth();
|
||||
int height = bitmap.getHeight();
|
||||
int stride = width * 3;
|
||||
|
@ -263,30 +220,17 @@ final class DecodeWorker implements Callable<Integer> {
|
|||
private static void writeResultImage(int stride,
|
||||
int height,
|
||||
int[] pixels,
|
||||
URI uri,
|
||||
String suffix) {
|
||||
URI input,
|
||||
String suffix) throws IOException {
|
||||
BufferedImage result = new BufferedImage(stride, height, BufferedImage.TYPE_INT_ARGB);
|
||||
result.setRGB(0, 0, stride, height, pixels, 0, stride);
|
||||
|
||||
// Use the current working directory for URLs
|
||||
String resultName = uri.getPath();
|
||||
if ("http".equals(uri.getScheme())) {
|
||||
int pos = resultName.lastIndexOf('/');
|
||||
if (pos > 0) {
|
||||
resultName = '.' + resultName.substring(pos);
|
||||
}
|
||||
}
|
||||
int pos = resultName.lastIndexOf('.');
|
||||
if (pos > 0) {
|
||||
resultName = resultName.substring(0, pos);
|
||||
}
|
||||
resultName += suffix;
|
||||
Path imagePath = buildOutputPath(input, suffix);
|
||||
try {
|
||||
if (!ImageIO.write(result, "png", Paths.get(resultName).toFile())) {
|
||||
System.err.println("Could not encode an image to " + resultName);
|
||||
if (!ImageIO.write(result, "png", imagePath.toFile())) {
|
||||
System.err.println("Could not encode an image to " + imagePath);
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
System.err.println("Could not write to " + resultName);
|
||||
System.err.println("Could not write to " + imagePath);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -53,8 +53,8 @@ public final class ImageReader {
|
|||
}
|
||||
|
||||
public static BufferedImage readDataURIImage(URI uri) throws IOException {
|
||||
String uriString = uri.toString();
|
||||
if (!uriString.startsWith("data:image/")) {
|
||||
String uriString = uri.getSchemeSpecificPart();
|
||||
if (!uriString.startsWith("image/")) {
|
||||
throw new IOException("Unsupported data URI MIME type");
|
||||
}
|
||||
int base64Start = uriString.indexOf(BASE64TOKEN);
|
||||
|
|
Loading…
Reference in a new issue