Как многие помнят, я работаю в лаборатории, где мы работаем с живыми и не очень организмами. Науку двигаем, короче. Обычно вперед. Иногда в качестве образцов нам достаются мертвые обезьяны, ткани которых потом идут на экспериментальные задачи. Выглядит обычно это крайне жизнерадостно. Раздается звонок в 11 часов вечера, и тебе сообщают, что в питомнике обезьянка убилась. Почти не поврежденная, соседи только сердце съели. Вздыхаем, лезем в расписание рейсов и едем в аэропорт. На месте тебе выдают нужные запчасти убиенной и складывают в прозрачный контейнер с консервационным раствором. В аэропорт с этим тащиться уже нельзя, так как ограничен провоз жидкостей. Идем на ж/д вокзал на экспресс до Краснодара. Милые девушки на контроле как правило приобретают восхитительный салатовый оттенок при виде медленно кружащихся органов в нежно-розовом растворе.
В-общем, привезли, нарезали все, что нужно ломтиками, покрасили… Но тут оказывается, что полученные исходники нужно обработать и посчитать в автоматическом режиме… Сразу хочу уточнить, что я врач-исследователь, а не профессиональный программист или математик. Поэтому, если что-то покажется ошибочным — буду рад правкам.
Формулируем задачу
Итак, в наличии есть серия срезов окрашенных двумя красителями. Голубоватый — гематоксилин Майера, коричневый — DAB хромаген. На этой иллюстрации пищевод обезьяны, а коричневым окрашены маркеры на панкератин. В принципе, это неважно. Главное, что у нас есть два красителя, которые могут одновременно окрашивать одни и те же структуры. Красить нужно одновременно обоими, иначе непонятно строение среза. Беда в том, что часто для подсчета объектов или определения площади структур с положительной реакцией на антитело нам нужно иметь изображение, где присутствует только один из пигментов. То есть задача может быть сформулирована примерно как "разделить обратно смесь кетчупа с майонезом". И здесь в игру вступает алгоритм цветовой деконволюции.
Цветовая деконволюция
Я постараюсь не влезать в глубокие дебри математики, так как сам с трудом ее понимаю. Цвет пикселя на изображении микропрепарата определяется спектром поглощения света в этой точке. Спектр поглощения соответственно зависит от того, какой пигмент поглощает свет. Однако, у нас неприятная ситуация, когда в одной точке одновременно присутствуют условный синий и коричневый краситель. То есть их спектры накладываются друг на друга.
I — значение интенсивности для данной длины волны. с — концентрация красителя, бета — коэффициент пропорциональности, который зависит от физических свойств данного вещества. То есть, по факту, нам нужно оценить тот вклад который вносит каждый из красителей в красный, зеленый и синий каналы пикселя. Для нашей задачи мы использовали алгоритм A.C. Ruifrok в модификации G. Landini, который написал плагин для открытого ПО ImageJ. ImageJ — софт для пакетной обработки изображения, написанный на Java. Крайне гибкий, позволяет обмазывать его самыми различными плагинами и писать под него скрипты. Сами мы ленивые, пусть за нас машина тонны изображений перемалывает. Но для начала нужно дать ей образцы, относительно которых нужно производить разделение.
Образцы получаем с помощью моноокрашивания срезов. Получаем таким образом RGB цвет опорного эталона. Естественно, что все манипуляции производятся при одинаковой экспозиции и балансе белого. Скармливаем плагину и получаем нужные нам значения:
if (myStain.equals("Custom DAB")){
// This is the Custom DAB
MODx[0]=0.66504073;
MODy[0]=0.61772484;
MODz[0]=0.41968665;
MODx[1]=0.4100872;
MODy[1]=0.5751321;
MODz[1]=0.70785;
MODx[2]=0.6241389;
MODy[2]=0.53632;
MODz[2]=0.56816506;
Теоретически можно взять готовые предустановленные лабораторные образцы. На практике получается, что в каждой лаборатории красители немного отличаются по оттенку, что делает крайне желательной калибровку на каждой партии. Для того, чтобы этот профиль появлялся в списке вариантов, придется засунуть это в исходный java-файл. Инструкция на странице разработчика. Чем лучше определены векторы красителей, тем ближе комплементарное изображение к белому — то, что остается после выковыривания наших пигментов. Итак, барабанная дробь и результат темной магии:
Пакетная обработка
Теперь нам нужно, чтобы все это безобразие работало в пакетном варианте. Мы же не хотим тыкать вручную в каждый файл? ImageJ позволяет писать пользовательские скрипты, что удобно для подобных задач. Собственно, создается обычный файл в формате txt, в котором описаны манипуляции с вашим исходником. У меня получилось примерно так:
dir1 = getDirectory("Choose Source Directory ");
dir2 = getDirectory("Choose Destination Directory ");
list = getFileList(dir1);
setBatchMode(true);
for (i=0; i<list.length; i++) {
showProgress(i+1, list.length);
open(dir1+list[i]);
imgName=getTitle();
run("Colour Deconvolution", "vectors=[Custom DAB]");
//close windows we don't need
selectWindow(imgName + "-(Colour_3)");
close();
selectWindow(imgName +"-(Colour_1)");
title = getTitle();
saveAs("JPG", dir2 + title);
close();
selectWindow(imgName + "-(Colour_2)");
title = getTitle();
saveAs("JPG", dir2 + title);
close();
}
Теперь можно скармливать полученные изображения софту, заточенному для распознавания объектов в биологических препаратах. Но это уже в следующей статье)
UPD. Автор алгоритма приводит достаточно интересные варианты применения — выявление надписей, залитых чернилами другого цвета (Beinecke Rare Book and Manuscript Library, Yale University)
UPD2. Вот эти милые бабуинчики очень любят убивать друг друга нахрен в иерархических стычках.
Комментарии (37)
Meklon
02.09.2015 14:14+3В следующем посте будем помол кофе оценивать автоматическими методами. Из снимков микроскопа) извините, что медленно, но времени немного.
Cheater
02.09.2015 16:23+4Норм, хотя лично мне непривычно видеть манипуляции с изображениями через скрипт, говорящий системе «нажми на этот пункт меню, нажми на окошко, сохрани файл указанный в этом окошке...». Как минимум такой подход требует изучать внутренние скриптовые команды пакета — зачем? Манипуляциями с файлами (сообщать java-приложению пути к файлам, или спрашивать эти пути у юзера) должен заниматься шелл, дело приложения — тупо конвертировать поток символов:
ls source*.jpg | xargs -I {} java_app -batch «script.java» -infile {} -outfile {}.converted
Потому что если например захотите передать выход приложения не в файл, а на вход convert (захотелось разрешение уменьшить), или там напрямую распознающему софту, без всяких промежуточных jpg, то весь скрипт придётся переписывать заново.
Если не секрет, как называется этот брутальный вид мартышек, которые ВЫРЫВАЮТ И ЖРУТ СЕРДЦА УМЕРШИХ СОБРАТЬЕВ? x_xMeklon
02.09.2015 16:36+3Бабуины. Агрессивные твари на редкость. Они вообще крайне склочные. Постоянные стычки иерархические. Еще очень милые слегка плотоядные капуцины могут подобное сотворить.
Meklon
02.09.2015 16:37+2Насчет костыльности и неоптимальности соглашусь. Но это из варианта — не стреляйте в пианиста, он играет как умеет. Я не знаю Java)
Moskus
02.09.2015 23:11+1С точки зрения программиста, такое решение — абсурд, а с точки зрения пользователя-врача, пользователя-биолога и т.п. — очень даже годное решение, потому что оно позволяет ему не изучать API модулей ImageJ (которых сотни).
Используемый макроязык позволяет ему достаточно просто формально описать то, что он уже умеет делать, используя GUI.Meklon
03.09.2015 13:09+1Согласен. Жизни не хватит, чтобы одновременно исследованиями заниматься, выучить все языки подряд и еще на баяне играть сбоку. Я сейчас и так зарылся в основы электроники и программирования микроконтроллеров. Тоже та еще тема для непрофессионала. Хотя работает и не взрывается, на удивление.
Alexeyslav
03.09.2015 17:04+1Да, кстати на удивление. Ибо я знаю товарища, у которого даже правильно собранные схемы сразу же сгорали со спецэффектами, хотя им взяться в тех схемах просто не от куда, даже переполюсовка не приводит к таким эффектам. И электронные часы у него не приживались категорически — неделю работают потом начинают дико отставать/уходить и вскоре просто перестают работать. Короче дикость какая-то с электроникой происходит с которой он соприкасается. В то же время точно такие же часы у меня работают 20-й год(даже наверно больше)… с точностью +-30 сек в год.
Moskus
04.09.2015 03:38Я просто насмотрелся на решения, которые порождают коллективы биологов, когда у них нет ни одного прикрепленного технаря. В худшем смысле этого, напоминает компьютеризацию середины девяностых годов.
Meklon
04.09.2015 08:21У меня все относительно прилично. С owncloud, автоматизацией и разными плюшками. Хотя опыта иногда не хватает в некоторых областях.
Moskus
04.09.2015 08:39Вопрос личной философии. Я вот не люблю программировать и этого не делаю почти, но уважаю автоматизацию во всех ее полезных формах. И знаю кучу инструментов, которые позволяют не программировать или программировать минимально. Потому хорошо представляю, чего можно добиться такими средствами. И когда вижу, что народ собирает данные наблюдений в текстовые файлы в произвольной форме (ну все равно что записывать в блокнот, только перепечатывать не надо) или вручную сортируют что-то, а также рисуют карты в Photoshop, мне удивительно: неужели им не лень и не жаль времени, которое можно потратить на что-то еще? Картинки массово приводят к одному размеру вручную, например… И так далее.
Meklon
04.09.2015 13:09Это мрак, да. Все, что может быть автоматизировано — должно быть автоматизировано.
darthslider
08.09.2015 10:34Друг в кадастровой палате работал, выписки делал. Так как друг знал магию MS Office, а местные девочки нет, то он делал 250 выписок в день, а девочки по 30.
Meklon
08.09.2015 13:18Ага. А зарплата одинаковая. Поэтому часто и саботируют все это.
darthslider
08.09.2015 15:49Хуже того, в подобных коллективов особо умных сотрудников обычно всячески выживают, т.к. на их фоне все остальные смотрятся не очень. А друг взвыл, уволился через пару месяцев и уже давно зарабатывает игрой в покер не выходя из дома.
RabbitGP
02.09.2015 18:00+1Как интересно и даже непривычно, такой подход очень неординарный, хотя прогресс, скоро всё будет выдаваться в скриптах. Вообще статья понравилась, очень познавательная. Пока учусь на биолога, но думаю скоро придётся так же работать с пигментацией растений.
Спасибо за статьюMeklon
02.09.2015 18:14+1Тогда заранее подскажу полезный софт — Cellprofiler. Там вообще полный фарш можно творить с подсчетом клеток, структур, статистики и прочей обработки.
RabbitGP
02.09.2015 18:36Здорово, надеюсь пригодится. С меня подписка, уж очень интересные у Вас статьи и оригинальные.
Meklon
02.09.2015 19:35Рад, что нравится) я вообще странными вещами занимаюсь)
RabbitGP
02.09.2015 20:10Насколько странными?
Meklon
02.09.2015 20:14+4Очень) например планирование замкнутого взрыва куска мяса в герметичном сосуде в среде жидкости с последующим исследованием. Применение программы для подсчёта клеток в качестве анализатора полимерных нанокластеров. И вообще я стоматолог.
withkittens
02.09.2015 23:00замкнутого взрыва куска мяса в герметичном сосуде
А это зачем, если не секрет?
Grisha_Kirilin
04.09.2015 11:25Срезы очень красивые, особенно в негативе. Я, вдохновившись вашими картинками, написал свой алгоритм на Wolfram Mathematica.
sergehog
04.09.2015 11:56Кажется что мат. модель всё-же должна быть другая. У вас фотографии на белом фоне, чем больше красителя — тем сильнее он удаляется от белого. Я бы использовал такую формулу:
I(x,y) = 1 — a(x,y)A — b(x,y)B, где A и B не синий и коричневый, а их обратные цвета (1-синий), (1-коричневый). А 1 (единица), соответственно белый цвет.
Впрочем, поскольку задача линейная, всё и так работает.
Meklon
Жаль нам математику толком не давали. Врачам она обычно нужна в очень усечённом виде.
nikitasius
Как и чистописание^^
Meklon
Я решил эту проблему проще) Стараюсь не писать ничего руками. А почерк кошмарен, да.