Краткое руководство
Если вы хорошо разбираетесь в Julia или уже использовали другой пакет для обработки изображений, эта страница может помочь вам быстро приступить к работе. Если какие-либо термины или понятия покажутся вам странными, не волнуйтесь — в следующих разделах вы найдете гораздо более подробные пояснения.
Для начала загрузим пакет Images.jl:
julia> using Images
Изображения — это просто массивы
В большинстве случаев любой массив AbstractArray можно рассматривать как изображение. Например, числовой массив можно интерпретировать как изображение в оттенках серого.
julia> img = rand(4, 3)
4×3 Matrix{Float64}:
 0.517866   0.525796   0.374141
 0.749719   0.840641   0.728543
 0.552534   0.523948   0.00277646
 0.0834401  0.0128461  0.134685
Не волнуйтесь, если в качестве результата вы получили не изображение. Это ожидаемо, поскольку он в первую очередь распознается как числовой массив, а не изображение. Позже в документации по JuliaImages вы узнаете, как автоматически отображать массив как изображение.
Можно также выбрать интересующую нас область на более крупном изображении:
# создаем изображение, которое начинается с черного цвета в левом верхнем углу
# и становится ярче в правом нижнем углу
img = Array(reshape(range(0,stop=1,length=10^4), 100, 100))
# создаем копию
img_c = img[51:70, 21:70] # отображаем красную область
# создаем представление
img_v = @view img[16:35, 41:90] # отображаем синюю область
Как вы, возможно, знаете, изменение значения представления влияет на исходное изображение, тогда как изменение копии не влияет:
fill!(img_c, 1) # красная область на исходном изображении не меняется
fill!(img_v, 0) # синяя область меняется
Дополнительные пакеты расширяют возможности. Например, следующим образом:
using Unitful, AxisArrays
using Unitful: mm, s
img = AxisArray(rand(256, 256, 6, 50), (:x, :y, :z, :time), (0.4mm, 0.4mm, 1mm, 2s))
определяется 4-мерное изображение (3 пространственных измерения плюс одно временное) с указанными именем и физическим интервалом между пикселями для каждой координаты. Пакет AxisArrays поддерживает эффективные операции с такими массивами и может быть полезен для отслеживания не только расстояния между пикселями, но и принятой ориентации для многомерных изображений.
JuliaImages легко взаимодействует с AxisArrays и многими другими пакетами.  Еще ряд примеров:
- 
Пакет
ImageMetadata(включенный вImages) позволяет «помечать» изображения пользовательскими метаданными. - 
Пакет
IndirectArraysподдерживает индексированные изображения (цветовые карты). - 
Пакет
MappedArraysпозволяет представлять отложенные преобразования значений, облегчая работу с изображениями, которые могут не помещаться в память полностью. - 
Пакет
ImageTransformationsпозволяет кодировать как немедленные, так и отложенные повороты, наклоны, деформации и т. д. 
В Julia очень легко определять новые типы массивов (и, следовательно, специализированные изображения или операции) и обеспечивать их беспроблемное взаимодействие с подавляющим большинством функций JuliaImages.
Элементы изображений — это цветовые составляющие пикселей
Элементы изображения называются пикселями, причем в JuliaImages пиксели рассматриваются как самостоятельные объекты. Например, это может быть Gray для пикселей в оттенках серого, RGB для пикселей цветов RGB, Lab для цветов Lab и т. д.
Чтобы создать пиксель, инициируйте структуру одного из этих типов:
Gray(0.0) # черный
Gray(1.0) # белый
RGB(1.0, 0.0, 0.0) # красный
RGB(0.0, 1.0, 0.0) # зеленый
RGB(0.0, 0.0, 1.0) # синий
Изображение — это всего лишь массив пиксельных объектов:
julia> img_gray = rand(Gray, 2, 2)
2×2 Array{Gray{Float64},2} with eltype Gray{Float64}:
 Gray{Float64}(0.412225)   Gray{Float64}(0.755344)
 Gray{Float64}(0.0368978)  Gray{Float64}(0.198098)
julia> img_rgb = rand(RGB, 2, 2)
2×2 Array{RGB{Float64},2} with eltype RGB{Float64}:
 RGB{Float64}(0.709158,0.584705,0.186682)  …  RGB{Float64}(0.202959,0.435521,0.116792)
 RGB{Float64}(0.760013,0.693292,0.20883)      RGB{Float64}(0.0709577,0.888894,0.310744)
julia> img_lab = rand(Lab, 2, 2)
2×2 Array{Lab{Float64},2} with eltype Lab{Float64}:
 Lab{Float64}(14.7804,-55.7728,-50.3997)  …  Lab{Float64}(68.5718,-91.6112,127.303)
 Lab{Float64}(61.1431,62.8829,-55.5891)      Lab{Float64}(74.6444,-105.937,81.6293)
Цветовые каналы не являются отдельным измерением массива изображения; вместо этого изображение представляет собой массив пикселей. Как видите, оба изображения img_rgb и img_lab имеют размер    (а не    или   ).
| 
 Вместо типов   | 
Такой подход упрощает создание универсального кода, который может обрабатывать как изображения в оттенках серого, так и цветные изображения без необходимости введения дополнительных циклов или проверок цветового измерения. Он также обеспечивает более эффективную поддержку трехмерных изображений в оттенках серого, которые могут иметь размер 3 по третьему измерению, и, следовательно, упрощает взаимодействие специалистов по компьютерному зрению и обработке биомедицинских изображений.
Преобразование между цветами и отдельными цветовыми каналами
Преобразование между различными Colorant осуществляется очень просто:
julia> RGB.(img_gray) # Gray => RGB
2×2 Array{RGB{Float64},2} with eltype RGB{Float64}:
 RGB{Float64}(0.412225,0.412225,0.412225)     …  RGB{Float64}(0.755344,0.755344,0.755344)
 RGB{Float64}(0.0368978,0.0368978,0.0368978)     RGB{Float64}(0.198098,0.198098,0.198098)
julia> Gray.(img_rgb) # RGB => Gray
2×2 Array{Gray{Float64},2} with eltype Gray{Float64}:
 Gray{Float64}(0.576542)  Gray{Float64}(0.32965)
 Gray{Float64}(0.658013)  Gray{Float64}(0.578422)
| 
 В JuliaImages местами используется механизм трансляции. Если вы с ним не знакомы, обратитесь к документации.  | 
Иногда для работы с другими пакетами может потребоваться преобразовать изображение RGB размером    в числовой массив    или наоборот. Для этого служат функции channelview и colorview. Например:
julia> img_CHW = channelview(img_rgb) # 3 * 2 * 2
3×2×2 reinterpret(reshape, Float64, ::Array{RGB{Float64},2}) with eltype Float64:
[:, :, 1] =
 0.709158  0.760013
 0.584705  0.693292
 0.186682  0.20883
[:, :, 2] =
 0.202959  0.0709577
 0.435521  0.888894
 0.116792  0.310744
julia> img_HWC = permutedims(img_CHW, (2, 3, 1)) # 2 * 2 * 3
2×2×3 Array{Float64, 3}:
[:, :, 1] =
 0.709158  0.202959
 0.760013  0.0709577
[:, :, 2] =
 0.584705  0.435521
 0.693292  0.888894
[:, :, 3] =
 0.186682  0.116792
 0.20883   0.310744
Чтобы преобразовать массив channelview в массив пикселей, убедитесь в том, что первое измерение — это цветовой канал, и используйте colorview:
julia> img_rgb = colorview(RGB, img_CHW) # 2 * 2
2×2 Array{RGB{Float64},2} with eltype RGB{Float64}:
 RGB{Float64}(0.709158,0.584705,0.186682)  …  RGB{Float64}(0.202959,0.435521,0.116792)
 RGB{Float64}(0.760013,0.693292,0.20883)      RGB{Float64}(0.0709577,0.888894,0.310744)
| 
 Работать с изображениями лучше всего в представлении   | 
| 
 Мы используем порядок «канал-высота-ширина» (CHW) вместо HWC,  поскольку это обеспечивает удобный с точки зрения памяти механизм индексирования для   | 
С помощью `PermutedDimsArray` можно перевернуть ориентацию блока памяти без создания копии, либо можно воспользоваться `permutedims`, если нужна копия.
Для изображений типа Gray следующие фрагменты кода почти эквивалентны, за исключением того, что версия с созданием копирует данные, а версия с представлением — нет.
img_num = rand(4, 4)
img_gray_copy = Gray.(img_num) # создание
img_num_copy = Float64.(img_gray_copy) # создание
img_gray_view = colorview(Gray, img_num) # представление
img_num_view = channelview(img_gray_view) # представление
Шкала интенсивности 0—1
В JuliaImages при отображении изображений по умолчанию предполагается, что 0 означает «черный», а 1 — «белый» (или «насыщенный» для изображения RGB).
Важно отметить, что это относится даже к уровням интенсивности, закодированным как 8-разрядные целые числа, которые сами по себе охватывают диапазон от 0 до 255 (а не от 0 до 1). Это возможно благодаря специальному числовому типу N0f8, который интерпретирует 8-разрядное «целое число» так, как если бы оно было масштабировано по 1/255, то есть значения от 0 до 1 кодируются в 256 шагов.
N0f8 означает «нормализованное (Normalized), с 0 битов для целой части и 8 битами для дробной части (8 fractional bits)». Есть и другие типы, например N0f16 для работы с 16-разрядными изображениями (и даже N2f14 для изображений, полученных с помощью 14-разрядной камеры, и т. д.).
julia> img_n0f8 = rand(N0f8, 2, 2)
2×2 reinterpret(N0f8, ::Matrix{UInt8}):
 0.435  0.725
 0.816  0.192
julia> float.(img_n0f8)
2×2 Matrix{Float32}:
 0.435294  0.72549
 0.815686  0.192157
| 
 Эта инфраструктура позволяет нам унифицировать «целочисленные» изображения и  изображения с плавающей запятой и избежать потребности в специальных функциях преобразования (таких как    | 
Хотя это и не рекомендуется, но вы можете использовать rawview для получения базовых данных, хранящихся в памяти, и преобразовать их в UInt8 (или другой тип), если это непременно нужно.
julia> img_n0f8_raw = rawview(img_n0f8)
2×2 rawview(reinterpret(N0f8, ::Matrix{UInt8})) with eltype UInt8:
 0x6f  0xb9
 0xd0  0x31
julia> float.(img_n0f8_raw)
2×2 Matrix{Float64}:
 111.0  185.0
 208.0   49.0
Преобразование типа хранения, то есть фактического числового типа, без изменения типа цвета поддерживается следующими функциями:
julia> img = rand(Gray{N0f8}, 2, 2)
2×2 Array{Gray{N0f8},2} with eltype Gray{N0f8}:
 Gray{N0f8}(0.925)  Gray{N0f8}(0.267)
 Gray{N0f8}(0.345)  Gray{N0f8}(0.827)
julia> img_float32 = float32.(img) # Gray{N0f8} => Gray{Float32}
2×2 Array{Gray{Float32},2} with eltype Gray{Float32}:
 Gray{Float32}(0.92549)   Gray{Float32}(0.266667)
 Gray{Float32}(0.345098)  Gray{Float32}(0.827451)
julia> img_n0f16 = n0f16.(img_float32) # Gray{Float32} => Gray{N0f16}
2×2 Array{Gray{N0f16},2} with eltype Gray{N0f16}:
 Gray{N0f16}(0.92549)  Gray{N0f16}(0.26667)
 Gray{N0f16}(0.3451)   Gray{N0f16}(0.82745)
Если вы не хотите указывать конечный тип, на этот случай есть функция float:
julia> img_n0f8 = rand(Gray{N0f8}, 2, 2)
2×2 Array{Gray{N0f8},2} with eltype Gray{N0f8}:
 Gray{N0f8}(0.886)  Gray{N0f8}(0.98)
 Gray{N0f8}(0.522)  Gray{N0f8}(0.412)
julia> img_float = float.(img_n0f8) # Gray{N0f8} => Gray{Float32}
2×2 Array{Gray{Float32},2} with eltype Gray{Float32}:
 Gray{Float32}(0.886275)  Gray{Float32}(0.980392)
 Gray{Float32}(0.521569)  Gray{Float32}(0.411765)
Для преобразования наподобие представления без выделения новой памяти есть функция of_eltype из пакета MappedArrays:
julia> using MappedArrays
ERROR: ArgumentError: Package MappedArrays not found in current path.
- Run `import Pkg; Pkg.add("MappedArrays")` to install the MappedArrays package.
julia> img_float_view = of_eltype(Gray{Float32}, img_n0f8)
ERROR: UndefVarError: `of_eltype` not defined
julia> eltype(img_float_view)
ERROR: UndefVarError: `img_float_view` not defined
Массивы с произвольными индексами
Если над входным изображением выполняется какое-либо пространственное преобразование, каким образом пиксели (или вокселы) в преобразованном изображении будут сопоставляться с пикселями на входе? Благодаря поддержке в Julia массивов с индексами, начинающимися не с 1, можно сделать так, чтобы индексы массива представляли абсолютное положение в пространстве, что упрощает отслеживание одного и того же положения на нескольких изображениях. Дополнительные сведения см. в разделе Keeping track of location with unconventional indices.
Указатель функций
Дополнительные сведения о каждой из приведенных ниже категорий см. в разделе Сводка и справка по функциям. Приведенный ниже список доступен в REPL Julia по команде ?Images. Если ранее вы использовали другие фреймворки, вам также может быть интересен раздел Сравнение с другими фреймворками обработки изображений. Также можно обратиться к описанию пакетов ImageFeatures.jl и ImageSegmentation.jl, которые поддерживают ряд алгоритмов, важных для компьютерного зрения.
Конструкторы, преобразования и типажи:
- 
Создание: используйте конструкторы специализированных пакетов, например
AxisArray,ImageMetaи т. д. - 
«Преобразование»:
colorview,channelview,rawview,normedview,PermutedDimsArray,paddedviews - 
Типажи:
pixelspacing,sdims,timeaxis,timedim,spacedirections 
Контраст и окрашивание:
- 
clamp01,clamp01nan,scaleminmax,colorsigned,scalesigned 
Алгоритмы:
- 
Редукции:
maxfinite,maxabsfinite,minfinite,meanfinite,sad,ssd,integral_image,boxdiff,gaussian_pyramid - 
Изменение размера и пространственные преобразования:
restrict,imresize,warp - 
Фильтрация:
imfilter,imfilter!,imfilter_LoG,mapwindow,imROF,padarray - 
Ядра фильтрации:
Kernel.илиKernelFactors., а затемando[345],guassian2d,imaverage,imdog,imlaplacian,prewitt,sobel - 
Экспозиция:
build_histogram,adjust_histogram,imadjustintensity,imstretch,imcomplement,AdaptiveEqualization,GammaCorrection,cliphist - 
Градиенты:
backdiffx,backdiffy,forwarddiffx,forwarddiffy,imgradients - 
Обнаружение краев:
imedge,imgradients,thin_edges,magnitude,phase,magnitudephase,orientation,canny - 
Обнаружение углов:
imcorner,harris,shi_tomasi,kitchen_rosenfeld,meancovs,gammacovs,fastcorners - 
Обнаружение пятен:
blob_LoG,findlocalmaxima,findlocalminima - 
Морфологические операции:
dilate,erode,closing,opening,tophat,bothat,morphogradient,morpholaplace,feature_transform,distance_transform - 
Связанные компоненты:
label_components,component_boxes,component_lengths,component_indices,component_subscripts,component_centroids - 
Интерполяция:
bilinear_interpolation 
Тестовые изображения и фантомы (см. также TestImages.jl):
- 
shepp_logan