Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a filled rectangle within an array in Julia

I am new to Julia, and I am trying to understand basic data visualization. I am creating a 2D array of noise with:

xRange, yRange = 1:1:300, 1:1:300
maxVal = 0.3
noiseArr = rand(length(xRange),length(yRange))*maxVal

The resulting array is shown below (left). I would like to identify specific pixels -- defined by a rectangle with length, width, and rotation -- and set those values to a known number. Ultimately, I would like something like the image shown below (right).

I've no package preferences, but I've been looking at Images, OpenCV, etc. I'm hoping there's a straight-forward way to do this.

enter image description here

like image 792
AaronJPung Avatar asked Sep 24 '20 21:09

AaronJPung


People also ask

How do you fill an array in Julia?

Create an array filled with the value x . For example, fill(1.0, (10,10)) returns a 10x10 array of floats, with each element initialized to 1.0 . If x is an object reference, all elements will refer to the same object. fill(Foo(), dims) will return an array filled with the result of evaluating Foo() once.

How do you create a matrix in Julia?

Julia provides a very simple notation to create matrices. A matrix can be created using the following notation: A = [1 2 3; 4 5 6]. Spaces separate entries in a row and semicolons separate rows. We can also get the size of a matrix using size(A).

Is Julia an array language?

Hello Julia: Learn the New Julia Programming Language In Julia, arrays are actually mutable type collections which are used for lists, vectors, tables, and matrices. That is why the values of arrays in Julia can be modified with the use of certain pre-defined keywords. With the help of push!


2 Answers

Here is a quick example of drawing it using primitives with Luxor.jl

using Luxor

function b()
    Drawing(300, 300, "hello-world.png")
    background("black")
    sethue("white")
    #Luxor.scale(1,1)
    Luxor.translate(150, 30)
    Luxor.rotate(10 * pi / 180)
    w = 40
    h = 200
    rect(O, w, h, :fill)
    finish()
    preview()
end

rotated_rectangle_on_black

Now, instead of drawing it directly with Luxor you can extract the transformation matrix and use it somewhere else.

using Plots
using GR
using LinearAlgebra

gr(size = (300, 300), legend = false)

function a(transform=Matrix{Int}(I, 3, 3))
    side = 300
    width = side
    height = side
    xs = [string("x", i) for i = 1:width]
    ys = [string("y", i) for i = 1:height]
    z = float((1:height) * reshape(1:width, 1, :))
    # Plots.heatmap(xs, ys, z, aspect_ratio = 1)

    white = maximum(z)

    # Draw a rectangle with a rotation matrix applied
    for x in 0:40
        for y in 0:200
            t = transform*[x;y;1]
            z[round(Int, t[2]), round(Int, t[1])] = white
        end
    end

    Plots.heatmap(xs, ys, z, aspect_ratio = 1)
end

using Luxor

function b()
    Drawing(300, 300, "hello-world.png")
    background("black")
    sethue("white")
    #Luxor.scale(1,1)
    Luxor.translate(100, 60)
    Luxor.rotate(-10 * pi / 180)
    w = 40
    h = 200
    rect(O, w, h, :fill)
    finish()
    preview()

    tranformation_matrix = Luxor.cairotojuliamatrix(Luxor.getmatrix())

    a(tranformation_matrix)
end

Note this leaves stray pixels, because my for loop isn't efficient at rasterizing the fill. Accessing pixel data out of Luxor may be better, or using some other function that applies affine transformations to a matrix.

rotated_rectangle_on_heatmap

Caution about plots in julia:

Time to first plot is slow. And to do any real iterations on it, you should leverage Revise.jl and save your test functions in a file and include it with includet("test.jl"). Then your julia session is persistent and you only have to wait for your using statements once.

like image 137
phyatt Avatar answered Oct 22 '22 15:10

phyatt


I would like to identify specific pixels -- defined by a rectangle with length, width, and rotation

Knowing length, width and rotation is not enough to uniquely position a rectangle on a plane. You also need translation.

Here is a simple code that gives you an example what you can do (it is not super efficient, but is fast enough for demo purposes):

function rectangle!(x1, x2, x3, noiseArr, val)
    A = [x2-x1 x3-x1]
    b = x1
    iA = inv(A)

    for i in axes(noiseArr, 1), j in axes(noiseArr, 2)
        it, jt = iA * ([i, j]-b)
        0 <= it <= 1 && 0 <= jt <= 1 && (noiseArr[i, j] = val)
    end
end

x1 = [40, 140]
x2 = [230, 100]
x3 = [50, 170]
rectangle!(x1, x2, x3, noiseArr, 0.0)

In it - instead of passing a rotation, scaling and translation I thought it is easier to assume you pass three vertices of the rectangle x1, x2, and x3 (assuming that its sides are formed by x1-x2 and x1-x3 pairs) and it calculates the affine transformation you need. val is the value the pixels inside of the rectangle should get.

Note that essentially what we do is computing a reverse affine transformation of a rectangle back to a unit square and check which points lie within a unit square (which is easy).

(as commenters noted above - this is not an optimal way to do it as you do a lot of computations that are not needed, but I hope that it has the benefit of being easier to understand; also the benefit of Julia is that writing of such a loop is not a problem and it still runs fast enough)

like image 27
Bogumił Kamiński Avatar answered Oct 22 '22 14:10

Bogumił Kamiński