\documentclass[a4paper,12pt]{extarticle} \usepackage{/home/eddy/ed, verbatim} \title{Практикум \No6: обработка изображений} \author{}\date{}\nocolon \long\def\task#1{\par\noindent\leavevmode\refstepcounter{sect}\llap{\textbf{\thesect}\;}\indent\textit{#1}\par} \def\t#1{{\upshape\ttfamily #1}} \usepackage{etoolbox} \makeatletter \preto{\@verbatim}{\topsep=2pt \partopsep=1pt} % remove big space before verbatim \AtEndEnvironment{verbatim}{\vspace{5pt}} \makeatother \begin{document} \maketitle \section{Работа с изображениями в Octave} \subsection{FITS-файлы} Наиболее популярной утилитой просмотра FITS-файлов является \t{ds9}. Для работы с обычными изображениями в Octave используются функции \t{imread}, \t{iminfo}, \t{imshow}. Основные функции обработки изображений находятся в пакете \t{image}. FITS-файлы~--- пакет \t{fits}. Узнать входящие в состав пакета функции можно так: \begin{verbatim} pkg describe -verbose fits --- Package name: fits Version: 1.0.7 Short description: The Octave-FITS package provides functions for reading, and writing FITS (Flexible Image Transport System) files. This package uses the libcfitsio library. Status: Loaded --- Provides: Reading and writing FITS files read_fits_image save_fits_image save_fits_image_multi_ext \end{verbatim} Для чтения используем \t{read\_fits\_image}: \begin{verbatim} [I H] = read_fits_image('stars.fit'); imshow(I,[1000 4000]) \end{verbatim} Необходимо указывать пороги интенсивностей для \t{imshow}. Более точно можно определить диапазоны из статистики по изображению: \begin{verbatim} Imin = min(I(:)) Imin = 1321 Imax(I(:)) Imax = 52826 Imean = mean2(I) Imean = 2054.6 Istd = std2(I) Istd = 1275.5 Imed = median(I(:)) Imed = 2009 Low = Imin; High = Imed + Istd; imshow(I, [Low High]); % or: imshow(I/High) \end{verbatim} Как видите, изображение повернуто вправо на $90\degr$. Если мы захотим отображать изображения как в оригинале, нужно заранее повернуть их: \begin{verbatim} I = rot90(I); imshow(I, [Low High]) \end{verbatim} Следует помнить, что в Octave координаты изображения начинаются с верхнего левого угла (и ось~Y направлена вниз), а в стандарте FITS~--- с левого нижнего (и ось~Y направлена вверх). Убедимся в этом: \begin{verbatim} I2=I; I2([1:100],[1:100])=65000; imshow(I2, [Low High]); \end{verbatim} Сравним, как отображаются координаты пикселя в ds9. \subsection{Работа с гистограммой} Для улучшения отображения попробуем эквализовать гистограмму: \begin{verbatim} HE = histeq(I/High, 65536); imshow(HE); % compare to HE = histeq(I/High, 256); \end{verbatim} Посмотрим, как меняется гистограмма изображения в зависимости от распределения шума. Сгенерируем сначала пробное изображение: \begin{verbatim} I = uint8(zeros(300,300)); I([1:50 250:end], :) = 200; I([51:249], [1:30 270:end]) = 200; I([101:199], [100:200]) = 100; imshow(I) imhist(I) Iuniform = I + uint8(50*rand(size(I))); imshow(Iuniform) imhist(Iuniform) Inorm = I + uint8(25*randn(size(I))); imshow(Inorm) Inorm = uint8(I + 25*randn(size(I))); imshow(Inorm) imhist(Inorm) Ipoisson = imnoise(I, "poisson"); imshow(Ipoisson) imhist(Ipoisson) Isaltpepper = imnoise(I, "salt & pepper", 0.2); imshow(Isaltpepper) imhist(Isaltpepper) Ispeckle = imnoise(I, "speckle", 0.03); imshow(Ispeckle) imhist(Ispeckle) \end{verbatim} Сравним с эквализацией гистограммы: \t{imshow(histeq(Inorm))}. Попробуем применить к изображению гамма-коррекцию: \begin{verbatim} imshow(imadjust(Iuniform, [], [], 0.5)) imshow(imadjust(Iuniform, [], [], 5)) \end{verbatim} Теперь попробуем преобразовать яркость кусочно-линейной функцией $$\begin{cases} I_{out} = 0.2 I_{in}, & I_{in} < 70,\\ I_{out} = 14 + 1.5 (I_{in}-70), & 70 \le I_{in} < 130, \\ I_{out} = 209 + 0.368*(I_{in}-130),& 130\le I_{in} < 256. \end{cases} $$ \begin{verbatim} Ilinhyst = zeros(size(Inorm)); idx = find(Inorm < 70); Ilinhyst(idx) = 0.2*Inorm(idx); idx = find(Inorm >= 70 & Inorm < 130); Ilinhyst(idx) = 14 + 1.5*(Inorm(idx)-70); idx = find(Inorm >= 130); Ilinhyst(idx) = 209 + 0.368*(Inorm(idx)-130); Ilinhyst = uint8(Ilinhyst); imshow(Ilinhyst); imhist(Ilinhyst) \end{verbatim} Попробуем сделать ступенчатое преобразование яркости: на изображении \t{Inorm} заменим все интенсивности в диапазоне $[50, 150]$ на 100: \begin{verbatim} Ilinhyst = Inorm; Ilinhyst(find(Inorm >= 50 & Inorm <= 150)) = 100; imshow(Ilinhyst) imhist(Ilinhyst) \end{verbatim} \subsection{Фильтрация изображений} Попробуем простую медианную фильтрацию на примере пары изображений. \begin{verbatim} subplot(2,2,1) subimage(Isaltpepper) subplot(2,2,2) subimage(medfilt2(Isaltpepper, [5 5]))) subplot(2,2,3) subimage(Inorm) subplot(2,2,4) subimage(medfilt2(Inorm, [5 5])) \end{verbatim} Видим, что медианный фильтр вполне неплохо справился с шумом <<соль-перец>> и значительно хуже~--- с гауссовым шумом. Рассмотрим применение различных фильтров. Для начала создадим матрицы фильтров: \begin{verbatim} fgauss = fspecial("gaussian", 5, 1); flog = fspecial("log", 5, 1); fsobelv = fspecial("sobel"); fsobelh = rot90(fsobelv); \end{verbatim} И применим их к оригинальному изображению, \t{Isaltpepper} и \t{Inorm}: \begin{verbatim} imshow(imfilter(I, fgauss, "symmetric")); imshow(imfilter(Isaltpepper, fgauss, "symmetric")); imshow(imfilter(Inorm, fgauss, "symmetric")); imshow(histeq(imfilter(I, flog, "symmetric"))); imshow(histeq(imfilter(Isaltpepper, flog, "symmetric"))); imshow(histeq(imfilter(Inorm, flog, "symmetric"))); \end{verbatim} Как видите, у дифференциального фильтра отсекается отрицательная компонента~--- т.к. изображения имеют тип \t{uint8\_t}, однако, можно сделать так: \begin{verbatim} imshow(histeq(imfilter(double(Isaltpepper), flog, "symmetric"))); imshow(histeq(imfilter(double(Inorm), flog, "symmetric"))); imshow(histeq(imfilter(double(I), flog, "symmetric"))); \end{verbatim} Octave неправильно рассчитывает значение фильтров, например: \begin{verbatim} sum(flog(:)) ans = -0.024092 sum(fgauss(:)) ans = 1.00000 sum(fsobelv(:)) ans = 0 \end{verbatim} У дифференциальных фильтров сумма элементов должна быть равна нулю (чтобы константные уровни превращались в нуль), а у усредняющих~--- единице (чтобы сохранять среднюю интенсивность по изображению). Расширим размер матрицы фильтра лапласиана гауссианы, чтобы улучшить приближение к нулю: \begin{verbatim} flog = fspecial("log", 17, 1); imshow(histeq(imfilter(double(I), flog, "symmetric"))); imshow(histeq(imfilter(double(Isaltpepper), flog, "symmetric"))); imshow(histeq(imfilter(double(Inorm), flog, "symmetric"))); \end{verbatim} Теперь на примере первого изображения мы видим, что фильтр работает правильно. Горизонтальная и вертикальная компоненты градиента: \begin{verbatim} ih = imfilter(double(Inorm), fsobelh, "symmetric"); iv = imfilter(double(Inorm), fsobelv, "symmetric"); imshow(histeq(iv))); imshow(histeq(ih)); grad = sqrt(ih.*ih + iv.*iv); imshow(histeq(grad)); \end{verbatim} Если мы попытаемся проделать то же самое для \t{I}, увидим, что \t{histeq} в данном случае не сработает, и отображать придется командой \begin{verbatim} imshow(grad,[min(grad(:)) max(grad(:))]); \end{verbatim} Здесь \t{histeq} не сработал из-за слишком <<кривой>> гистограммы: подавляющее большинство пикселей приходится на нулевой уровень. Для смягчения общего контраста можно использовать прием, называемый <<нерезким маскированием>> (применяемый еще в фотографии на фотоэмульсию). Для этого воспользуемся формулой $$I_{out}=N\cdot I_{in} - F(I_{in}),$$ где $F$~-- некоторый усредняющий фильтр (в оригинале~--- расфокусированное позитивное изображение). \begin{verbatim} imshow(2*Inorm-imfilter(Inorm, fgauss, "symmetric")); \end{verbatim} Посмотрим, как изменится размытое изображение при нерезком маскировании: \begin{verbatim} ig = imfilter(I, fgauss, "symmetric"); subplot(1,2,1) imshow(ig) subplot(1,2,2) imshow(histeq(2*ig-imfilter(ig, fgauss, "symmetric"))) \end{verbatim} Очистим все и вернемся к FITS-файлам. Загрузим изображение и построим дифференциальные фильтры с более крупным ядром: \begin{verbatim} clear [I H] = read_fits_image('stars.fit'); imshow(histeq(I,65536)); fgauss = fspecial("gaussian", 30, 5); flog = fspecial("log", 37, 5); \end{verbatim} Попробуем теперь применить к изображению нерезкое маскирование и различные фильтры: \begin{verbatim} imshow(histeq(I-imfilter(I, fgauss, "symmetric"), 65536)); imshow(histeq(imfilter(I, fgauss, "symmetric"), 65536)); imshow(histeq(imfilter(I, flog, "symmetric"), 65536)); imshow(histeq(2*I-imfilter(I, fgauss, "symmetric"), 65536)); imshow(histeq(medfilt2(I), 65536)); imshow(histeq(I-medfilt2(I, [15 15]), 65536)) \end{verbatim} Последний вариант является первым приближением к вычитанию фона. Посмотрим, как можно найти приближенную маску для поиска звезд: \begin{verbatim} Imed = medfilt2(I, [3 3]); % [3 3] is default, we can omit it here ImedLG = imfilter(Imed, flog, "symmetric"); imshow(ImedLG, [-0.1, 0.1]); mask = logical(zeros(size(I))); \end{verbatim} Теперь нужно подобрать подходящий пороговый уровень: \begin{verbatim} mask = logical(zeros(size(I))); mask(find(ImedLG < 0)) = 1; imshow(mask) mask = logical(zeros(size(I))); mask(find(ImedLG < -0.005)) = 1; imshow(mask) \end{verbatim} Уже такое грубое приближение позволило обнаружить некоторое количество звезд на изображении. Если использовать более точный (<<робастный>>) метод выделения фона (а здесь фон достаточно перекошен), получатся результаты куда лучше. Отобразим оригинал, заменив нулями все точки, где маска истинна, а затем~--- наоборот: \begin{verbatim} J=I; J(mask)=0; % hide stars imshow(histeq(J, 65536)); J=I; J(~mask)=0; % hide background imshow(J); \end{verbatim} Наша грубая маска определила все звезды, а также некоторое количество шумов, но при помощи морфологических операций мы можем удалить мелкие объекты (правда, не факт, что не потеряем таким образом слабые звезды; кроме того, мы видим на первом изображении, что особо яркие звезды маскированы не полностью, наш способ достаточно плох и годится лишь для очень грубой астрометрии). \subsection{Морфологические операции, поиск связных областей} Попробуем подчистить немного нашу маску. \begin{verbatim} imshow(imopen(mask, strel('disk', 1, 0))); imshow(imopen(mask, strel('disk', 2, 0))); imshow(imopen(mask, strel('disk', 3, 0))); % use this mask = imopen(mask, strel('disk', 3, 0)); \end{verbatim} Теперь можем аналогично просмотреть маскированные изображения: \begin{verbatim} J=I; J(mask)=0; % hide stars imshow(histeq(J, 65536)); J=I; J(~mask)=0; % hide background imshow(J); \end{verbatim} Но нам интересней выделить все звезды из изображения. Используем поиск связных областей: \begin{verbatim} [L NUM]= bwlabel(mask, 8); NUM NUM = 200 imshow(L, [1 NUM]) \end{verbatim} Как видим, нумерация объектов идет слева-направо, сверху-вниз. Чтобы выделить конкретный объект, мы можем применить маску такого типа: \begin{verbatim} J = zeros(size(I)); idx = find(L == 100); J(idx) = I(idx); imshow(J); \end{verbatim} Мы выделили одну звезду. Можем выделить тонкую полоску фона вокруг нее: \begin{verbatim} mask100 = logical(zeros(size(I))); mask100(idx) = 1; mask100 = xor(imdilate(mask100, strel('disk',2,0)), mask100); imshow(mask100); \end{verbatim} И посчитать по ней статистику: \begin{verbatim} bg100 = I(mask100); mean(bg100) ans = 2063.1 std(bg100) ans = 61.711 \end{verbatim} Но для вычисления положений центроидов удобней использовать другую функцию поиска связных областей: \t{bwconncomp}. \begin{verbatim} K = bwconncomp(mask, 8); p = regionprops(K); \end{verbatim} По умолчанию в \t{p} (это~--- массив структур) содержатся поля \t{Area} (площадь объекта), \t{BoundingBox} (координаты бокса, в котором содержится объект: левый верхний угол и размер) и \t{Centroid} (геометрический центр объекта). По первым двум параметрам мы можем отфильтровать слишком мелкие и слишком крупные объекты, а также объекты, у которых отношение ширины к высоте сильно отличается от единицы (т.е. там внутри~--- явно не круг). Третий параметр позволяет судить о примерном расположении центра объекта (более точно, конечно, мы можем вычислить его лишь взяв данные с оригинального изображения, вычтя фон и посчитав точный центроид). Можно нарисовать несколько боксов: \begin{verbatim} rectangle('Position', p(1).BoundingBox, 'EdgeColor','g','LineWidth',2) rectangle('Position', p(3).BoundingBox, 'EdgeColor','g','LineWidth',2) rectangle('Position', p(100).BoundingBox, 'EdgeColor','g','LineWidth',2) \end{verbatim} \begin{verbatim} centroids = cat(1, p.Centroid); plot(centroids(:,1), centroids(:,2), 'ko'); \end{verbatim} Теперь сравним координаты центроида изображения звезды с координатами центроида маски. Обратите внимание, что \t{BoundingBox} имеет координаты <<с половинками>>: \begin{verbatim} p(100).BoundingBox 502.500 710.500 14.000 14.000 imshow(mask100); rectangle('Position', p(100).BoundingBox, 'EdgeColor','g','LineWidth',2) \end{verbatim} Это связано с тем, что при отображении изображений целым координатам соответствует центр пикселя. Контур, описывающий нашу область интереса, не должен пересекать пиксели, из-за этого координаты его левого угла на 0.5\,пикселя меньше, а ширина~--- на 1\,пиксель больше. Следовательно, для того, чтобы получить часть изображения, содержащую только пиксели зоны интереса, нам нужно будет сделать преобразование: \begin{verbatim} Bstart = p(100).BoundingBox(1:2) + 0.5; Bsize = p(100).BoundingBox(3:4) - 1; rectangle('Position', [Bstart Bsize], 'EdgeColor','r','LineWidth',2); \end{verbatim} Теперь наш бокс включает все пиксели изображения звезды. Вычислим суммарный поток и центроид: \begin{verbatim} bg = mean(bg100); % mean background [y x] = ind2sub(size(I), idx); % coordinates of pixels in mask of 100th star M = sum(I(idx)) - numel(idx)*bg % energy in circle M = 55148.54545 xc=0; yc=0; for n=1:numel(x); i=I(idx(n))-bg; xc+=i*x(n); yc+=i*y(n); printf("%d(%d,%d)\n",i,x(n),y(n)); endfor; xc/=M; yc/=M; xc % X centroid position xc = 509.36 yc % Y centroid position yc = 718.41 p(100).Centroid % centroid by mask ans = 509.63 717.57 imshow(histeq(I, 65536)); % take a look at real image % real centroid: rectangle('Position', [xc-1 yc-1 2 2], 'EdgeColor','g','LineWidth',2, "Curvature", 1) % centroid of mask: rectangle('Position', [p(100).Centroid-1 2 2], 'EdgeColor','r','LineWidth',2, "Curvature", 1) \end{verbatim} Мы видим, что настоящий центр тяжести изображения звезды достаточно сильно отличается от геометрического центра маски. Особенно сильно это проявляется в случае деформаций изображения (вследствие сдвига изображения, комы или прочих аберраций). Вычислять большое количество характеристик удобней иначе. Например, так: \begin{verbatim} regioninfo = regionprops(L,I,'MajorAxisLength','MinorAxisLength','PixelIdxList', 'PixelValues'); \end{verbatim} мы получим значения большой и малой полуосей эллипса с идентичными нашей фигурой моментами (что позволит судить о <<круглости>> каждого объекта), список индексов пикселей и список интенсивностей пикселей на изображении. Если бы фон у нашего объекта был идеально гладким, можно было бы добавить еще свойство \t{'WeightedCentroid'}, чтобы сразу получить координаты центра каждой звезды, но, увы, для каждой звезды придется считать фон аналогично вышесказанному~--- через кольца. Для тесных полей потребуется убедиться, что зона вокруг звезды свободна от другой звезды (при помощи все тех же морфологических операций). Аргументом \t{regionprops} может выступать и структура, полученная при помощи \t{bwconncomp}. Теперь найдем все объекты более-менее круглой формы: \begin{verbatim} roundness = [regioninfo.MajorAxisLength] ./ [regioninfo.MinorAxisLength]; staridx = find(roundness < 1.1 & roundness > 0.9); % indexes of "stars" imshow(ismember(L, staridx)); % show filtered image \end{verbatim} Теперь, если пройтись итеративно по каждому объекту с номером \t{staridx}, можно посчитать для него индивидуальную маску фона (как было показано выше~--- через дилатацию), определить таким образом уровень фона и использовать его для вычисления прочих параметров: полной интенсивности внутри маски и координат центроида. Если мы добавим в \t{regionprops} свойство \t{'Area'}, у нас появится возможность до получения номеров объектов, возможно являющихся звездами, отсортировать их по убыванию площади (скорей всего~--- и по убыванию яркости): \begin{verbatim} [~, i] = sort([regioninfo(staridx).Area], 'descend'); % get sorted indexes staridxsorted = staridx(i); \end{verbatim} И можем отобразить окрестности первых 10 по площади маски: \begin{verbatim} imshow(histeq(I, 65536)); for i=1:10; c = hsv2rgb([i/10. 1 1]); rectangle('Position', p(staridxsorted(i)).BoundingBox, 'EdgeColor', c, 'LineWidth',2); endfor for i = 1:10; ltxt{i} = sprintf("star #%d", i); endfor legend(ltxt) \end{verbatim} Видим, что у ярких звезд маска потеряла приличную часть крыльев, а также одна достаточно яркая звезда была пропущена. Более надежным будет отсортировать по значению интенсивности. \section{Первичная обработка снимков} Наиболее распространенными светоприемниками в астрофизике оптического диапазона являются ПЗС-матрицы. Среди прочих светоприемников этого диапазона они имеют наилучшие характеристики. Однако, есть у них и недостатки: в тонких подложках возможна интерференция (особенно ближе к ИК-диапазону), приводящая к возникновения <<фрингов>>; а наличие хотя бы одного очень яркого источника приводит к <<растеканию заряда>>, усугубляющемуся при считывании изображения. КМОП-светоприемники в значительной мере избавлены от проблем растекания заряда, но в них есть другая проблема: каждый пиксель по сути снабжен собственным усилителем, в результате точная фотометрия с КМОП превращается в сплошной ад! Процесс получения изображений на ПЗС можно в упрощенном виде свести к формуле: $$ ADU_{x,y}= \gamma F_{x,y}(e_{x,y} + D_{x,y})T + B_{x,y} + R,$$ (здесь опущен процесс дискретизации, растекания заряда и т.п.), где $ADU$~-- отсчеты, считанные с пикселя с координатами $(x,y)$; $\gamma$~-- <>, коэффициент преобразования фотоэлектронов в $ADU$ (зависит от длины волны излучения); $F$~-- поглощение излучения вследствие неидеальности оптической системы (компенсируется делением на <<плоские поля>>); $e$~-- количество фотоэлектронов, возникших при попадании кванта в данную ячейку (зависит от длины волны), оно зашумляется множеством факторов: фотонным шумом, турбулентностью атмосферы и т.д., и т.п.; $D$~-- <<темновой ток>>, определяющийся электронами, возникающими в результате тепловых процессов в теле полупроводника; $B$~-- <>, начальное смещение (даже при нулевой экспозиции~--- собственно, так кадры <> и получают) для устранения нелинейности; $T$~-- время накопления сигнала (экспозиция); $R$~-- шум считывания. Избавиться от шума считывания невозможно, однако, как мы помним, если усреднить $N$~кадров, то $R$~уменьшится в $\sqrt{N}$~раз. Кроме того, чем больше экспозиция, тем большим будет вклад прочих шумов. Однако, при малых экспозициях может проявляться <<эффект затвора>>, вызванный тем, что время открывания\slash закрывания затвора конечно. Чтобы скорректировать интенсивность на уровень смещения, мы можем сделать как можно больше кадров с нулевой экспозицией (<>), получить медиану стопки изображений и вычесть из изображения. При этом шум bias'а также будет уменьшен в $\sqrt{N}$~раз. Для коррекции на тепловой шум нам необходимо накопить как можно больше <<дарков>> (в идеале их экспозиция должна быть такой же, как экспозиция кадра данных, а условия должны быть абсолютно такими же: температура чипа, окружающей среды и т.д., и т.п.). При закрытом затворе мы получим: $$ ADU_{x,y}= \gamma D_{x,y}T + B_{x,y} + R,$$ т.е. <<автоматически>> сюда же включен уровень смещения. Следовательно, если наши <<дарки>> имеют ту же самую экспозицию, что и основной научный кадр, то для удаления тепловых шумов достаточно вычесть медианное усреднение стопки <<дарков>> из нашего кадра. Однако, не всегда это возможно: скажем, при получении спектра высокого разрешения экспозиция может длиться несколько часов! Естественно, мы не можем получить даже десятка темновых кадров с такой экспозицией (т.к. и условия поменяться успеют, и начнется новая ночь). В подобных случаях прибегают к хитрости: темновой ток пропорционален времени экспозиции. Это означает, что можно сделать несколько серий коротких экспозиций (скажем, одна, пять, десять и двадцать минут) в течение дня, а затем экстраполировать их. Вот в этом случае для получения такого <<искусственного дарка>> необходимо будет, помимо всех остальных кадров, снимать еще и bias'ы. Для каждой экспозиции~$T$ мы вычисляем темновой ток как $D_T=M(D_{Ti})-M(B_i)$, где $M$~-- медиана. При помощи линейной экстраполяции мы получаем значение~$D$ для заданной экспозиции. Но т.к. это значение уже избавлено от смещения, для коррекции нам необходимо будет из изображения вычесть не только~$D$, но и~$B$! Это~--- единственный случай, когда нам необходимо будет получить и стопку кадров смещения (как говорилось выше, если <<темновые>> сняты с той же экспозицией, что и основной кадр, то смещение автоматически корректируется при вычитании <<темновых>> из <<объекта>>). Значительно более сложной является коррекция на неоднородности оптической системы (вплоть до пылинок на фильтрах и прочих оптических поверхностях), $F$. Термин <<плоскость>> в данном случае носит двоякий смысл: для прямых снимков это~--- неоднородность освещенности по кадру. Для спектральных~--- еще и неоднородность кривой спектральной чувствительности. Отвлечемся пока от спектроскопии. Основными факторами, искажающими интенсивность объектов в кадре, являются виньетирование (экранирование боковых пучков апертурой оптического тракта) и прочие неоднородные свойства оптических систем. В идеальной ситуации для компенсирования~$F$ нам необходимо получить снимок абсолютно однородно засвеченного протяженного объекта с угловыми размерами, значительно превосходящими поле зрения нашей оптической системы. В случае телескопов с малым полем зрения для данных целей хватает вечерних или утренних снимков неба при максимально рассеянном солнечном свете во время гражданских сумерек. При большом поле зрения необходимо использовать специальные источники света с как можно более равномерным освещением. После получения <<плоских>> кадров следует вычислить медиану из их стопки, вычесть <<темновые>>, затем нормировать на максимум интенсивности, а далее~--- разделить подготовленный (с вычетом <<темновых>>) научный кадр на полученное изображение. При этом мы получаем: $$I_{x,y} = \frac{\gamma F_{x,y}e_{x,y}T + R}{\gamma F_{x,y}T + R}\approx e_{x,y}T.$$ Конечно, <<плоские>> кадры мы можем снимать с любой подходящей экспозицией. Но для коррекции на темновые токи, если экспозиция <<плоских>> отличается от экспозиции <<научных>> кадров, нам необходимо будет сделать еще один набор <<темновых>>. Как пример медианного усреднения стопки изображений, сгенерируем зашумленные изображения (можно использовать и \t{awgn}): \begin{verbatim} for N=1:10; II(:,:,N) = I+100*randn(size(I)); endfor imshow(histeq(II(:,:,2),65536)) \end{verbatim} Подобным образом формируются на ПЗС реальные изображения (разве что шумы мы несколько преувеличили). Именно из-за шумов для получения качественных кадров dark и flat необходимо сделать их как можно больше! После чего стопка изображений усредняется: \begin{verbatim} Imed = median(II, 3); subplot(2,2,1) imshow(histeq(II(:,:,2),65536)) subplot(2,2,2) imshow(histeq(II(:,:,5),65536)) subplot(2,2,3) imshow(histeq(Imed,65536)) subplot(2,2,4) imshow(histeq(I,65536)) \end{verbatim} Даже для шума такого уровня мы приблизились к оригиналу достаточно неплохо всего-то на десяти изображениях. Если же их сделать хотя бы сотню, восстановление будет еще лучше. А учитывая то, что медианный фильтр хорошо удаляет выбросы, такой способ отлично очищает опорные кадры от космических частиц. И для изображений объекта мы можем выполнять подобные манипуляции для снижения шума каждого $e_{x,y}$. Но, в этом случае процесс восстановления будет намного сложней, т.к., во-первых, телескоп имеет неидеальное сопровождение (и объекты не только немного <<размазываются>>, но еще и смещаются от экспозиции к экспозиции); во-вторых, в процессе получения изображений могут изменяться условия (seeing, температура и пр.), в результате чего PSF изображений также будет меняться (следовательно, придется сделать некоторую деконволюцию и приведение изображения к единой координатной системе, а это чревато ошибками интерполяции). \section{Задание для самостоятельного выполнения} В данных к заданию содержатся кадры object, dark и flat. Выполните следующее. \begin{enumerate} \item Из <<научных>> кадров выберите один с наилучшим качеством изображений. \item Вычислите медианные значения темновых и плоских, получите нормированное изображение. \item Подберите подходящий фильтр, который позволит получить маску, выделяющую изображения звезд. \item Подготовьте m-файлы для выполнения рутинных операций: вычисления маски кольца фоновых пикселей, определения уровня фона, освещенности от звезды и координат ее центроида. \item Отсортируйте маски по уменьшению площади, для дальнейших вычислений используйте первые пятьсот. \item Вычислите освещенности от звезд в пределах полученных масок. Отсортируйте по уменьшению освещенности. Выберите первые 20. \item Выполните астрометрию для этих самых ярких звезд кадра, получив координаты~$(x,y)$ центроидов. \item Проверьте полученные величины (преобразуйте ADU в звездные величины и проведите нормировку на стандартные объекты из каталогов; для координат центроидов учтите, что изображения повернуты на $90\degr$ и ось~$Y$ направлена вниз) по каталогам. Определите погрешность своих расчетов (звездных величин и координат). \end{enumerate} Для определения каталожных величин воспользуйтесь aladin, xephem, stellarium (и т.п.) или on-line сервисами каталогов. Преобразовать координаты~$(x,y)$ на изображении в $(\alpha, \delta)$ можно при помощи утилиты \t{xy2sky} из пакета \t{wcstools} (либо при помощи \t{ds9}). Не забывайте о собственных движениях звезд: координаты из каталога (в т.ч. те, которыми мы можем воспользоваться в \t{ds9}) привязаны к эпохе J2000, а с тех пор прошло уже два десятилетия. \end{document}