Данный текст представляет собой документацию к
пакету "show_3d".
Ссылки:
http://aravidze.narod.ru/n1/bmp_xy.zip - собственно пакет;
http://aravidze.narod.ru/n1/bmp_xy.htm - копия документации
для ознакомления (продублирован вне архива только *.htm
без иллюстраций).
Данный текст описывает работу с пакетом "bmp_xy",
с версией от 2025,8,25.
Назначение пакета "bmp_xy" - оцифровка графиков
из иллюстраций к статьям и т.п., т.е. получение таблиц (x,y).
(Название связано с тем, что в первых версиях картинки
следовало переводить в формат *.bmp).
Ниже описаны примеры работы с этим пакетом. (Во всех
случаях предполагается, что рабочие файлы находятся в
текущем каталоге, а программы и модули - находятся в нём же,
или же доступны по path etc.).
Итак, имеется файл krasilxnikow2023_Fig12.png :
Этот файл вырезан из скачанной статьи:
V.N. Krasil'nikov et al., Vanadyl formate VO(HCOO)2*H2O
as a precursor for preparing nanoscale vanadium sesquioxide V2O3,
Ceramics International(2023), Journal Pre-proof.
Хотим оцифровать дифрактограмму в левой части, соответствующую
T=100K, и соответствующую ей эталонную дифрактограмму
из чёрточек (в левой части вверху).
Принципы работы с пакетом:
1. Если на графике имеются пересекающиеся разноцветные кривые,
для выбора цвета для выделения нужной кривой может оказаться
полезным просмотр распределения цветов в исходной картинке
по типу
anal_cwet.py krasilxnikow2023_Fig12.png
; пример такого просмотра будет дан ниже, но вначале рассмотрим
пример, когда такого усложнения нет.
2. Создаём файл описания для выделения требуемой кривой (на питоне,
имя - произвольное, напр., lew_100K.py - левая половина графика,
кривая для T=100K). Шаблон такого файла дан ниже.
3. orig_predw.py lew_100K.py - из картинки-оригинала получаем
предварительный вырезанный график (файл lew_100K_predw.png).
На этой стадии уточняем значения параметров, фигурирующих
в файле описания - чтобы нужный график был отличён от фона
и других графиков, и чтобы координаты в номерах пикселей были
корректно преобразованы в координаты на подписях к рисунку.
4. Копируем lew_100K_predw.png в lew_100K_okonc.png
(т.е. окончательный вырезанный график), запускаем
mspaint.exe lew_100K_okonc.png
, и удаляем всё не относящееся к оцифровываемому графику.
Если график был пересечён и затёрт другими графиками - нужно
дорисовать затёртые участки!
5. okonc_pontos.py lew_100K.py - из окончательного графика
получаем файл точек графика - lew_100K_pontos.py . С учётом диапазона
распознанных точек корректируем параметры x0 и x1 в файле
описания.
6. pontos_xy.py lew_100K.py - из файла точек графика получается
итоговый файл с интерполированными равномерно отстоящими точками -
lew_100K_xy.py . Одновременно получается иллюстрация -
lew_100K_xy.png .
7. Если полученный график содержит подозрительные выбросы
и т.п. - запускаем
show_pontos.py lew_100K.py 40 50
(здесь 40 и 50 - границы диапазона, который мы хотим рассмотреть) -
программа выводит графики точек графика и интерполированных
точек, что позволяет понять, откуда взялись выбросы,
после чего можно подредактировать файл *_okonc.png и,
повторив шаги 5..6, избавиться от них.
Ниже - шаблон файла описания.
-----------------------------------
from geom import *
imq0 = 'krasilxnikow2023_Fig12.png'
comment = "{ablon fajla opisaniq ocifrowywaemoj kriwoj."
def kriterij(color):
r,g,b = color
sirina = 30
t1 = (0,50,150)
gut1 = rasst(color,centr)<sirina
corn = (r<50) and (g<50) and (b<50)
gut = gut1 or corn
return(gut)
def preobr(ix,iy):
x=ix*2
y=1000.-iy
x = sdwig(382.,1232., 20.,70., x)
y = sdwig(790.,900., 0.,1., y)
return(x,y)
def treb_show(x,y):
g = True
# g = x>15 and x<80 and y>-1 and y<1.2
return(g)
x0,x1,dx = 21.9,73.96,0.02
----------------------------------------
Смысл констант и подпрограмм в файле описания:
imq0 - имя файла-картинки;
comment - для копирования в файл с оцифрованной кривой;
kriterij - распознавание (по цвету) точек, принадлежащих графику;
preobr - переход от координат в пикселях к "физическим
координатам", соответствующим подписям на графике;
в данном случае номера пикселей по горизонтали
382 и 1232 преобразуются в 2*theta = 20 и 70o,
а номера пикселей по вертикали 790 и 900 - в интенсивности
0 и 1.
treb_show - задание области, выводимой на экран при отладке;
начинать работу следует с g=True - выводить всю картинку,
а при уточнении значений для preobr удобно выводить один лишь
целевой график и/или оси координат.
x0,x1,dx - диапазон и шаг для вывода оцифрованного графика.
Файлы lew_100K.py и etalon_100K.py, соответствующие
находящимся в левой части исходной картинки экспериментальной
и эталонной кривым для T=100K, в которых проведена подгонка
всех параметров, и получаемые с их использованием файлы
*_okonc.png - находятся в подкаталоге primery/krasilxn .
При запуске orig_predw.py получаются отладочные картинки -
на их основе корректируем файлы описания, чтобы полностью
выделялась нужная кривая, и чтобы координаты в пикселях (ix,iy)
корректно преобразовывались в координаты (x,y) в единицах из подписей
(напр., в данном случае - в угол в градусах и в относительную
интенсивность).
Замечание: если картинка получена пересъёмкой, и оси
на ней изогнуты и/или неравномерно сжаты - следует в
подпрограмме preobr "расправлять" оси. А именно,
после каждого запуска orig_predw.py на экран выводится
результат текущего преобразования (ix,iy) в (x,y), и можно
принять решение вида - "до x=15 ось 0x идёт хорошо,
а дальше она сжата и поднята вверх - надо растянуть и
опустить", что делается вставкой в подпрограмму preobr
конструкций вида:
if x>15: x=x+(x-15)*0.1
if x>15: y=y-(x-15)*0.05
.
В данном случае отладочные картинки имеют следующий вид:

После отладки и окончательного запуска orig_predw.py получаем
картинки с предварительно выделенными искомыми кривыми:


После их редактирования посредством mspaint.exe получаем
окончательно выделенные кривые:


При этом размер картинки в пикселях - тот же, что у исходной
картинки, так что преобразование от (ix,iy) к (x,y) в файле описания
даст правильные значения (x,y).
После отработки okonc_pontos.py и pontos_xy.py получаем
результаты оцифровки - lew_100K_xy.py и etalon_100K_xy.py,
и результаты их визуализации - lew_100K_xy.png и etalon_100K_xy.png :

В lew_100K_xy.png выбросов нет, но имеются странные вихляния,
а в etalon_100K_xy.png - возле правого края заметен небольшой выброс.
Демонстрируем работу программы show_pontos.py для выявления природы
этих вихляний и этого выброса.
--------
show_pontos.py lew_100K.py 48 52

Как видим, вихляния на lew_100K являются следствием шума на
исходном графике. При этом шумовая дорожка на
исходном графике (много точек на каждой вертикали) даёт понять,
что измения сигнала в пределах дорожки - это шумы. Но после
оцифровки шумовая дорожка исчезает, а интерполяция между
точками исходного графика создаёт впечатление гладкой кривой без шумов,
но с вихляниями, и нефизичность этих вихляний становится ясной
лишь при увеличении картинки (синие кружочки - точки исходного
графика, красные крестики - интерполированные точки).
Для борьбы с такими вихляниями можно попытаться
увеличить шаг dx - в надежде, что тогда будет понятнее,
что перед нами не вихляния, а шумы. Но для практических
целей достаточно полученного результата оцифровки.
--------
show_pontos.py etalon_100K.py 72 76

А для etalon_100K сразу видно, откуда берётся выброс,
но возникает сомнение - неужели исходные точки на самом деле
были расположены таким странным образом.
Увеличив в mspaint'е *_okonc.png, мы убеждаемся,
что так и есть.
Для борьбы с этим ложным выступом можно отредактировать
*_okonc.png, но, опять-таки, для практических целей достаточно
полученного результата оцифровки.
--------
Теперь рассмотрим случай, когда имеются пересекающиеся
разноцветные кривые, причём эти кривые - толстые
и без резких границ (цвет плавно меняется при пересечении
таких кривых), фон и оси - не являются чисто белыми
или чёрными, и выбор цветов для предварительного выделения
как нужных кривых, так и осей - затруднителен. Для выбора
цветов в таком случае полезна программа anal_cwet.py .
В данном случае у нас имеется файл eu_tb.jpg (был найден
в поисковике при запросе примерного вида "lanthanide spectrum image",
ссылка утеряна); хотим оцифровать кривые для
люминесценции Eu3+ и Tb3+. Сам файл и связанные с ним
рабочие файлы находятся в подкаталоге primery/eu_tb .
Для демонстрации работы программы anal_cwet.py запускаем:
anal_cwet.py krasilxnikow2023_Fig12.png
anal_cwet.py eu_tb.jpg
; создаются файлы krasilxnikow2023_Fig12_rgb.png
и eu_tb_rgb.png . Рассмотрим их; рекомендуется также
рассмотреть их вне браузера с увеличением, обращая
внимание на подписи осей r, g, b.

Смысл: для файла картинки представим 3-мерный куб,
по осям - яркости каналов rgb (red, green, blue) - от 0 до 255;
строятся 3 проекции - rg, rb, gb; на проекции rg
для точки с заданными значениями r и g яркостью обозначается
число точек исходной картинки с данными значениями r и g,
и аналогично для rb и gb. Далее, эти проекции размещаем так,
что справа вверху показан "вид сверху" на этот куб -
т.е. проекция gb, а слева и снизу от этой проекции -
виды на этот куб с двух боковых сторон, так, что
сближенные на рисунке оси разных проекций совпадают.
Результат более нагляден для krasilxnikow2023_Fig12_rgb.png:
имеются три ярких пятна со следующими значениями rgb:
t1 = (200,150,50)
t2 = (50,100,150)
t3 = (0,50,150)
, и от каждой в точку белого цвета (255,255,255) тянется
хвост, соответствующий размытости линий - на краях происходит
плавный переход от окраски собственно линии к белому
цвету фона. (Попытки использовать плавность перехода для
более корректной оценки значения за счёт усреднения с весами,
как представляется, усложнили бы не только логику программы,
но и работу с ней, но вряд ли дали бы заметное улучшение).
Кроме того, имеется пространственная диагональ r=g=b,
соответствующая оттенкам серого (на границах подписей).
Кстати, в модуле geom.py для обработки таких хвостов
имеется подпрограмма ot_cilindra, а в примере lew_100K.py
дан пример работы с ней:
centr = (0,50,150)
t2 = LK(0.5,centr,0.5,(255,255,255)) # Hwost w storonu belogo.
gut1 = rasst(color,centr)<sirina
pro,d = ot_cilindra(centr,t2,color)
gut2 = pro>=0 and pro<=1 and d<sirina
; здесь pro - продольная координата вдоль цилиндра с
осью (centr,t2), а d - расстояние от оси цилиндра.
Как видим и в eu_tb.jpg, и в eu_tb_rgb.png, здесь
и фон не белый, и подписи не чёрные, и пятна -
неправильной формы с изогнутыми хвостами.
Выделяются три ярких пятна - два расположенных
близко друг к другу - (240,200,110) и (240,180,50),
и ещё одно - (190,60,60). Создаём для них файлы
описания - eu_tb_240_200_110.py, eu_tb_240_180_50.py,
eu_tb_190_60_60.py - и смотрим, что они выделяют из
исходной картинки.
Как видим, разные участки кривой для Eu3+ изображены
хоть и близкими, но разными цветами (а именно - соответствующими
упомянутым выше двум близким пятнам). А третье из упомянутых пятен
соответствует кривой "Red acceptor".
Можно тж. предроложить, что пятно на пространственной диагонали
соответствует кривой для Tb3+ (она серая). Но можно предложить
более непосредственный способ нахождения соответствия кривых
и соответствующих им пятен в пространстве rgb. А именно,
создаём копии файлa eu_tb.jpg - green.jpg, red.jpg, eu.jpg,
tb.jpg, в каждом выделяем участок соответствующей кривой
и дублируем его в хаотично выбранных местах много раз,
сохраняя при этом и участки иных кривых - чтобы пятна
других кривых не были полностью подавлены.
Из созданных файлов получаем файлы распределений цветов
*_rgb.png и изображаем каждый из них рядом с файлом распределения
цветов eu_tb_rgb.png для исходной картинки eu_tb.jpg .
--------
green:
--------
red:
--------
Eu3+:
--------
Tb3+:
--------
Это позволяет оперативно понять, какие именно пятна
на eu_tb_rgb.png соответствуют тем или иным кривым,
и написать в файлах описания критерии для отличения
нужной кривой от всего прочего.
А дальше обнаруживаем, что 450..580 нм кривые и для
Eu3+, и для Tb3+, идущие на уровне
интенсивности =0, перекрыты кривой "Red acceptor".
Для Eu3+ перекрывающая кривая "Red acceptor" хоть слабо,
но видна, что позволяет при редактировании восстановить
перекрытую часть кривой для Eu3+. Но для Tb3+
кривая "Red acceptor" полностью подавлена, в указанной области -
белое поле, опереться не на что, и восстановление
перекрытой части кривой становится менее надёжным.
Для борьбы с этим эффектом разрешаем вывод, кроме кривой
для Tb3+, также кривой "Red acceptor", а потом
при редактировании убираем её.
Так получаем:
Отладочные картинки:

Предварительно выделенные искомые кривые:


Отредактированные выделенные кривые:

Результат оцифровки:

Как видим, для Tb3+ - результат хорош, а для
Eu3+ - получилось уродливо: человек мысленно корректирует
нарисованные линии и понимает, что полоса вблизи 700 нм - двойная;
но программа при наивном усреднении провала не чувствует и выдаёт
просто уширенную линию. Удаление части точек с целью "подсказать"
программе, что там есть провал, и что максимумы полос 570..630 нм
соответствуют вершинам, а не усреднению со слипшимися
краями толстых линий, привело к уродливой форме итоговых
кривых. Следовало бы, напр., вручную нарисовать другим цветом
узкую кривую, угадываемую человеком, а исходную толстую кривую -
удалить, но наша цель - показать принципы, поэтому ограничиваемся
тем, что есть.
----------------------------------