我有一个Android应用程序,我试图让用户使用本机相机捕获文档的图像,然后将其发送到服务器以使其成为黑白。
在Android应用程序中,我正在关注this example从相机拍摄完整尺寸的照片。
我遇到的问题是旧的Nexus 4相机会产生较暗的图像,因此部分文档会变成黑色而不是白色(大部分位于文档的边缘)。但是使用更新的Nexus 6相机没有问题。
使用this code更改对比度/亮度非常有助于提高Nexus 4图像的质量,但可以清除较新的Nexus 6图像。
是否有办法从较旧的Android设备自动检测较暗,质量较差的图像,以便仅对这些图像应用图像校正?
简单地用this code计算图像的平均亮度会因文档周围背景暗的可能性而被抛弃。
答案 0 :(得分:0)
可能,标准不是亮度,而是对比度。无论如何,挑战是艰难的,你不能指望完美的流程,但要准备好通过反复试验来处理它。
尝试拍摄具有正常对比度的照片;如果失败,请设置一个标志,以便下次使用额外的亮度。请注意,野外设备可能会有非常不同的相机行为,即使使用同一部手机,照片条件也会有所不同。
您可以将Nexus 4的标志初始化为高亮度。实际上,我不确定这是否适用于所有Nexus 4设备,或仅适用于某些设备。
如果您的应用程序将被大量使用,您可以收集每个模型的统计信息,并在安装应用程序时将其应用为初始猜测。
答案 1 :(得分:0)
我最终在C ++ OpenCV中找到了这个algorithm,我在后端服务器上用Java编写了一个实现。它似乎很好地处理来自旧相机的较暗图像。虽然它似乎只适用于JPG图像,但不适用于PNG。
private BufferedImage autoCorrectBrightnessAndContrast(final BufferedImage image) {
final int[] histogram = makeHistogram(image);
if (LOG.isDebugEnabled()) {
LOG.debug("Histogram: {}", Arrays.toString(histogram));
}
final int[] cumulativeHistogram = getCumulativeHistogram(histogram);
/*
* Finding the minimum and maximum indices in the histogram where the value at the index is greater than a given
* threshold, computed from CLIP_PERCENT.
*/
final int min = getMinIndex(cumulativeHistogram);
final int max = getMaxIndex(cumulativeHistogram);
LOG.debug("Min: {} Max: {}", min, max);
/*
* alpha is the scaling factor, or the amount that we need to expand the histogram graph horizontally so that it
* fills the entire width. Essentially increasing contrast.
*/
double alpha = 1;
if (max != min) {
alpha = MAX_COLOR / (double) (max - min);
}
/*
* beta is the translating factor, or the amount that we need to slide the histogram graph left or right so that
* it lines up with the origin. Essentially adjusting brightness.
*/
final double beta = -min * alpha;
return adjustBrightnessAndContrast(image, alpha, beta);
}
/**
* Creates an int array of length MAX_COLOR representing a histogram from the input image. Each index represents the
* number of pixels of that greyscale shade in the image.
*
* @param image
* the bufferedImage to create a histogram from.
* @return an int array represenation of the histogram.
*
* @see <a href="https://stackoverflow.com/a/10109389/1088659">Plot a histogram for a buffered image</a>
*/
private static int[] makeHistogram(final BufferedImage image) {
final int[] histogram = new int[MAX_COLOR];
final ColorSpace colorSpace = image.getColorModel().getColorSpace();
LOG.debug("Color space type: {}, is RGB = {}", colorSpace.getType(), colorSpace.isCS_sRGB());
for (int x = 0; x < image.getWidth(); x++) {
for (int y = 0; y < image.getHeight(); y++) {
final int color = image.getRGB(x, y);
final int red = (color & 0x00ff0000) >> 16;
final int green = (color & 0x0000ff00) >> 8;
final int blue = color & 0x000000ff;
// Constructing a weighted average of the three color bands
// based on how much they contribute to the overall brightness
// of a pixel. (Relative luminance - https://en.wikipedia.org/wiki/Relative_luminance)
final double greyscaleBrightness = .2126 * red + .7152 * green + .0722 * blue;
histogram[(int) greyscaleBrightness]++;
}
}
return histogram;
}
/**
* @param histogram
* @return an int array representing the cumulative histogram of the given histogram.
* @see <a href="https://en.wikipedia.org/wiki/Histogram#Cumulative_histogram">Cumulative Histogram</a>
*/
private static int[] getCumulativeHistogram(final int[] histogram) {
final int[] cumulativeHistogram = new int[histogram.length];
cumulativeHistogram[0] = histogram[0];
for (int i = 1; i < histogram.length; i++) {
cumulativeHistogram[i] = cumulativeHistogram[i - 1] + histogram[i];
}
return cumulativeHistogram;
}
/**
* @param cumulativeHistogram
* @return the minimum index where the cumulative histogram goes above the threshold set by CLIP_PERCENT.
*/
private static int getMinIndex(final int[] cumulativeHistogram) {
final int maxValue = cumulativeHistogram[cumulativeHistogram.length - 1];
final double clipThreshold = CLIP_PERCENT * (maxValue / 100.0) * .5;
int minIndex = 0;
while (cumulativeHistogram[minIndex] < clipThreshold) {
minIndex++;
}
return minIndex;
}
/**
* @param cumulativeHistogram
* @return the maximum index where the cumulative histogram goes below the threshold set by CLIP_PERCENT.
*/
private static int getMaxIndex(final int[] cumulativeHistogram) {
final int maxValue = cumulativeHistogram[cumulativeHistogram.length - 1];
final double clipThreshold = CLIP_PERCENT * (maxValue / 100.0) * .5;
int maxIndex = cumulativeHistogram.length - 1;
while (cumulativeHistogram[maxIndex] >= maxValue - clipThreshold) {
maxIndex--;
}
return maxIndex;
}
/**
* @param image
* @param alpha
* the scaling factor to adjust the contrast.
* @param beta
* the offset factor to adjust the brightness.
* @return the adjusted image.
*/
private static BufferedImage adjustBrightnessAndContrast(final BufferedImage image, final double alpha, final double beta) {
BufferedImage processedImage = new BufferedImage(image.getWidth(), image.getHeight(), image.getType());
processedImage = new RescaleOp((float) alpha, (float) beta, null).filter(image, processedImage);
LOG.debug("alpha: {} beta: {}", (float) alpha, (float) beta);
return processedImage;
}