IndirectArray indexing

I have a PNG file that loads as an IndirectArray with an eltype of RGB{N0f8}:
mwe
I use the following code:

using FileIO
using ImageIO

function f()
    img = load("mwe.png")
    @show typeof(img)
    return img[1]
end

Calling f() gives the following result:

typeof(img) = IndirectArrays.IndirectArray{ColorTypes.RGB{FixedPointNumbers.N0f8}, 2, UInt8, Matrix{UInt8}, OffsetArrays.OffsetVector{ColorTypes.RGB{FixedPointNumbers.N0f8}, Vector{ColorTypes.RGB{FixedPointNumbers.N0f8}}}}
CanonicalIndexError: getindex not defined for OffsetArrays.OffsetVector{ColorTypes.RGB{FixedPointNumbers.N0f8}, Vector{ColorTypes.RGB{FixedPointNumbers.N0f8}}}
Stacktrace:
 [1] error_if_canonical_getindex(::IndexCartesian, A::OffsetArrays.OffsetVector{ColorTypes.RGB{FixedPointNumbers.N0f8}, Vector{ColorTypes.RGB{FixedPointNumbers.N0f8}}}, ::Int64)
   @ Base .\abstractarray.jl:1357
 [2] getindex
   @ .\abstractarray.jl:1341 [inlined]
 [3] _getindex
   @ .\abstractarray.jl:1388 [inlined]
 [4] getindex
   @ .\abstractarray.jl:1342 [inlined]
 [5] getindex(A::IndirectArrays.IndirectArray{ColorTypes.RGB{FixedPointNumbers.N0f8}, 2, UInt8, Matrix{UInt8}, OffsetArrays.OffsetVector{ColorTypes.RGB{FixedPointNumbers.N0f8}, Vector{ColorTypes.RGB{FixedPointNumbers.N0f8}}}}, i::Int64)
   @ IndirectArrays ...\.julia\packages\IndirectArrays\BUQO3\src\IndirectArrays.jl:64
 [6] f()
   @ Main .\REPL[2]:4
 [7] top-level scope
   @ REPL[3]:1

A second call of the function gives the result I expected on the first call:

typeof(img) = IndirectArrays.IndirectArray{ColorTypes.RGB{FixedPointNumbers.N0f8}, 2, UInt8, Matrix{UInt8}, OffsetArrays.OffsetVector{ColorTypes.RGB{FixedPointNumbers.N0f8}, Vector{ColorTypes.RGB{FixedPointNumbers.N0f8}}}}
RGB{N0f8}(1.0, 1.0, 1.0)

Is there something I should be doing before the first getindex operation to ensure that it works?

It’s surely a world-age error. Try using IndirectArrays at the top of your file.

Thanks for the quick reply. Unfortunately, I get the same result.

Odd, I don’t get the error or the IndirectArray (Windows, Julia v1.12.5, FileIO v1.18.0, ImageIO v0.6.9, IndirectArrays v1.0.0):

julia> f()
typeof(img) = Matrix{ColorTypes.RGB{FixedPointNumbers.N0f8}}
RGB{N0f8}(1.0, 1.0, 1.0)

I right-click-saved the PNG image, maybe something was lost there?

That’s a package’s job, it’s abnormal for a first call to define a needed method.

I think the discourse software (or the browser) converts the format. I have put a copy of the original file here.

That’s a package’s job, it’s abnormal for a first call to define a needed method.

The package defines getindex(A::IndirectArray, i::Int), in a way that looks perfectly reasonable, but the error is thrown once control gets to Base.

I’m seeing the same error and array types now. Seeing something else weird:

julia> load("mwe.png") |> typeof
IndirectArrays.IndirectArray{ColorTypes.RGB{FixedPointNumbers.N0f8}, 2, UInt8, Matrix{UInt8}, OffsetArrays.OffsetVector{ColorTypes.RGB{FixedPointNumbers.N0f8}, Vector{ColorTypes.RGB{FixedPointNumbers.N0f8}}}}

julia> load("mwe.png") |> IndexStyle
IndexLinear()

julia> load("mwe.png").values |> typeof
OffsetVector{RGB{N0f8}, Vector{RGB{N0f8}}} (alias for OffsetArrays.OffsetArray{ColorTypes.RGB{FixedPointNumbers.Normed{UInt8, 8}}, 1, Array{ColorTypes.RGB{FixedPointNumbers.Normed{UInt8, 8}}, 1}})

julia> load("mwe.png").values |> IndexStyle
IndexLinear()

So where did that IndexCartesian come from? The last getindex just calls IndexStyle like the above:

function getindex(A::AbstractArray, I...)
    @_propagate_inbounds_meta
    error_if_canonical_getindex(IndexStyle(A), A, I...) # L1341
    _getindex(IndexStyle(A), A, to_indices(A, I)...)
end

…and why does it suddenly work after the first call to getindex?

Still a world age error. But the missing packages turned out to be OffsetArrays and ColorVectorSpace.

The issue is that load loads packages on-demand, but this means new methods are getting defined while your compiled function f is running. Those are defined in a new world, but the compiled function is running in the old world. When you come back to the command-line prompt, the world age updates, so the next run works as expected.

Here’s how to diagnose what’s missing:

julia> using FileIO, ImageIO

julia> function f()
           img = load("mwe.png")
               @show typeof(img)
           return img[1]
       end
f (generic function with 1 method)

julia> mods = Base.loaded_modules_array();

julia> f()
typeof(img) = IndirectArrays.IndirectArray{ColorTypes.RGB{FixedPointNumbers.N0f8}, 2, UInt8, Matrix{UInt8}, OffsetArrays.OffsetVector{ColorTypes.RGB{FixedPointNumbers.N0f8}, Vector{ColorTypes.RGB{FixedPointNumbers.N0f8}}}}
ERROR: CanonicalIndexError: getindex not defined for OffsetArrays.OffsetVector{ColorTypes.RGB{FixedPointNumbers.N0f8}, Vector{ColorTypes.RGB{FixedPointNumbers.N0f8}}}
Stacktrace:
 [1] error_if_canonical_getindex(::IndexCartesian, A::OffsetArrays.OffsetVector{ColorTypes.RGB{FixedPointNumbers.N0f8}, Vector{ColorTypes.RGB{FixedPointNumbers.N0f8}}}, ::Int64)
   @ Base .\abstractarray.jl:1357
 [2] getindex
   @ .\abstractarray.jl:1341 [inlined]
 [3] _getindex
   @ .\abstractarray.jl:1388 [inlined]
 [4] getindex
   @ .\abstractarray.jl:1342 [inlined]
 [5] getindex(A::IndirectArrays.IndirectArray{ColorTypes.RGB{FixedPointNumbers.N0f8}, 2, UInt8, Matrix{UInt8}, OffsetArrays.OffsetVector{ColorTypes.RGB{FixedPointNumbers.N0f8}, Vector{ColorTypes.RGB{FixedPointNumbers.N0f8}}}}, i::Int64)
   @ IndirectArrays C:\Users\timho\.julia\packages\IndirectArrays\BUQO3\src\IndirectArrays.jl:64
 [6] f()
   @ Main .\REPL[4]:4
 [7] top-level scope
   @ REPL[6]:1

julia> mods2 = Base.loaded_modules_array();

julia> setdiff(mods2, mods)
20-element Vector{Module}:
 Reexport
 Statistics
 FixedPointNumbers
 ColorTypes
 Colors
 TensorCore
 ColorVectorSpace
 OffsetArrays
 PaddedViews
 MappedArrays
 StackViews
 MosaicViews
 Preferences
 PrecompileTools
 ImageCore
 JLLWrappers
 libpng_jll
 CEnum
 PNGFiles
 StyledStringsExt

But if you make sure that all the methods are defined before you run f for the first time (by pre-loading the required packages), then it works on first call.

load is convenient, but also a foot-gun for this reason.

4 Likes

Thanks again for looking at this (thanks also to @Benny). The minimum that will work in this case is

using FileIO, ImageIO, OffsetArrays

I will look into pre-load options. Is there an alternative to load? I would rather abandon convenience for reliability.

One option might be to add a noimports=true kwarg to load, and have it throw an error when it needs to load a package in order to satisfy the request. If you don’t want to implement that yourself, I would guess making that kind of change should be well within the capabilities of modern AI agents, so either way I encourage you to give it a try and submit a PR if it works.

2 Likes

Thanks for the feedback. I’ll take a look and see whether I can produce something that seems usable.