I'd like to occlude the pixels of an image in specific locations. Let's say from row 300:422 and columns 522:741. And let's say my code is this:
using Images
add = "http://wonderfulengineering.com/wp-content/uploads/2014/04/ship-image-3.jpeg"
img = load(download(add))
occlude = RMat = rand(RGB,100,250);
rowi = size(RMat,1)
coli = size(RMat,2)
for i in 1:rowi, j in 1:coli
img[i,j] = RMat[i, j]
end
However, this changes the position in image that are correspond to pixel 1:size_of_Rmat. Is there an automatic way to randomly select a regions and put a RMat on top of img
You can broadcast-assign to the region where you want to position the new image.
julia> function occluderegion!(img, rowrange, colrange)
occlude = rand(RGB, length(rowrange), length(colrange))
img[rowrange, colrange] .= occlude
end
occluderegion! (generic function with 1 method)
julia> occluderegion!(img, 300:422, 522:741);
Related
I use makie.jl with slicesNumb for visualization of PET/CT scans, I have 3d array of attenuation values and I display heatmap with changing slices using slider - this works well I have two problems
I do not know how to be able to define custom colormaps (basically I need to be able to specify that all above some threshold value will be black and all below white and values between will have grey values proportional to attenuation value).
2)I would like to be able to display to display over my image (tachnically heatmap) another ones where I would be able to controll transparency - alpha value of pixels - in order to display some annotations/ PET ...
code that works but without those 2 functionalities and how it looks
using GLMakie
```#doc
simple display of single image - only in transverse plane
```
function singleCtScanDisplay(arr ::Array{Number, 3})
fig = Figure()
sl_x = Slider(fig[2, 1], range = 1:1:size(arr)[3], startvalue = 40)
ax = Axis(fig[1, 1])
hm = heatmap!(ax, lift(idx-> arr[:,:, floor(idx)], sl_x.value) ,colormap = :grays)
Colorbar(fig[1, 2], hm)
fig
end
Thanks for help !
You can use Colors and ColorSchemeTools, but you will need to add the top and bottom of the scheme according to your thresholds.
using Colors, ColorSchemeTools
truemin = 0
truemax = 600
max_shown_black = 20
min_shown_white = 500
data = rand(truemin:truemax, (500, 500, 20))
grayscheme = [fill(colorant"black", max_shown_black - truemin + 1);
collect(make_colorscheme(identity, identity, identity,
length = min_shown_white - max_shown_black - 1));
fill(colorant"white", truemax - min_shown_white + 1)]
For controlling alpha, I would add a popup window with an alpha slider. Take a look at some of the distributable DICOM tools for examples.
I finally managed it basically I load 3 dimensional data stored in hdf5 (I loaded it into hdf5 from raw using python)
It enables viewing transverse slices and annotate 3d pathes in a mask that will be displayed over main image
exmpleH = #spawnat persistenceWorker Main.h5manag.getExample()
minimumm = -1000
maximumm = 2000
arrr= fetch(exmpleH)
imageDim = size(arrr)
using GLMakie
maskArr = Observable(BitArray(undef, imageDim))
MyImgeViewer.singleCtScanDisplay(arrr, maskArr,minimumm, maximumm)
Now definition of the required modules
```#doc
functions responsible for displaying medical image Data
```
using DrWatson
#quickactivate "Probabilistic medical segmentation"
module MyImgeViewer
using GLMakie
using Makie
#using GeometryBasics
using GeometricalPredicates
using ColorTypes
using Distributed
using GLMakie
using Main.imageViewerHelper
using Main.workerNumbers
## getting id of workers
```#doc
simple display of single image - only in transverse plane we are adding also a mask that
arrr - main 3 dimensional data representing medical image for example in case of CT each voxel represents value of X ray attenuation
minimumm, maximumm - approximately minimum and maximum values we can have in our image
```
function singleCtScanDisplay(arrr ::Array{Number, 3}, maskArr , minimumm, maximumm)
#we modify 2 pixels just in order to make the color range constant so slices will be displayed in the same windows
arrr[1,1,:].= minimumm
arrr[2,1,:].= maximumm
imageDim = size(arrr) # dimenstion of the primary image for example CT scan
slicesNumb =imageDim[3] # number of slices
#defining layout variables
scene, layout = GLMakie.layoutscene(resolution = (600, 400))
ax1 = layout[1, 1] = GLMakie.Axis(scene, backgroundcolor = :transparent)
ax2 = layout[1, 1] = GLMakie.Axis(scene, backgroundcolor = :transparent)
#control widgets
sl_x =layout[2, 1]= GLMakie.Slider(scene, range = 1:1: slicesNumb , startvalue = slicesNumb/2 )
sliderXVal = sl_x.value
#color maps
cmwhite = cgrad(range(RGBA(10,10,10,0.01), stop=RGBA(0,0,255,0.4), length=10000));
greyss = createMedicalImageColorSchemeB(200,-200,maximumm, minimumm )
####heatmaps
#main heatmap that holds for example Ct scan
currentSliceMain = GLMakie.#lift(arrr[:,:, convert(Int32,$sliderXVal)])
hm = GLMakie.heatmap!(ax1, currentSliceMain ,colormap = greyss)
#helper heatmap designed to respond to both changes in slider and changes in the bit matrix
currentSliceMask = GLMakie.#lift($maskArr[:,:, convert(Int32,$sliderXVal)])
hmB = GLMakie.heatmap!(ax1, currentSliceMask ,colormap = cmwhite)
#adding ability to be able to add information to mask where we clicked so in casse of mit matrix we will set the point where we clicked to 1
indicatorC(ax1,imageDim,scene,maskArr,sliderXVal)
#displaying
colorB = layout[1,2]= Colorbar(scene, hm)
GLMakie.translate!(hmB, Vec3f0(0,0,5))
scene
end
```#doc
inspired by https://github.com/JuliaPlots/Makie.jl/issues/810
Generaly thanks to this function the viewer is able to respond to clicking on the slices and records it in the supplied 3 dimensional AbstractArray
ax - Axis which store our heatmap slices which we want to observe wheather user clicked on them and where
dims - dimensions of main image for example CT
sc - Scene where our axis is
maskArr - the 3 dimensional bit array that has exactly the same dimensions as main Array storing image
sliceNumb - represents on what slide we are on currently on - ussually it just give information from slider
```
function indicatorC(ax::Axis,dims::Tuple{Int64, Int64, Int64},sc::Scene,maskArr,sliceNumb::Observable{Any})
register_interaction!(ax, :indicator) do event::GLMakie.MouseEvent, axis
if event.type === MouseEventTypes.leftclick
println("clicked")
##async begin
#appropriately modyfing wanted pixels in mask array
#async calculateMouseAndSetmaskWrap(maskArr, event,sc,dims,sliceNumb)
#
#
# println("fetched" + fetch(maskA))
# finalize(maskA)
#end
return true
#print("xMouse: $(xMouse) yMouse: $(yMouse) compBoxWidth: $(compBoxWidth) compBoxHeight: $(compBoxHeight) calculatedXpixel: $(calculatedXpixel) calculatedYpixel: $(calculatedYpixel) pixelsNumbInX $(pixelsNumbInX) ")
end
end
end
```#doc
wrapper for calculateMouseAndSetmask - from imageViewerHelper module
given mouse event modifies mask accordingly
maskArr - the 3 dimensional bit array that has exactly the same dimensions as main Array storing image
event - mouse event passed from Makie
sc - scene we are using in Makie
```
function calculateMouseAndSetmaskWrap(maskArr, event,sc,dims,sliceNumb)
maskArr[] = calculateMouseAndSetmask(maskArr, event,sc,dims,sliceNumb)
end
end #module
and helper methods
```#doc
functions responsible for helping in image viewer - those functions are meant to be invoked on separate process
- in parallel
```
using DrWatson
#quickactivate "Probabilistic medical segmentation"
module imageViewerHelper
using Documenter
using ColorTypes
using Colors, ColorSchemeTools
using Makie
export calculateMouseAndSetmask
export createMedicalImageColorSchemeB
# using AbstractPlotting
```#doc
given mouse event modifies mask accordingly
maskArr - the 3 dimensional bit array that has exactly the same dimensions as main Array storing image
event - mouse event passed from Makie
sc - scene we are using in Makie
```
function calculateMouseAndSetmask(maskArr, event,sc,dims,sliceNumb)
#position from top left corner
xMouse= Makie.to_world(sc,event.data)[1]
yMouse= Makie.to_world(sc,event.data)[2]
#data about height and width in layout
compBoxWidth = 510
compBoxHeight = 510
#image dimensions - number of pixels from medical image for example ct scan
pixelsNumbInX =dims[1]
pixelsNumbInY =dims[2]
#calculating over which image pixel we are
calculatedXpixel =convert(Int32, round( (xMouse/compBoxWidth)*pixelsNumbInX) )
calculatedYpixel = convert(Int32,round( (yMouse/compBoxHeight)*pixelsNumbInY ))
sliceNumbConv =convert(Int32,round( sliceNumb[] ))
#appropriately modyfing wanted pixels in mask array
return markMaskArrayPatch( maskArr ,CartesianIndex(calculatedXpixel, calculatedYpixel, sliceNumbConv ),2)
end
```#doc
maskArr - the 3 dimensional bit array that has exactly the same dimensions as main Array storing image
point - cartesian coordinates of point around which we want to modify the 3 dimensional array from 0 to 1
```
function markMaskArrayPatch(maskArr, pointCart::CartesianIndex{3}, patchSize ::Int64)
ones = CartesianIndex(patchSize,patchSize,patchSize) # cartesian 3 dimensional index used for calculations to get range of the cartesian indicis to analyze
maskArrB = maskArr[]
for J in (pointCart-ones):(pointCart+ones)
diff = J - pointCart # diffrence between dimensions relative to point of origin
if cartesianTolinear(diff) <= patchSize
maskArrB[J]=1
end
end
return maskArrB
end
```#doc
works only for 3d cartesian coordinates
cart - cartesian coordinates of point where we will add the dimensions ...
```
function cartesianTolinear(pointCart::CartesianIndex{3}) :: Int16
abs(pointCart[1])+ abs(pointCart[2])+abs(pointCart[3])
end
```#doc
creating grey scheme colors for proper display of medical image mainly CT scan
min_shown_white - max_shown_black range over which the gradint of greys will be shown
truemax - truemin the range of values in the image for which we are creating the scale
```
#taken from https://stackoverflow.com/questions/67727977/how-to-create-julia-color-scheme-for-displaying-ct-scan-makie-jl/67756158#67756158
function createMedicalImageColorSchemeB(min_shown_white,max_shown_black,truemax,truemin ) ::Vector{Any}
# println("max_shown_black - truemin + 1")
# println(max_shown_black - truemin + 1)
# println(" min_shown_white - max_shown_black - 1")
# println( min_shown_white - max_shown_black - 1)
# println("truemax - min_shown_white + 1")
# println(truemax - min_shown_white + 1)
return [fill(colorant"black", max_shown_black - truemin + 1);
collect(make_colorscheme(identity, identity, identity,
length = min_shown_white - max_shown_black - 1));
fill(colorant"white", truemax - min_shown_white + 1)]
end
end #module
I'm trying to draw car trips on a plane. I'm using Plotters library.
Here is some code example of trips' drawing procedure:
pub fn save_trips_as_a_pic<'a>(trips: &CarTrips, resolution: (u32, u32))
{
// Some initializing stuff
/// <...>
let root_area =
BitMapBackend::new("result.png", (resolution.0, resolution.1)).into_drawing_area();
root_area.fill(&WHITE).unwrap();
let root_area =
root_area.margin(10,10,10,10).titled("TITLE",
("sans-serif", 20).into_font()).unwrap();
let drawing_areas =
root_area.split_evenly((cells.1 as usize, cells.0 as usize));
for (index, trip) in trips.get_trips().iter().enumerate(){
let mut chart =
ChartBuilder::on(drawing_areas.get(index).unwrap())
.margin(5)
.set_all_label_area_size(50)
.build_ranged(50.0f32..54.0f32, 50.0f32..54.0f32).unwrap();
chart.configure_mesh().x_labels(20).y_labels(10)
.disable_mesh()
.x_label_formatter(&|v| format!("{:.1}", v))
.y_label_formatter(&|v| format!("{:.1}", v))
.draw().unwrap();
let coors = trip.get_points();
{
let draw_result =
chart.draw_series(series_from_coors(&coors, &BLACK)).unwrap();
draw_result.label(format!("TRIP {}",index + 1)).legend(
move |(x, y)|
PathElement::new(vec![(x, y), (x + 20, y)], &random_color));
}
{
// Here I put red dots to see exact nodes
chart.draw_series(points_series_from_trip(&coors, &RED));
}
chart.configure_series_labels().border_style(&BLACK).draw().unwrap();
}
}
What I got now on Rust Plotters:
So, after drawing it in the 'result.png' image file, I struggle to understand these "lines", because I don't see the map itself. I suppose, there is some way in this library to put a map "map.png" in the background of the plot. If I would use Python, this problem will be solved like this:
# here we got a map image;
img: Image.Image = Image.open("map-image.jpg")
img.putalpha(64)
imgplot = plt.imshow(img)
# let's pretend that we got our map size in pixels and coordinates
# just in right relation to each other.
scale = 1000
x_shift = 48.0
y_shift = 50.0
coor_a = Coordinate(49.1, 50.4)
coor_b = Coordinate(48.9, 51.0)
x_axis = [coor_a.x, coor_b.x]
x_axis = [(element-x_shift) * scale for element in x_axis]
y_axis = [coor_a.y, coor_b.y]
y_axis = [(element-y_shift) * scale for element in y_axis]
plt.plot(x_axis, y_axis, marker='o')
plt.show()
Desired result on Python
Well, that's easy on Python, but I got no idea, how to do similar thing on Rust.
I have a 3d game where I will create an rectangle which is working as screen and the game itself works with vectors to positions. so I will create an rectangle and have only these parameters aviable:
start position ->vector (x,y,z).
Angle(rotation) of object(x,y,z).
size of rectangle.
now also the object need to be roatet to the right side so they are using angels also (x,y,z).
example:
position:-381.968750 -28.653845 -12702.185547
angle: -0.000 90.000 90.000
What I will create is an little bit hard but as idea simple.
I choose 2 complete different positions and angles and will create from the first vector to the second an rectangle.
I can only create an rectangle with the start point and angle.
and I can set the size so (x,y)
So I will now insert 2 positions(vectors) with 2 different angles
The rectangle will have the middle value between the first and second angle so like (90 and 0) -> 45
And the rectangle will start at the start vector and will end with his own size so I don't have a chance to use the end vector directly.
Legendary on photo:
Green=>start and end positions(vectors).
red => the marked zone.
Blue => how I will have the rectangle.
aem_point = vgui.Create( "AEM.Main.Panel" )
if IsValid(aem_point) then
aem_point:SetSize( 2,2 ) -- <-the size that i can set
aem_point:SetPos( 0, 0 )
aem_ph = vgui.Create( "DHTML", aem_point )
aem_ph:SetSize( aem_point:GetSize() )
aem_ph:SetPos(aem_point:GetPos())
aem_ph:SetVisible( true )
aem_ph:SetHTML([[
<html>
<body style="margin:0px;padding:0px;font-size:20px;color:red;border-style: solid;border-color: #ff0000;background-color:rgba(255,0,0,0.1);">
</body>
</html>
]] )
end
hook.Add( "PostDrawOpaqueRenderables", "DrawSample3D2DFrame" .. math.random(), function()
if first and dat_finish then
vgui.Start3D2D( input_position, input_angle, 1 ) -- <-and position&vec
aem_point:Paint3D2D()
vgui.End3D2D()
end
end )
Oh so you want to create a 3d2d plane from 2 vector positions?
Vec1 = A
Vec2 = B
input_position = ( Vec1 + Vec2 ) / 2
A problem you will run into is you need 3 points to generate a plane, so while you can get the position of the screen to get the angle of it you will need another point.
If these screens of yours are staticly set, as in you put their position into the lua code manually and dont intend to have it move or anything, just manually putting in the angle is by far the most simple approch.
As you can see, both of these planes are on the same two points, but they have diffrent angles.
I wrote the demo in expression 2, this should make it clear as to how this works, if you have any other questions just ask.
A = entity(73):pos()
B = entity(83):pos()
if(first())
{
holoCreate(1)
holoCreate(2)
}
holoPos(1,(A+B)/2)
holoScaleUnits(1,vec( abs(B:y() - A:y()) , 1 , abs(sqrt( ( B:z() - A:z() ) ^ 2 + ( B:x() - A:x() ) ^ 2 ))))
holoAng(1,ang(0,90,45))
holoPos(2,(A+B)/2)
holoScaleUnits(2,vec( abs(sqrt( ( B:x() - A:x() ) ^ 2 + ( B:y() - A:y() ) ^ 2 )) , 1 , abs(B:z()-A:z())))
holoAng(2,ang(0,45,0))
Trying to mosaic an image with rmagick.
How would one "mosaic blur" an existing image making the picture that it represents mosaic'ed ?
Like:
This is how you do a mosaic using RMagick
#!/home/software/ruby-1.8.5/bin/ruby -w
require 'RMagick'
# Demonstrate the mosaic method
a = Magick::ImageList.new
letter = 'A'
26.times do
# 'M' is not the same size as the other letters.
if letter != 'M'
a.read("images/Button_"+letter+".gif")
end
letter.succ!
end
# Make a copy of "a" with all the images quarter-sized
b = Magick::ImageList.new
page = Magick::Rectangle.new(0,0,0,0)
a.scene = 0
5.times do |i|
5.times do |j|
b << a.scale(0.25)
page.x = j * b.columns
page.y = i * b.rows
b.page = page
(a.scene += 1) rescue a.scene = 0
end
end
# Make a 5x5 mosaic
mosaic = b.mosaic
mosaic.write("mosaic.gif")
# mosaic.display
exit
Given a canvas, let's say 10x10, and given 3 rectangles/squares.
Canvas = 10x10
Rectangle 1 = 2x2
Rectangle 2 = 3x3
Rectangle 3 = 2x4
I've created a recursive function that loops every position of every rectangle on the canvas, and it works fine. (I've included the function below incase anyone wants to see it but I don't think it's necessary).
We can see that rectangle 1 and 2 are non rotatable, IE, if you rotate either of them 90 degrees essentially they are the same shape. However rectangle 3 is rotatable.
How do I change/construct the loop/recurisve function so that it loops every position of every rectangle, along with every possible rotation?
The aim is to loop through every possible fitting of the shapes on the canvas.
Thanks for any help!
Sub recurse(ByVal startPoint As Integer)
Dim x As Integer
Dim y As Integer
Dim validSolution As Boolean = isSolutionValid()
Dim loopXTo As Integer
Dim loopYTo As Integer
Dim solutionRating As Integer
'If parent nodes create invalid solution, we can skip (375 iterations from 1,600 iterations saving)
If validSolution = True Then
If (startPoint = 0) Then
loopXTo = Math.Floor((canvasCols - squareObjects(startPoint).sqRows()) / 2) '576 iterations from 1,680 iterations
loopYTo = Math.Floor((canvasRows - squareObjects(startPoint).sqCols) / 2) '31,104 iterations from 90,720 iterations
Else
loopXTo = canvasCols - squareObjects(startPoint).sqRows
loopYTo = canvasRows - squareObjects(startPoint).sqCols
End If
'Loop all positions on canvas
For x = 0 To loopXTo
For y = 0 To loopYTo
'Set coords of square
squareObjects(startPoint).setSquareCords(x, y)
'Phyiscally place it in canvas
placeSquareOnCanvas(x, y, squareObjects(startPoint).sqRows, squareObjects(startPoint).sqCols)
'Recursive, get next square
If (startPoint + 1 < totalSquares) Then
recurse(startPoint + 1)
Else
validSolution = isSolutionValid()
'Is solution valud
If (validSolution = True) Then
solutions = solutions + 1
End If
iterations = iterations + 1
'Response.Write("<br /><b>Iteration " & iterations & "</b>")
If (validSolution) Then
'Rate solution, record if best
solutionRating = rateSolution()
If solutionRating > bestCellSaving Then
bestCellSaving = solutionRating
copySolution()
End If
'Response.Write(" <span style='color:green'> <B>VALID SOLUTION</B></span> (" & rateSolution() & ")")
End If
'printCanvas(canvas)
End If
squareObjects(startPoint).removeSquare(canvas)
Next
Next
End If
End Sub
If you extract the loops in a separate routine the solution emerges relatively easily.
I have changed the validSolution logic a bit to make the code shorter - now we don't call recurse if the solution is invalid and we don't need to check for isSolutionValid() at the beginning of recurse(). These changes make counting the iterations harder so I removed that code, but it should be possible to add it later.
The recurse() routine without the last "If" statement should behave exactly as your code. The last "If" statement essentially performs the loops for a rotated rectangle.
I am not sure how removeSquare() is implemented, but it may need to know the orientation to be able to work correctly.
Sub recurse(ByVal startPoint As Integer)
Dim loopXTo As Integer
Dim loopYTo As Integer
If (startPoint = 0) Then
loopXTo = Math.Floor((canvasCols - squareObjects(startPoint).sqRows) / 2)
loopYTo = Math.Floor((canvasRows - squareObjects(startPoint).sqCols) / 2)
Else
loopXTo = canvasCols - squareObjects(startPoint).sqRows
loopYTo = canvasRows - squareObjects(startPoint).sqCols
End If
fitSqare(loopXTo, loopYTo, False)
If (squareObjects(startPoint).sqCols <> squareObjects(startPoint).sqRows) Then
fitSqare(loopYTo, loopXTo, True)
End If
End Sub
Sub fitSquare(ByVal loopXTo As Integer, ByVal loopYTo As Integer, ByVal rotate As Boolean)
Dim x As Integer
Dim y As Integer
Dim solutionRating As Integer
Dim validSolution As Boolean
'Loop all positions on canvas
For x = 0 To loopXTo
For y = 0 To loopYTo
'Set coords of square
squareObjects(startPoint).setSquareCords(x, y)
'Phyiscally place it in canvas
If (rotate) Then
placeSquareOnCanvas(x, y, squareObjects(startPoint).sqCols, squareObjects(startPoint).sqRows)
Else
placeSquareOnCanvas(x, y, squareObjects(startPoint).sqRows, squareObjects(startPoint).sqCols)
End If
validSolution = isSolutionValid()
'Is solution valud
If (validSolution) Then
'Recursive, get next square
If (startPoint + 1 < totalSquares) Then
recurse(startPoint + 1)
Else
solutions = solutions + 1
'Rate solution, record if best
solutionRating = rateSolution()
If solutionRating > bestCellSaving Then
bestCellSaving = solutionRating
copySolution()
End If
End If
End If
squareObjects(startPoint).removeSquare(canvas) 'removeSquare may require additional work to handle rotated state
Next
Next
End Sub
If the canvas is always a square then you don't need to change much. The result for the rotated rectangle 3 is the same as for the unrotated, except the origin of the Canvas is different. Imagine leaving the Rectangle 3 unrotated and rotating the canvas 90 degrees in the other direction. This means that you should be able to use some maths on the same results to get your answer.
Put your (x,y) coordinate loop in its own function. Then call the (x,y) coordinate loop on a rectangle of WxH, and then call it again on the rotated rectangle HxW.
Alternatively you can put the branching on the two rotations of your rectangle inside the (x,y) loop, after both coordinates have been picked, but before you make the recursive call.
In both cases you will need to be careful about whether your rotation causes the rectangle to exceed the height or width of your bounding box.
Can't you simply scan the list of shapes and for those that are rectangles (SizeX != SizeY) add a cloned rectangle with { SizeX = source.SizeY, SizeY = source.SizeX } (eg.: rotated rectangle)?
That would of course mean to do the loops twice (one for the unrotated list of shapes and one for the rotated one).
=> doing something like
squareObjects(startPoint) = squareObjects(startPoint).Rotate();
recurse(startPoint);
Frankly I don't think your implementation is the best- but if you don't want to make big changes and make separate routines you can just put the code for the rectangles twice in the same function-iteration.
So after:
'Phyiscally place it in canvas
placeSquareOnCanvas(x, y, squareObjects(startPoint).sqRows, squareObjects(startPoint).sqCols)
[......]
End If
squareObjects(startPoint).removeSquare(canvas)
You can do a check
IF the square is rectangle (width <> height)
then copy the same code again (in Then code) changing sqRows with sqCols in placeSquareOnCanvas().
The recursion will not be anymore linear, as this will make 2 recursive branches for each rectangle. Maybe it is not very nice written copying the same code 2 times, but the result will be right, the code changing is minimal and this straight solution based on your code will have more performance than trying other tweaks.