【问题标题】:Simple Image Correction for Older Android Cameras旧 Android 相机的简单图像校正
【发布时间】:2017-11-21 16:33:06
【问题描述】:

我有一个 android 应用程序,我试图让用户使用本机相机捕获文档的图像,然后将其发送到服务器以进行黑白处理。

在 Android 应用程序中,我关注 this example 以从相机中拍摄一张全尺寸照片。

我遇到的问题是旧的 Nexus 4 相机导致图像较暗,因此文档的某些部分变为黑色而不是白色(主要是文档边缘周围)。但使用较新的 Nexus 6 摄像头则没有问题。

使用this code 更改对比度/亮度非常适合提高 Nexus 4 图像的质量,但会洗掉较新的 Nexus 6 图像。

有没有一种方法可以自动检测来自较旧 Android 设备的较暗、质量较差的图像,以便仅对这些图像应用图像校正?

使用this code 简单地计算图像的平均亮度会被文档周围的深色背景的可能性所抛弃。

【问题讨论】:

    标签: java android android-camera2


    【解决方案1】:

    我最终在 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;
    }
    

    【讨论】:

      【解决方案2】:

      可能标准不是亮度,而是对比度。无论如何,挑战是艰巨的,你不能期望完美的流程,但要准备好通过反复试验来处理它。

      尝试以正常对比度拍照;如果失败,请设置一个标志以下次使用额外的亮度。请注意,野外的设备可能具有非常不同的相机行为,即使使用同一部手机,照片条件也会有所不同。

      您可以将 Nexus 4 的标志初始化为高亮度。实际上,我不确定这是否适用于所有 Nexus 4 设备,或者仅适用于某些设备。

      如果您的应用将被大量使用,您可以收集每个模型的统计数据,并在安装应用时将其用作初始猜测。

      【讨论】:

        猜你喜欢
        • 2014-07-30
        • 2015-11-16
        • 1970-01-01
        • 2020-11-10
        • 2014-08-12
        • 2017-12-28
        • 2020-10-13
        • 2012-06-02
        • 1970-01-01
        相关资源
        最近更新 更多