How to change white background for black

寵の児 提交于 2019-12-12 13:24:55

问题


In a recent project I have to manipulate images, but since this is new to me I am kind of lost.

I need to scan the hand using a regular scan device. I could acomplish this but the background is white and I need it to be black. After several days of research finding the way to change the color, I only got an image that seems cut and paste in ms paint.

The original image:

Test:

What I need is something like this:

I trying using Marvin Framework, Imagej, Catalano framework. To see the setps that I need I use gimp, marving editor, fiji app (but without getting the result I was looking for).
I think what I need is to convert to gray scale, apply some sort of threshold but in a certain range of colors use alpha colors (but I did not find the way, only threshold for binary images), and then apply a mask into the original using the threshold grayscaled image, but again I don't know how to do that, in Java directly, or using any of the frameworks I mentioned above.
Any help would be appreciated.

UPDATE Based on what m69 said, i tried playing whith luminiscent value, converting from rgb to hsl. I only set darker colors which were lighten.

First try with a threshold of 0.5 of light:

Second try with a a threshold of 0.9 of light

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));
        }
    }
}

The problem is that this approach changes the light of all pixels, the ideal is to change only color outside of the object. I was looking for a ideas on the internet and i found a thesis by Martin Janík for orthopaedic analisis. He proposes the next algorithm:

  1. apply Gaussian filtering to the feet scan to get filtered image
  2. threshold the filtered image to get binary image
  3. morphologically close the binary image to get closed binary image
  4. apply Gaussian filtering to the binary image to get grayscale mask
  5. apply this mask to the feet scan to get overall foot image

And with this i can get the next result:

And this is close to what i want, because colors in the object are not touched. But still one problem the white border around the object. This is because i am using combineByMask marving plugin and it just support binary images (well not just binary images but only can mask one color). I think a new plugin is needed to combine using a grayscale image mask, when the color were in the range 1-255 try to combine to the image base to get a darker or lighten color (obiously when the color is 255, it should leave just the color of the base image).

This is an example image of what i am talking about masking grayscale images:

I think this is the path i am going to take.

UPDATE 2

After doing some research, i think i am close to the result i want. The algorithm i used was:

  1. Apply a contrast of five
  2. Convert to gray image
  3. Apply gaussian filter
  4. Threshold the image
  5. Morphological close
  6. Applay a gaussian filter again
  7. Use this result image as a grayscale mask to the original

This is the result image:

This is close to what i want. In the six step i could apply two, three or more times a gaussian filter, given a more soft border effect, but in the end a thin white border is always displayed because of the nature of scanned image (i think this is something i can not deal with) but i am comfortable with this result. Now as i did not found a java algorithm to apply a grayscale mask, i code this:

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);
        }
    }

And work almost very well, but with a little tiny detail that i can not resolve. The idea is to mix the images as in gimp i did the following: having the grayscale mask in a upper layer, apply a color to alpha function to the white color, give this result:

With the algorithm i wrote for marving framework, i get the next image:

The difference is that my algorithm can not low the intensity color when there is a more white color on the original image, you can see that effect comparing the 2 images. Any idea how to deal with it? This is the image result after applying the layer combination in gimp:


回答1:


Your best bet is to go into photo shop or GIMP and use the quick mask feature paint in or out what you want and you can get very detailed down to the pixel then exit quick mask mode and make sure the white is the part your marching ants are around and delete it from there go ahead and fill a layer for black with background. You may have use a slight feather on the edges to blend it in naturally. There are more specific tutorials on how to do this but that is how I would approach it.




回答2:


The approach depends on the purpose of your analysis. The first approach affects the entire image, therefore the texture of the palm of the hand is changed! The second approach only affects the hand border!

The both approaches were developed using Marvin Image Processing Framework.

INPUT :

APROACH 1:

As sugested by user m69, a color tranformation based on the gray scale values.

output:

source:

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;
        }
    }
}

APROACH 2:

You can use HSV collor model to darken bright areas of the image considering some saturation and value threshold. This approach does not affect the texture of the palm of the hand.

output:

source:

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");       
    }
}



回答3:


You should probably start by converting RGB to HSL (Hue, Saturation, Luminance/Brightness) and then apply a curve to the luminance so that values above a certain level (too bright) gradually go back to black again. Exactly up to which point the luminance should follow the diagonal before dropping off would depend on the lightness and contrast of the photos. (I'm guessing these are probably standard functions in graphics frameworks.)

Here are two examples of curves applied to luminance to demonstrate the kind of result you'd get:




回答4:


This might seem like a really stupid suggestion, but can you scan your hand with the scanner lid open? The only reason you have a white background is because the inside lid of the scanner is white and is reflecting light from the scanner itself. If you open the lid and scan your hand in a dark room, you should have an effect that is very close to the photo of feet you referenced.

Not a software solution, but it might get you closer to your desired result. It could also help with the oversaturation on your index and middle fingers.



来源:https://stackoverflow.com/questions/36817851/how-to-change-white-background-for-black

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!