Related
I am trying to implement convolution by hand in Julia. I'm not too familiar with image processing or Julia, so maybe I'm biting more than I can chew.
Anyway, when I apply this method with a 3*3 edge filter edge = [0 -1 0; -1 4 -1; 0 -1 0] as convolve(img, edge), I am getting an error saying that my values are exceeding the allowed values for the RGBA type.
Code
function convolve(img::Matrix{<:Any}, kernel)
(half_kernel_w, half_kernel_h) = size(kernel) .÷ 2
(width, height) = size(img)
cpy_im = copy(img)
for row ∈ 1+half_kernel_h:height-half_kernel_h
for col ∈ 1+half_kernel_w:width-half_kernel_w
from_row, to_row = row .+ (-half_kernel_h, half_kernel_h)
from_col, to_col = col .+ (-half_kernel_h, half_kernel_h)
cpy_im[row, col] = sum((kernel .* RGB.(img[from_row:to_row, from_col:to_col])))
end
end
cpy_im
end
Error (original)
ArgumentError: element type FixedPointNumbers.N0f8 is an 8-bit type representing 256 values from 0.0 to 1.0, but the values (-0.0039215684f0, -0.007843137f0, -0.007843137f0, 1.0f0) do not lie within this range.
See the READMEs for FixedPointNumbers and ColorTypes for more information.
I am able to identify a simple case where such error may occur (a white pixel surrounded by all black pixels or vice-versa). I tried "fixing" this by attempting to follow the advice here from another stackoverflow question, but I get more errors to the effect of Math on colors is deliberately undefined in ColorTypes, but see the ColorVectorSpace package..
Code attempting to apply solution from the other SO question
function convolve(img::Matrix{<:Any}, kernel)
(half_kernel_w, half_kernel_h) = size(kernel) .÷ 2
(width, height) = size(img)
cpy_im = copy(img)
for row ∈ 1+half_kernel_h:height-half_kernel_h
for col ∈ 1+half_kernel_w:width-half_kernel_w
from_row, to_row = row .+ [-half_kernel_h, half_kernel_h]
from_col, to_col = col .+ [-half_kernel_h, half_kernel_h]
cpy_im[row, col] = sum((kernel .* RGB.(img[from_row:to_row, from_col:to_col] ./ 2 .+ 128)))
end
end
cpy_im
end
Corresponding error
MethodError: no method matching +(::ColorTypes.RGBA{Float32}, ::Int64)
Math on colors is deliberately undefined in ColorTypes, but see the ColorVectorSpace package.
Closest candidates are:
+(::Any, ::Any, !Matched::Any, !Matched::Any...) at operators.jl:591
+(!Matched::T, ::T) where T<:Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8} at int.jl:87
+(!Matched::ChainRulesCore.AbstractThunk, ::Any) at ~/.julia/packages/ChainRulesCore/a4mIA/src/tangent_arithmetic.jl:122
Now, I can try using convert etc., but when I look at the big picture, I start to wonder what the idiomatic way of solving this problem in Julia is. And that is my question. If you had to implement convolution by hand from scratch, what would be a good way to do so?
EDIT:
Here is an implementation that works, though it may not be idiomatic
function convolve(img::Matrix{<:Any}, kernel)
(half_kernel_h, half_kernel_w) = size(kernel) .÷ 2
(height, width) = size(img)
cpy_im = copy(img)
# println(Dict("width" => width, "height" => height, "half_kernel_w" => half_kernel_w, "half_kernel_h" => half_kernel_h, "row range" => 1+half_kernel_h:(height-half_kernel_h), "col range" => 1+half_kernel_w:(width-half_kernel_w)))
for row ∈ 1+half_kernel_h:(height-half_kernel_h)
for col ∈ 1+half_kernel_w:(width-half_kernel_w)
from_row, to_row = row .+ (-half_kernel_h, half_kernel_h)
from_col, to_col = col .+ (-half_kernel_w, half_kernel_w)
vals = Dict()
for method ∈ [red, green, blue, alpha]
x = sum((kernel .* method.(img[from_row:to_row, from_col:to_col])))
if x > 1
x = 1
elseif x < 0
x = 0
end
vals[method] = x
end
cpy_im[row, col] = RGBA(vals[red], vals[green], vals[blue], vals[alpha])
end
end
cpy_im
end
First of all, the error
Math on colors is deliberately undefined in ColorTypes, but see the ColorVectorSpace package.
should direct you to read the docs of the ColorVectorSpace package, where you will learn that using ColorVectorSpace will now enable math on RGB types. (The absence of default support it deliberate, because the way the image-processing community treats RGB is colorimetrically wrong. But everyone has agreed not to care, hence the ColorVectorSpace package.)
Second,
ArgumentError: element type FixedPointNumbers.N0f8 is an 8-bit type representing 256 values from 0.0 to 1.0, but the values (-0.0039215684f0, -0.007843137f0, -0.007843137f0, 1.0f0) do not lie within this range.
indicates that you're trying to write negative entries with an element type, N0f8, that can't support such values. Instead of cpy_im = copy(img), consider something like cpy_im = [float(c) for c in img] which will guarantee a floating-point representation that can support negative values.
Third, I would recommend avoiding steps like RGB.(img...) when nothing about your function otherwise addresses whether images are numeric, grayscale, or color. Fundamentally the only operations you need are scalar multiplication and addition, and it's better to write your algorithm generically leveraging only those two properties.
Tim Holy's answer above is correct - keep things simple and avoid relying on third-party packages when you don't need to.
I might point out that another option you may not have considered is to use a different algorithm. What you are implementing is the naive method, whereas many convolution routines using different algorithms for different sizes, such as im2col and Winograd (you can look these two up, I have a website that covers the idea behind both here).
The im2col routine might be worth doing as essentially you can break the routine in several pieces:
Unroll all 'regions' of the image to do a dot-product with the filter/kernel on, and stack them together into a single matrix.
Do a matrix-multiply with the unrolled input and filter/kernel.
Roll the output back into the correct shape.
It might be more complicated overall, but each part is simpler, so you may find this easier to do. A matrix multiply routine is definitely quite easy to implement. For 1x1 (single-pixel) convolutions where the image and filter have the same ordering (i.e. NCHW images and FCHW filter) the first and last steps are trivial as essentially no rolling/unrolling is necessary.
A final word of advice - start simpler and add in the code to handle edge-cases, convolutions are definitely fiddly to work with.
Hope this helps!
I'd like to plot the following piecewise function with Fourier series in wxMaxima:
for given values of constants.
Here's my current input in wxMaxima:
a_1(t):=A_0+sum(A_n*cos(n*ω*(t-t_0))+B_n*sin(n*ω*(t-t_0)), n, 1, N);
a_2(t):=A_0;
a(t):=if(is(t>=t_0)) then a_1(t) else a_2(t);
N=2$
ω=31.416$
t_0=-0.1614$
A_0=0$
A_1=0.227$
B_1=0$
A_2=0.413$
B_2=0$
plot2d([a(t)], [t,0,0.5])$
Unfortunately, it doesn't work. I get the expression evaluates to non-numeric value everywhere in plotting range error. What can I do to make it work? Is it possible to plot this function in wxMaxima?
UPDATE: It works with modifications suggested by Robert Dodier:
a_1(t):=A[0]+sum(A[n]*cos(n*ω*(t-t_0))+B[n]*sin(n*ω*(t-t_0)), n, 1, N);
a_2(t):=A[0];
a(t):=if t>=t_0 then a_1(t) else a_2(t);
N:2$
ω:31.416$
t_0:-0.1614$
A[0]:0$
A[1]:0.227$
B[1]:0$
A[2]:0.413$
B[2]:0$
wxplot2d([a(t)], [t,0,0.5], [ylabel,"a"])$
I have long symbolic function in Maxima, let say
fn(x,y):=<<some long equation using x and y>>
I would like to calculate polynomial approximation of this function, let say
fn_poly(x,y)
within known range of x and y and with maximum error e
I know, that there is a funcionality in Maxima, e.g. plsquares, but it needs a matrix on input and I have only function fn(x,y). I don't know how to generate this matrix from my function. genmatrix creates matrix not usable by plsquares.
Is this possible in Maxima?
Make list of lists and transform it to matrix.
load(plsquares);
f(x,y):=x^2+y^3;
mat:makelist(makelist([X,Y,f(X,Y)],X,1,10,2),Y,1,10,2);
-> [[[1,1,2],[3,1,10],[5,1,26],[7,1,50],[9,1,82]],[[1,3,28],[3,3,36],[5,3,52],[7,3,76],[9,3,108]],[[1,5,126],[3,5,134],[5,5,150],[7,5,174],[9,5,206]],[[1,7,344],[3,7,352],[5,7,368],[7,7,392],[9,7,424]],[[1,9,730],[3,9,738],[5,9,754],[7,9,778],[9,9,810]]]
mat2:[];
for i:1 thru length(mat) do mat2:append(mat2,mat[i]);
mat3:funmake('matrix,mat2);
-> matrix([1,1,2],[3,1,10],[5,1,26],[7,1,50],[9,1,82],[1,3,28],[3,3,36],[5,3,52],[7,3,76],[9,3,108],[1,5,126],[3,5,134],[5,5,150],[7,5,174],[9,5,206],[1,7,344],[3,7,352],[5,7,368],[7,7,392],[9,7,424],[1,9,730],[3,9,738],[5,9,754],[7,9,778],[9,9,810])
ZZ:rhs(plsquares(mat3,[X,Y,Z],Z,3,3));
-> Determination Coefficient for Z = 1.0
-> Y^3+X^2
Given I have a linear model as the following I would like to get the gradient vector with regards to W and b.
# tf Graph Input
X = tf.placeholder("float")
Y = tf.placeholder("float")
# Set model weights
W = tf.Variable(rng.randn(), name="weight")
b = tf.Variable(rng.randn(), name="bias")
# Construct a linear model
pred = tf.add(tf.mul(X, W), b)
# Mean squared error
cost = tf.reduce_sum(tf.pow(pred-Y, 2))/(2*n_samples)
However if I try something like this where cost is a function of cost(x,y,w,b) and I only want to gradients with respect to w and b:
grads = tf.gradients(cost, tf.all_variable())
My placeholders will also be included (X and Y).
Even if I do get a gradient with [x,y,w,b] how do I know which element in the gradient that belong to each parameter since it is just a list without names to which parameter the derivative has be taken with regards to?
In this question I'm using parts of this code and I build on this question.
Quoting the docs for tf.gradients
Constructs symbolic partial derivatives of sum of ys w.r.t. x in xs.
So, this should work:
dc_dw, dc_db = tf.gradients(cost, [W, b])
Here, tf.gradients() returns the gradient of cost wrt each tensor in the second argument as a list in the same order.
Read tf.gradients for more information.
What am I doing wrong in this code?
atvalue(y(x),[x=0],1)$
desolve(diff(y(x),x)=y(x),y(x));
plot2d(y(x),[x,-6,6]);
Output:
plot2d: expression evaluates to non-numeric value everywhere in plotting range.
plot2d: nothing to plot
false
I want to plot y(x) which is obtained from a differential equation.
In Maxima y(x) = ... is an equation, and y(x) := ... is a function, and those two things are different. Try this:
atvalue (y(x), [x=0], 1)$
desolve (diff(y(x),x)=y(x), y(x));
define (y(x), rhs(%));
plot2d (y(x), [x, -6, 6]);
Here define(y(x), ...) is a different way to define a function. define evaluates the function body rhs(%) to yield exp(x) but := quotes it (not what you want).
The reason is that the result you see after the desolve does not mean y is defined as a function of x; in fact you obtain the same error if you change y(x) with f(x) (or any other unknown function) in plot2d. See the difference:
(%i9) atvalue(y(x),[x=0],1)$
(%i10) desolve(diff(y(x),x)=y(x),y(x));
x
(%o10) y(x) = %e
(%i11) y(x);
(%o11) y(x)
(%i12) y(x):=%e^x;
x
(%o12) y(x) := %e
(%i13) y(x);
x
(%o13) %e
I don't know if there's a way to “transform” the equation (the result) into a function definition automatically. If I find a way, I will complete the answer.