Как поменять белый фон на черный

В недавнем проекте мне приходилось манипулировать изображениями, но, поскольку это для меня ново, я отчасти потерян.

Мне нужно сканировать руку с помощью обычного устройства сканирования. Я мог бы сделать это, но фон белый, и мне нужно, чтобы он был черным. После нескольких дней исследований по поиску способа изменения цвета я получил только изображение, которое кажется вырезанным и вставленным в мс краску.

Исходное изображение:

https://s31.postimg.org/4oe0xvjx7/Scan.jpg

Тестовое задание:

https://s31.postimg.org/7pdpgr5p7/test.jpg

Что мне нужно, это что-то вроде этого:

https://s31.postimg.org/4ti8qx863/pies1.jpg

Я пытался использовать Marvin Framework, Imagej, Catalano Framework. Чтобы увидеть нужные мне настройки, я использую gimp, редактор marving, приложение fiji (но без получения результата, который я искал).
Я думаю, что мне нужно преобразовать в оттенки серого, применить какой-то порог, но в определенном диапазоне цветов использовать альфа-цвета (но я не нашел пути, только порог для двоичных изображений), а затем применить маску в оригинал, используя пороговое изображение в градациях серого, но опять же, я не знаю, как это сделать, непосредственно в 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));
        }
    }
}

Проблема в том, что этот подход меняет свет всех пикселей, в идеале нужно менять только цвет вне объекта. Я искал идеи в интернете и нашел тезис Мартина Яника о ортопедическом анализе. Он предлагает следующий алгоритм:

  1. применить фильтр Гаусса к сканированию ног, чтобы получить отфильтрованное изображение
  2. порог отфильтрованного изображения, чтобы получить двоичное изображение
  3. морфологически закрыть двоичное изображение, чтобы получить закрытое двоичное изображение
  4. применить гауссовскую фильтрацию к двоичному изображению, чтобы получить маску в градациях серого
  5. примените эту маску для сканирования ног, чтобы получить общее изображение ног

И с этим я могу получить следующий результат:

Алгоритм Мартина Яника

И это близко к тому, что я хочу, потому что цвета в объекте не затрагиваются. Но все же одна проблема - белая граница вокруг объекта. Это потому, что я использую плагин MarvingBeMask и он просто поддерживает двоичные изображения (ну, не только двоичные изображения, но может маскировать только один цвет). Я думаю, что новый плагин необходим для объединения с использованием маски изображения в градациях серого, когда цвет был в диапазоне 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, я сделал следующее: имея маску в оттенках серого в верхнем слое, примените функцию color to alpha к белому цвету, дайте такой результат:

https://s32.postimg.org/p45k3rxs1/image.png

С помощью алгоритма, который я написал для marving framework, я получаю следующее изображение:

https://s32.postimg.org/u8tfzjni9/image.png

Разница в том, что мой алгоритм не может снизить интенсивность цвета, когда на исходном изображении присутствует более белый цвет, вы можете увидеть этот эффект, сравнивая 2 изображения. Есть идеи, как с этим бороться? Это результат изображения после применения комбинации слоев в gimp:

https://s32.postimg.org/pog1g9e4l/image.png

4 ответа

Лучше всего зайти в фотомагазин или в GIMP и использовать функцию быстрой маски, чтобы нарисовать или изобразить то, что вы хотите, и вы можете получить очень подробную информацию до пикселя, затем выйти из режима быстрой маски и убедиться, что белый цвет - это часть ваших походных муравьев вокруг и удалите его оттуда и залейте черным фоном слой. Вы можете использовать небольшое перо по краям, чтобы смешать его естественно. Есть более конкретные уроки о том, как это сделать, но так я бы подошел.

Подход зависит от цели вашего анализа. Первый подход влияет на все изображение, поэтому текстура ладони меняется! Второй подход влияет только на границу руки!

Оба подхода были разработаны с использованием 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 collor для затемнения ярких областей изображения с учетом некоторого порога насыщенности и значения. Этот подход не влияет на текстуру ладони.

выход:

введите описание изображения здесь

источник:

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

Вероятно, вам следует начать с преобразования RGB в HSL (оттенок, насыщенность, яркость / яркость), а затем применить кривую к яркости, чтобы значения выше определенного уровня (слишком яркие) постепенно снова возвращались к черному. Точно до того момента, когда яркость должна следовать диагонали, перед тем как упасть, будет зависеть от яркости и контрастности фотографий. (Я предполагаю, что это, вероятно, стандартные функции в графических структурах.)

Вот два примера кривых, примененных к яркости, чтобы продемонстрировать, какой результат вы получите:

введите описание изображения здесь введите описание изображения здесь

Это может показаться глупым предложением, но можете ли вы отсканировать руку с открытой крышкой сканера? Единственная причина, по которой у вас белый фон, заключается в том, что внутренняя крышка сканера белая и отражает свет самого сканера. Если вы откроете крышку и отсканируете руку в темной комнате, у вас должен получиться эффект, очень близкий к фотографии ступней, на которую вы ссылаетесь.

Не программное решение, но оно может приблизить вас к желаемому результату. Это также может помочь с перенасыщением вашего указательного и среднего пальцев.

Другие вопросы по тегам