Данный текст представляет собой документацию к
пакету "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 нм
соответствуют вершинам, а не усреднению со слипшимися 
краями толстых линий, привело к уродливой форме итоговых
кривых. Следовало бы, напр., вручную нарисовать другим цветом
узкую кривую, угадываемую человеком, а исходную толстую кривую -
удалить, но наша цель - показать принципы, поэтому ограничиваемся
тем, что есть.


----------------------------------