Reputation: 131
I want to do logical masking in Julia.
If 2nd and 3rd elements of an image equal, they should be changed to 0 and others be not changed.
I tried this;
mask = img[:, :, 2] .== img[:, :, 3]
img[:, :, 2][mask] .= 0
The image size is (1200, 1920, 3), but size of img[:, :, 2] is (1676348,).
I guess 'true' values on mask remain, but 'false' values are removed.
I want to deal the logical-masked-image as an image, therefore it should be (1200, 1920, 1).
How to keep the size with logical masking?
Upvotes: 3
Views: 729
Reputation: 69949
Edit:
The fast way to do it is:
@views @. img[:, :, 2] *= img[:, :, 2] != img[:, :, 3]
and here is a benchmark:
julia> img = rand(1:3, 10000, 10000, 3);
julia> @time @views @. img[:, :, 2] *= img[:, :, 2] != img[:, :, 3];
0.111409 seconds (28 allocations: 1.219 KiB)
There are alternative ways to do what you want, but if we want to follow your implementation then do:
julia> img = rand(1:3, 4,4,3)
4×4×3 Array{Int64,3}:
[:, :, 1] =
1 3 2 3
1 1 1 3
3 2 1 3
2 2 3 1
[:, :, 2] =
1 2 3 2
2 2 1 1
3 3 3 2
1 3 2 1
[:, :, 3] =
3 2 2 2
3 2 2 1
3 2 3 3
2 2 1 2
julia> mask = img[:, :, 2] .== img[:, :, 3]
4×4 BitArray{2}:
0 1 0 1
0 1 0 1
1 0 1 0
0 0 0 0
julia> view(img,:, :, 2)[mask] .= 0;
julia> img
4×4×3 Array{Int64,3}:
[:, :, 1] =
1 3 2 3
1 1 1 3
3 2 1 3
2 2 3 1
[:, :, 2] =
1 0 3 0
2 0 1 0
0 3 0 2
1 3 2 1
[:, :, 3] =
3 2 2 2
3 2 2 1
3 2 3 3
2 2 1 2
(it is crucial to use view
to get what you want, as otherwise img[:, :, 2]
creates a copy)
Alternatively in Julia it is easy enough to write the same using the loop e.g.:
function applymask!(img)
for i in axes(img, 1), j in axes(img, 2)
img[i, j, 2] == img[i, j, 3] && (img[i, j, 2] = 0)
end
end
(this is not the fastest possible implementation as it uses branching and does bounds checking, but it should be good enough in most cases)
And now you can write:
julia> img = rand(1:3, 4,4,3)
4×4×3 Array{Int64,3}:
[:, :, 1] =
1 3 1 3
3 3 1 2
3 3 3 3
2 1 3 1
[:, :, 2] =
3 1 3 1
3 3 3 3
2 1 1 3
1 2 3 3
[:, :, 3] =
1 1 2 1
2 1 3 2
3 3 1 1
1 2 1 3
julia> applymask!(img)
julia> img
4×4×3 Array{Int64,3}:
[:, :, 1] =
1 3 1 3
3 3 1 2
3 3 3 3
2 1 3 1
[:, :, 2] =
3 0 3 0
3 3 0 3
2 1 0 3
0 0 3 0
[:, :, 3] =
1 1 2 1
2 1 3 2
3 3 1 1
1 2 1 3
Upvotes: 4
Reputation: 12179
The best way to write what you're asking for might be img[mask, 2] .= 0
, but this doesn't work on Julia up through 1.5. As a workaround, you can create a mutable slice with @view
:
julia> img = cat([1 2; 3 4], [1 5; 6 7]; dims=3)
2×2×2 Array{Int64, 3}:
[:, :, 1] =
1 2
3 4
[:, :, 2] =
1 5
6 7
julia> mask = img[:,:,1] .== img[:,:,2]
2×2 BitMatrix:
1 0
0 0
julia> v = @view img[:,:,2]
2×2 view(::Array{Int64, 3}, :, :, 2) with eltype Int64:
1 5
6 7
julia> v[mask] .= 0
1-element view(reshape(view(::Array{Int64, 3}, :, :, 2), 4), [1]) with eltype Int64:
0
julia> img
2×2×2 Array{Int64, 3}:
[:, :, 1] =
1 2
3 4
[:, :, 2] =
0 5
6 7
It's worth emphasizing that without the @view
, v = img[:,:,2]
would create a copy of that slice of img
, and consequently any changes you made to v
would not be reflected in img
. The view ensures that v
remains coupled to img
.
The image size is (1200, 1920, 3), but size of img[:, :, 2] is (1676348,).
For accuracy, I should point out that's not true. Using img
from above:
julia> size(img[:,:,2])
(2, 2)
Upvotes: 1