Как поменять белый фон на черный
В недавнем проекте мне приходилось манипулировать изображениями, но, поскольку это для меня ново, я отчасти потерян.
Мне нужно сканировать руку с помощью обычного устройства сканирования. Я мог бы сделать это, но фон белый, и мне нужно, чтобы он был черным. После нескольких дней исследований по поиску способа изменения цвета я получил только изображение, которое кажется вырезанным и вставленным в мс краску.
Исходное изображение:
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));
}
}
}
Проблема в том, что этот подход меняет свет всех пикселей, в идеале нужно менять только цвет вне объекта. Я искал идеи в интернете и нашел тезис Мартина Яника о ортопедическом анализе. Он предлагает следующий алгоритм:
- применить фильтр Гаусса к сканированию ног, чтобы получить отфильтрованное изображение
- порог отфильтрованного изображения, чтобы получить двоичное изображение
- морфологически закрыть двоичное изображение, чтобы получить закрытое двоичное изображение
- применить гауссовскую фильтрацию к двоичному изображению, чтобы получить маску в градациях серого
- примените эту маску для сканирования ног, чтобы получить общее изображение ног
И с этим я могу получить следующий результат:
И это близко к тому, что я хочу, потому что цвета в объекте не затрагиваются. Но все же одна проблема - белая граница вокруг объекта. Это потому, что я использую плагин MarvingBeMask и он просто поддерживает двоичные изображения (ну, не только двоичные изображения, но может маскировать только один цвет). Я думаю, что новый плагин необходим для объединения с использованием маски изображения в градациях серого, когда цвет был в диапазоне 1-255, попробуйте объединить его с основой изображения, чтобы получить более темный или более светлый цвет (очевидно, когда цвет равен 255, он должен оставить просто цвет базового изображения).
Это пример изображения того, что я говорю о маскировке изображений в градациях серого:
Я думаю, что это путь, по которому я собираюсь идти.
ОБНОВЛЕНИЕ 2
После некоторых исследований я думаю, что близок к желаемому результату. Алгоритм, который я использовал, был:
- Применить контраст пяти
- Преобразовать в серое изображение
- Применить гауссов фильтр
- Пороговое изображение
- Морфологический конец
- Снова примените фильтр Гаусса
- Используйте это изображение в качестве маски в градациях серого для оригинала
Это результат изображения:
Это близко к тому, что я хочу. На шестом шаге я мог бы применить фильтр Гаусса два, три или более раз, учитывая более мягкий эффект границы, но в конце концов всегда отображается тонкая белая рамка из-за природы отсканированного изображения (я думаю, что это то, что я могу не иметь дело с), но я доволен этим результатом. Теперь, когда я не нашел алгоритм 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:
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 (оттенок, насыщенность, яркость / яркость), а затем применить кривую к яркости, чтобы значения выше определенного уровня (слишком яркие) постепенно снова возвращались к черному. Точно до того момента, когда яркость должна следовать диагонали, перед тем как упасть, будет зависеть от яркости и контрастности фотографий. (Я предполагаю, что это, вероятно, стандартные функции в графических структурах.)
Вот два примера кривых, примененных к яркости, чтобы продемонстрировать, какой результат вы получите:
Это может показаться глупым предложением, но можете ли вы отсканировать руку с открытой крышкой сканера? Единственная причина, по которой у вас белый фон, заключается в том, что внутренняя крышка сканера белая и отражает свет самого сканера. Если вы откроете крышку и отсканируете руку в темной комнате, у вас должен получиться эффект, очень близкий к фотографии ступней, на которую вы ссылаетесь.
Не программное решение, но оно может приблизить вас к желаемому результату. Это также может помочь с перенасыщением вашего указательного и среднего пальцев.