Access each element in the broadcast operation

Is there anyway I can acess the current element I’m iterating through when I use a broadcast operation? I would want to make this code in just one line, so, for every pixel I’m iterating in my image I would want to perform the operation the function is doing, but without having to call it. Like the for loop i did.

function bright(pixel)

pixel = RGB(clamp(pixel.r * 2, 0, 1), clamp(pixel.g * 2, 0, 1), clamp(pixel.b * 2, 0, 1))

return pixel
end

bright.(img)

begin
h = size(img, 1)
w = size(img, 2)

for i in 1:h
for j in 1:w
img1[i, j] = RGB(clamp(img1[i,j].r * 2, 0, 1), clamp(img1[i,j].g * 2, 0, 1), clamp(img1[i,j].b * 2, 0, 1))
end
end

img1
end

1 Like

Yes for example pixel.r is just a simpler way of writing getproperty(pixel, :r) (that’s what we call syntax sugar). So to broadcast this access to the r field you can write getproperty.(pixel, :r).

(PS: see Please read: make it easier to help you for tips on how to format your code on this forum.)

1 Like

I don’t get how that applies to my code, sorry. What I would like to is acess the pixel [i,j] over the broadcasting, something like:

img .= RGB(clamp(img1[i,j].r * 2, 0, 1), clamp(img1[i,j].g * 2, 0, 1), clamp(img1[i,j].b * 2, 0, 1))

Replace this with the getproperty call.

But to be honest, the option with the function definition and the bright.(img) call is so much cleaner…

Perhaps this will help.

The r, g, and b fields have accessor functions called red, green, and blue.

julia> using Colors

julia> img = rand(RGB, 2, 2)
2×2 Array{RGB{Float64},2} with eltype RGB{Float64}:
 RGB{Float64}(0.873815,0.178169,0.869838)  …  RGB{Float64}(0.198527,0.556571,0.661415)
 RGB{Float64}(0.380691,0.850955,0.472295)     RGB{Float64}(0.844428,0.151697,0.343053)

julia> getproperty.(img, :r)
2×2 Matrix{Float64}:
 0.873815  0.198527
 0.380691  0.844428

julia> getproperty.(img, :g)
2×2 Matrix{Float64}:
 0.178169  0.556571
 0.850955  0.151697

julia> getproperty.(img, :b)
2×2 Matrix{Float64}:
 0.869838  0.661415
 0.472295  0.343053

julia> red.(img)
2×2 Matrix{Float64}:
 0.873815  0.198527
 0.380691  0.844428

julia> green.(img)
2×2 Matrix{Float64}:
 0.178169  0.556571
 0.850955  0.151697

julia> blue.(img)
2×2 Matrix{Float64}:
 0.869838  0.661415
 0.472295  0.343053

julia> x->clamp(x*2,0,1)
#16 (generic function with 1 method)

julia> c = x->clamp(x*2,0,1)
#18 (generic function with 1 method)

julia> c.(red.(img)), c.(green.(img)), c.(blue.(img))
([1.0 0.3970532281590171; 0.7613825068122753 1.0], [0.35633721857052847 1.0; 1.0 0.3033949814585586], [1.0 1.0; 0.9445902569521101 0.6861064554657248])

julia> RGB.(c.(red.(img)), c.(green.(img)), c.(blue.(img)))
2×2 Array{RGB{Float64},2} with eltype RGB{Float64}:
 RGB{Float64}(1.0,0.356337,1.0)      …  RGB{Float64}(0.397053,1.0,1.0)
 RGB{Float64}(0.761383,1.0,0.94459)     RGB{Float64}(1.0,0.303395,0.686106)

Another solution is to use channelview from Images.jl or more precisely ImageCore.jl. This turns your image or 2D RGB array into a 3D array of numbers wherw dimension has a size of 3, one each for r, g, and b.

You can then clamp the entire array since you are treating all the sizes the same.

To get back a RGB image, we can then use reinterpret.

julia> using ImageCore

julia> channelview(img)
3×2×2 reinterpret(reshape, Float64, ::Array{RGB{Float64},2}) with eltype Float64:                                                           [:, :, 1] =
 0.873815  0.380691
 0.178169  0.850955
 0.869838  0.472295

[:, :, 2] =
 0.198527  0.844428
 0.556571  0.151697
 0.661415  0.343053

julia> c.(channelview(img))
3×2×2 Array{Float64, 3}:
[:, :, 1] =
 1.0       0.761383
 0.356337  1.0
 1.0       0.94459

[:, :, 2] =
 0.397053  1.0
 1.0       0.303395
 1.0       0.686106

julia> reinterpret(RGB{Float64}, c.(channelview(img)))[1,:,:]
2×2 Array{RGB{Float64},2} with eltype RGB{Float64}:
 RGB{Float64}(1.0,0.356337,1.0)      …  RGB{Float64}(0.397053,1.0,1.0)
 RGB{Float64}(0.761383,1.0,0.94459)     RGB{Float64}(1.0,0.303395,0.686106)

I recalling there might be a shortcut for reinterpret here, but it is not coming to mind immediately.

1 Like

Wrapping symbols in a tuple seems redundant. They are scalars, I believe.

2 Likes

How about

map(img) do pixel RGB(clamp(pixel.r * 2, 0, 1), clamp(pixel.g * 2, 0, 1), clamp(pixel.b * 2, 0, 1)) end

but it would be more common to split this into 3 lines

map(img) do pixel
   RGB(clamp(pixel.r * 2, 0, 1), clamp(pixel.g * 2, 0, 1), clamp(pixel.b * 2, 0, 1))
end

An alternative is
https://docs.julialang.org/en/v1/manual/arrays/#man-comprehensions

1 Like