【问题标题】:How to change white background for black如何将白色背景更改为黑色
【发布时间】:2016-08-17 12:04:08
【问题描述】:

在最近的一个项目中,我必须处理图像,但由于这对我来说是新的,所以我有点迷茫。

我需要使用常规扫描设备扫描手部。我可以完成这个,但背景是白色的,我需要它是黑色的。经过几天的研究,找到了改变颜色的方法,我只得到了一张似乎在 ms paint 中剪切和粘贴的图像。

原图:

测试:

我需要的是这样的:

我尝试使用Marvin FrameworkImagejCatalano 框架。要查看我需要的 setps,我使用 gimp、marving 编辑器、斐济应用程序(但没有得到我想要的结果)。
我想我需要的是转换为灰度,应用某种阈值,但在一定的颜色范围内使用 alpha 颜色(但我没有找到方法,只有二进制图像的阈值),然后将蒙版应用到原始使用阈值灰度图像,但我不知道如何直接使用 Java 或使用我上面提到的任何框架。
任何帮助将不胜感激。

更新 根据m69所说,我尝试使用发光值,从rgb转换为hsl。我只设置了较暗的颜色。

首先尝试使用 0.5 的光阈值:

使用 0.9 的光阈值进行第二次尝试

float threshold = 0.5f;

for(int y=0; y<imageIn.getHeight(); y++){
    for(int x=0; x<imageIn.getWidth(); x++){

        int r = imageOut.getIntComponent0(x, y);
        int g = imageOut.getIntComponent1(x, y);
        int b = imageOut.getIntComponent2(x, y);
        float[] hsl = HSLColor.fromRGB(r, g, b, null);

        if(hsl[2] >= threshold){
            float t = (hsl[2]-0.5f)*2f; 
            hsl[2] -= t;
            imageOut.setIntColor(x, y, HSLColor.toRGB(hsl));
        }
    }
}

问题在于这种方法会改变所有像素的光线,理想的情况是只改变物体外部的颜色。我在互联网上寻找一个想法,我找到了 Martin Janík 的一篇关于骨科分析的论文。他提出了下一个算法:

  1. 对足部扫描应用高斯滤波以获得滤波图像
  2. 对过滤后的图像进行阈值化得到二值图像
  3. 形态上关闭二值图像得到闭合二值图像
  4. 对二值图像应用高斯滤波得到灰度蒙版
  5. 将此蒙版应用于足部扫描以获得整体足部图像

有了这个我可以得到下一个结果:

这与我想要的很接近,因为对象中的颜色没有被触及。但是对象周围的白色边框仍然存在问题。这是因为我正在使用 combineByMask marving 插件,它只支持二进制图像(不仅是二进制图像,而且只能屏蔽一种颜色)。我认为需要一个新插件来使用灰度图像蒙版进行组合,当颜色在 1-255 范围内时,尝试与图像基础组合以获得更暗或更亮的颜色(当颜色为 255 时,它应该离开只是基本图像的颜色)。

这是我所说的屏蔽灰度图像的示例图像:

我认为这是我要走的路。

更新 2

在做了一些研究之后,我想我已经接近我想要的结果了。我使用的算法是:

  1. 应用五个对比度
  2. 转换为灰度图像
  3. 应用高斯滤波器
  4. 图像阈值
  5. 形态接近
  6. 再次应用高斯滤波器
  7. 将此结果图像用作原始图像的灰度蒙版

这是结果图片:

这接近我想要的。在第六步中,我可以应用两次、三次或更多次高斯滤镜,给出更柔和的边框效果,但由于扫描图像的性质,最后总是显示一个细的白色边框(我认为这是我可以做到的)不处理)但我对这个结果很满意。现在因为我没有找到应用灰度掩码的 java 算法,所以我编写了以下代码:

for(int y=0; y<mask.getHeight(); y++){
        for(int x=0; x<mask.getWidth(); x++){

            //ya que está en grayscale, los 3 valores son los mismos
            int r1 = mask.getIntComponent0(x, y);
            int g1 = mask.getIntComponent1(x, y);
            int b1 = mask.getIntComponent2(x, y);

            int r2 = image.getIntComponent0(x, y);
            int g2 = image.getIntComponent1(x, y);
            int b2 = image.getIntComponent2(x, y);

            //al color de salida, le asignamos la luminicencia de la imagen mascara
            int r = 0, g = 0, b = 0;
            if(r1 > 0 || r2 > 0){
                r = r1*r2/Math.max(r1, r2);
            }

            if(g1 > 0 || g2 > 0){
                g = g1*g2/Math.max(g1, g2);
            }

            if(b1 > 0 || b2 > 0){
                b = b1*b2/Math.max(b1, b2);
            }

            image.setIntColor(x, y, r, g, b);
        }
    }

而且工作得非常好,但有一点我无法解决的小细节。想法是像在 gimp 中那样混合图像,我做了以下操作:在上层设置灰度蒙版,将颜色应用于 alpha 函数到白色,给出以下结果:

使用我为marving框架编写的算法,我得到了下一张图片:

不同的是,我的算法不能在原始图像上有更多白色时降低强度颜色,你可以看到比较两个图像的效果。知道如何处理吗?这是在gimp中应用图层组合后的图像结果:

【问题讨论】:

  • 我认为白色边框来自于在曲线的阈值处使用突然的方向变化,而不是像我在示例中所做的那样使用圆形弯曲。检查您的框架是否有任何方法可以为亮度创建样条曲线,或者用自己的部分圆或其他东西滚动。
  • 如果你还在做这个项目,并且想进一步讨论,你可以在 ImageJ 论坛上发帖:forum.imagej.net。那里有很多图像处理专家,他们可能很感兴趣,可以为您的分析提供建议和努力。

标签: java algorithm image-processing imagej marvin-framework


【解决方案1】:

您最好的选择是进入 photo shop 或 GIMP 并使用快速蒙版功能在您想要的内容中画入或画出,您可以获得非常详细的像素,然后退出快速蒙版模式并确保白色是部分你的行军蚂蚁在附近并从那里删除它继续并用背景填充黑色图层。您可能会在边缘使用轻微的羽毛使其自然融合。有更多关于如何做到这一点的具体教程,但这就是我的做法。

【讨论】:

  • 感谢您的回答,但我需要软件自动执行此操作。
【解决方案2】:

您可能应该首先将 RGB 转换为 HSL(色相、饱和度、亮度/亮度),然后将曲线应用于亮度,以便高于某个级别(太亮)的值逐渐恢复为黑色。亮度在下降之前应该遵循对角线的确切程度取决于照片的亮度和对比度。 (我猜这些可能是图形框架中的标准函数。)

以下是两个应用于亮度的曲线示例,以展示您将获得的结果:

【讨论】:

    【解决方案3】:

    方法取决于您的分析目的。第一种方法会影响整个图像,因此手掌的纹理会发生变化!第二种方法只影响手部边框!

    这两种方法都是使用 Marvin Image Processing Framework 开发的。

    输入:

    方法 1:

    根据用户 m69 的建议,基于灰度值的颜色转换。

    输出:

    来源:

    import marvin.image.MarvinImage;
    import marvin.io.MarvinImageIO;
    
    public class ScanTest {
        public static void main(String[] args) {
            MarvinImage image = MarvinImageIO.loadImage("./res/scan.jpg");
            int r,g,b;
            for(int y=0; y<image.getHeight(); y++){
                for(int x=0; x<image.getWidth(); x++){
                    r = image.getIntComponent0(x, y);
                    g = image.getIntComponent1(x, y);
                    b = image.getIntComponent2(x, y);
    
                    int gray = (int)((0.22*r)+(0.7*g)+(0.08*b));
                    double t = transform(gray, 1.3);
                    image.setIntColor(x, y, (int)(r*t), (int)(g*t), (int)(b*t));    
                }
            }
            MarvinImageIO.saveImage(image, "./res/scan_out.jpg");       
        }
    
        private static double transform(int gray, double brightness){
            if(gray < 127){
                return brightness;
            }
            else{
                return (1-((double)(gray-127)/128))*brightness;
            }
        }
    }
    

    方法 2:

    考虑到一些饱和度和值阈值,您可以使用 HSV 颜色模型使图像的明亮区域变暗。这种做法不会影响手掌的纹理。

    输出:

    来源:

    public class ScanTest {
        public static void main(String[] args) {
            MarvinImage image = MarvinImageIO.loadImage("./res/scan.jpg");
            int r,g,b;
            int rgb[] = new int[1];
            double hsv[];
            for(int y=0; y<image.getHeight(); y++){
                for(int x=0; x<image.getWidth(); x++){
                    r = image.getIntComponent0(x, y);
                    g = image.getIntComponent1(x, y);
                    b = image.getIntComponent2(x, y);
                    rgb[0] = image.getIntColor(x, y);
                    hsv = MarvinColorModelConverter.rgbToHsv(rgb);
    
                    if(r >= 235 && g >= 235 && b >=235){
                        image.setIntColor(x, y, 0,0,0);
                    }
                    else if(hsv[1] <= 0.12 && hsv[2] >= 0.6){
                        double diff = 1-hsv[2];
                        if(diff > 0.02){
                            diff = Math.max(diff,0.2);
                        }
                        diff*=3;
                        image.setIntColor(x, y, (int)(r*diff), (int)(g*diff*0.75), (int)(b*diff*0.75));
                    }
                }
            }
            MarvinImageIO.saveImage(image, "./res/scan_out.jpg");       
        }
    }
    

    【讨论】:

      【解决方案4】:

      这似乎是一个非常愚蠢的建议,但是您可以在扫描仪盖打开的情况下扫描您的手吗?您有白色背景的唯一原因是扫描仪的内盖是白色的,并且会反射扫描仪本身的光。如果您打开盖子并在黑暗的房间中扫描您的手,您应该会得到与您引用的脚的照片非常接近的效果。

      不是软件解决方案,但它可能会让您更接近您想要的结果。它还可以帮助解决食指和中指的过饱和问题。

      【讨论】:

      • 没有人,嘴唇张开仍然扫描白色背景,但感谢您的建议。
      • 这很奇怪,我昨晚用我的扫描仪做了一个测试,它工作正常。您是否在黑暗的房间中打开盖子进行扫描?今晚晚些时候我会发布一些我的照片。
      猜你喜欢
      • 2019-12-13
      • 2019-07-01
      • 2020-04-03
      • 1970-01-01
      • 1970-01-01
      • 2014-01-19
      • 2011-02-12
      • 1970-01-01
      • 2015-05-30
      相关资源
      最近更新 更多