Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add basic functions for colorspace transforms. #3

Merged
merged 41 commits into from
Jun 22, 2021
Merged

Add basic functions for colorspace transforms. #3

merged 41 commits into from
Jun 22, 2021

Conversation

SomTambe
Copy link
Member

@SomTambe SomTambe commented Jun 7, 2021

This PR makes the base for any colorspace transform.

  • add colorify and channelify functions.

Usage:

julia> f = Chain(x->HSV.(x),channelify,flatten,Dense(768,16),Dense(16,10),x->σ.(x))

julia> f(rand(RGB,16,16,1)) # single image of dims 16 x 16

colorify can be used if one ever requires a colorspace transform between two layers.

julia> f = Chain(...,
                         x->colorify(RGB,x),
                         x->Gray.(x),
                         channelify,
                         ...
                         ) # this would allow for a Grayscale colorspace transform in between the network

I am not sure if ever this will come to use. It's better to provide an option rather than not providing anything at all.
I would love to hear your comments @DhairyaLGandhi @johnnychen94

Also, channelview is not differentiable. I will be writing an adjoint for that.

Copy link
Collaborator

@johnnychen94 johnnychen94 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good start.

One thing I notice about this PR is that permutedims creates a memory contiguous array while colorview doesn't. This implies that channeify creates a contiguous dense array, while colorify does not. This memory non-contiguity can be a source of performance regression.

Perhaps a 1-argument version of colorify would be useful, i.e., colorify(RGB) that creates x->colorify(RGB, x).

Should this function specially handle ndims(m) == 2? I guess no? If so it's probably worth documenting something like "the last dimension is faithfully recognized as a batch dimension so you need to unsqueeze the input manually if the input is only one sample"

src/colors/conversions.jl Outdated Show resolved Hide resolved
src/colors/conversions.jl Outdated Show resolved Hide resolved
@SomTambe
Copy link
Member Author

SomTambe commented Jun 7, 2021

Should this function specially handle ndims(m) == 2? I guess no? If so it's probably worth documenting something like "the last dimension is faithfully recognized as a batch dimension so you need to unsqueeze the input manually if the input is only one sample"

Yes you are right. I do require them to have the last dimension as a batch. I should document it as you say.

@SomTambe
Copy link
Member Author

SomTambe commented Jun 8, 2021

@johnnychen94 Should I merge this PR? I'll add the adjoints in another PR, will that be fine?

@johnnychen94
Copy link
Collaborator

Every non-trivial PR should have its associated test cases.

@SomTambe
Copy link
Member Author

SomTambe commented Jun 8, 2021

Every non-trivial PR should have its associated test cases.

I completely forgot about the tests. No worries I will write them.

@SomTambe
Copy link
Member Author

SomTambe commented Jun 8, 2021

@johnnychen94 I have written the tests. Could you review them please?

Copy link
Collaborator

@johnnychen94 johnnychen94 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel this unit test is unnecessarily complicated. You don't need to introduce all other layers as long as the basic properties of channelify/colorify get tested.

Why not just test

X = rand(RGB{N0f8}, 4, 4, 5)
Y = channelify(X)

@test Y[:, :, 1, 1] == channelview(X[:, :, 1])
@test size(Y) == (4, 4, 3, 5)
@test eltype(Y) == N0f8
# make sure it has contiguous memory layout
@test stride(Y, 1) == 1
# maybe there're other properties

and also test 2d, 3d and 4d cases. And test various colorspace, e.g., RGB, HSV, Gray, RGBA, Gray24.

@SomTambe
Copy link
Member Author

I have added the adjoints for the necessary functions. I'll be adding the tests by tonight too.
cc. @DhairyaLGandhi @johnnychen94

src/colors/conversions.jl Outdated Show resolved Hide resolved
src/colors/conversions.jl Show resolved Hide resolved
src/colors/conversions.jl Outdated Show resolved Hide resolved
src/colors/conversions.jl Outdated Show resolved Hide resolved
src/colors/conversions.jl Outdated Show resolved Hide resolved
src/colors/conversions.jl Outdated Show resolved Hide resolved
@testset "Testing colorify" begin
@test size(g_3(rand(20,20,7,3))) == (10,3)
@test size(g_1(rand(20,20,7,3))) == (10,3)
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also test for differentiability

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes sure

:DIN99,:ADIN99,:DIN99A,
:LMS,:ALMS,:LMSA,
:YIQ,:AYIQ,:YIQA)
@eval @adjoint function $f(x...)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This adjoint is for RGB(1, 1, 1), we would also need adjoint for c=Lab(100, 0, 0); RGB(c)

Suggested change
@eval @adjoint function $f(x...)
@eval @adjoint function $f(x::Real...)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why should it be for Real only?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My first look is that the pullback is not correct for the Colorant inputs.

The most important thing currently is to add unit tests. More than 70% of the efforts should be paid to unit tests to make sure things are written correctly.

With unit tests appropriately written, we can then revisit this one and see if it is correct for colorant inputs.

src/colors/conversions.jl Show resolved Hide resolved
end

# adjoint for colorview
@adjoint function colorview(T, x) where {T}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's also a multi inputs version: colorview(T, gray1, gray2...)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does colorview handle batches?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it does

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. Yes it does; channelview and colorview is just a wrapper of Base.ReinterpretedArray:

julia> X = rand(RGB, 12, 24, 64);

julia> channelview(X) |> size
(3, 12, 24, 64)

julia> colorview(eltype(X), channelview(X)) == X
true

e = eltype(x)
y = channelview(x)
function pullback(Δ)
return (colorview(e,Δ),)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does collect(colorview(e, Δ)) solve the issue in FluxML/Zygote.jl#993?

src/colors/conversions.jl Outdated Show resolved Hide resolved
src/colors/conversions.jl Outdated Show resolved Hide resolved
Copy link
Collaborator

@johnnychen94 johnnychen94 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A good start on improving coverage. Nicely written 👍

some numerical tests are still required, e.g., 1) differentiability 2) reversibility (that channelify(colorify(RGB, data)) == data and colorify(RGB, channelify(rgb_data)) == rgb_data

tests/colors/conversions.jl Outdated Show resolved Hide resolved
@SomTambe
Copy link
Member Author

SomTambe commented Jun 17, 2021

If the adjoint of (CT<:Colorant)(x::T) where T is still an issue. The relevant codes should be removed for this PR and added back in future PR.

I have commented out the tests which would evaluate gradients for these. This would be added in the next PR.

  • Added gradients for colorview and channelview.
  • Completed CI integration.

I am still debugging CI integration. Will be done soon.
I feel the colorview derivative tests are a bit messy.

@SomTambe SomTambe mentioned this pull request Jun 17, 2021
3 tasks
@SomTambe
Copy link
Member Author

@johnnychen94 I have done all the required tasks we needed. I have also created #5 to track all our progress remaining over here.

Copy link
Collaborator

@johnnychen94 johnnychen94 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last round of comments with only style suggestions.

@DhairyaLGandhi any more comments?

.github/workflows/ci.yml Outdated Show resolved Hide resolved
src/colors/conversions.jl Outdated Show resolved Hide resolved
src/colors/conversions.jl Outdated Show resolved Hide resolved
test/colors/conversions.jl Outdated Show resolved Hide resolved
test/colors/conversions.jl Outdated Show resolved Hide resolved
test/colors/conversions.jl Outdated Show resolved Hide resolved
@SomTambe
Copy link
Member Author

Committed all the changes you asked for.

@@ -42,8 +42,15 @@ jobs:
- run: |
julia --project=docs -e '
using Pkg
Pkg.add("Documenter")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one can also be added into docs/Project.toml

Comment on lines +48 to +53
- run: |
julia --project=docs -e '
using Pkg
using Documenter: doctest
using DiffImages
doctest(DiffImages)'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry that I didn't make it clear. I was thinking of adding doctest(DiffImages) into test/runtests.jl. See OffsetArrays as an example.

Copy link
Member

@DhairyaLGandhi DhairyaLGandhi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Largely LGTM , added a couple thoughts on the tests and let's get this merged!

src/colors/conversions.jl Show resolved Hide resolved
src/colors/conversions.jl Show resolved Hide resolved
test/colors/conversions.jl Show resolved Hide resolved
@SomTambe
Copy link
Member Author

I am merging the PR as per conversation with @DhairyaLGandhi
cc @johnnychen94

@SomTambe SomTambe merged commit f9694c5 into main Jun 22, 2021
(25, 25, 7)
```
"""
function colorify(color::Type{CT}, m::AbstractArray) where CT <: Colorant
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like we may want to restrict this function to AbstractArray{T,4} since the case of (28,28,3) with Grayscale should be a simple reinterpret

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current function gives the freedom of any number of dims >= 2. We could restrict it to 4 but it could be we may require something for higher dimensions in the future.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My main concern is the case I highlighted earlier. If that is handled correctly, then I'm fine

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand that classic deep learning mainly deals with 4D tensor but I'm not very positive about this type constraint; especially when it's not due to implementation restriction.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am the first person to not restrict types for sure, just wanted to ensure we're doing the correct thing for different inputs we can expect to get.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants