I am working on converting Velox file (HDF5) to .dm3 file using Tore Niermann's plugin (gms_plugin_hdf5) to read string. Annotations on HDF5 file also need to transfer to .dm3 file. HDF5 file maybe rotate in any angle. But the position coordinate of annotation read from hdf5 file is corresponding to images without rotating.
I found that the annotations didn't move with rotating images. I had to re-calculate the position coordinate for every annotation. It isn't convenient for annotations such as box or oval. And I need to extract maximum area when rotating images. So the image size will change with rotation angle. So is there better solutions for rotating the annotations? Thanks.
Here is a sample function from my script. I didn't attach all because it's quite long.
image GetAnnotations(Taggroup names, string filename, string name, Taggroup Annotations, Image VeloxImg, number Angle)
{
number i, j, imagex, imagey, xscale, yscale
String Displaypath, AnnotationStr, DisplayStr, units
taggroup attr = NewTagList()
getsize(VeloxImg, imagex, imagey)
number centerx=imagex/2
number centery=imagey/2
getscale(veloximg, xscale, yscale)
units=getunitstring(veloximg)
component imgdisp=imagegetimagedisplay(VeloxImg, 0)
For (j=0; j<TagGroupCountTags(Annotations); ++j)
{
TagGroupGetIndexedTagAsString(Annotations, j, AnnotationStr)
string AnnotPath=h5_read_string_dataset(filename, AnnotationStr)
string AnnotDataPath=GetValueFromLongStr(AnnotPath, "dataPath\": \"", "\"")
AnnotDataPath=ReplaceStr(AnnotDataPath, "\\/", "\/")
string AnnotLabel=GetValueFromLongStr(AnnotPath, "label\": \"", "\"")
string AnnotDrawPath=h5_read_string_dataset(filename, AnnotDataPath)
image img := RealImage( "", 4, 1, 1 )
TagGroup AnnoTag=alloc(MetaStr2TagGroup).ParseText2ImageTag(AnnotDrawPath, img )
deleteimage(img)
string AnnotDrawType=TagGroupGetTagLabel(AnnoTag,0)
//AnnoTag.TagGroupOpenBrowserWindow( "AnnotationsTag", 0 )
if (AnnotDrawType=="arrow")
{
number p1_x,p1_y,p2_x,p2_y
TagGroupGetTagAsNumber(AnnoTag, "arrow:p1:x", p1_x)
TagGroupGetTagAsNumber(AnnoTag, "arrow:p1:y", p1_y)
TagGroupGetTagAsNumber(AnnoTag, "arrow:p2:x", p2_x)
TagGroupGetTagAsNumber(AnnoTag, "arrow:p2:y", p2_y)
//VeloxImg.CreateArrowAnnotation( p1y, p1x, p2y, p2x )
number p1_x_new=(p1_x-0.5)*cos(Angle)+(p1_y-0.5)*sin(Angle)+0.5
number p1_y_new=-(p1_x-0.5)*sin(Angle)+(p1_y-0.5)*cos(Angle)+0.5
number p2_x_new=(p2_x-0.5)*cos(Angle)+(p2_y-0.5)*sin(Angle)+0.5
number p2_y_new=-(p2_x-0.5)*sin(Angle)+(p2_y-0.5)*cos(Angle)+0.5
result(p1_x+" "+p1_y+" new "+p1_x_new+" "+p2_y_new+"\n")
component arrowAnno=newarrowannotation(p1_y_new*imagey, p1_x_new*imagex, p2_y_new*imagey, p2_x_new*imagex)
arrowAnno.ComponentSetForegroundColor( 1, 0 , 0 )
arrowAnno.ComponentSetDrawingMode( 2 )
imgdisp.ComponentAddChildAtEnd( arrowAnno )
}
Not directly answering your question, but maybe nevertheless of interest to you:
While GMS does not support the rotation of annotations (Rect, Oval, Text, ImageDisplay...) it does support a rotation property for ROIs. So maybe you can just use rect-ROIs and oval-ROIs instead of annotations in your application.
Example (never mind, that I did the shift-computation wrongly):
image test := realImage("Test",4,512,512)
test= abs(sin(6*PI()*icol/iwidth))*abs(cos(4*PI()*irow/iheight*iradius/150))
test.showimage()
imageDisplay disp = test.ImageGetImageDisplay(0)
ROI box = NewROI()
box.RoiSetRectangle(46,88,338,343)
box.RoiSetVolatile(0)
ROI oval = NewROI()
oval.ROISetOval(221,226,287,254)
oval.RoiSetVolatile(0)
disp.ImageDisplayAddROI(box)
disp.ImageDisplayAddROI(oval)
number rot_deg = 8
image rot := test.rotate( pi()/180*rot_deg)
rot.ShowImage()
imageDisplay disp_rot = rot.ImageGetImageDisplay(0)
ROI box_rot = box.ROIClone()
ROI oval_rot = oval.ROIClone()
disp_rot.ImageDisplayAddROI(box_rot)
disp_rot.ImageDisplayAddROI(oval_rot)
number shift_x = (rot.ImageGetDimensionSize(0)-test.ImageGetDimensionSize(0)) / 2
number shift_y = (rot.ImageGetDimensionSize(1)-test.ImageGetDimensionSize(1)) / 2
number t,l,b,r
box_rot.ROIGetRectangle(t,l,b,r)
box_rot.ROISetRectangle(t+shift_y,l+shift_x,b+shift_y,r+shift_x)
oval_rot.ROIGetOval(t,l,b,r)
oval_rot.ROISetOval(t+shift_y,l+shift_x,b+shift_y,r+shift_x)
box_rot.ROISetRotationAngle( rot_deg )
oval_rot.ROISetRotationAngle( rot_deg )
If I understood you correctly, then your source data (HDF5) stores the image (2D array?) plus a rotation angle, but the annotations in the coordinate system of the (not rotated) image? How is the source-data displayed in the original software then? (Is it showing a rotated rectangle-image?)
GMS does not support rotating imagesDisplays (as objects) and consequently also not rotations of annotations. The coordinates systems are always screen-axis aligned orthogonal. Hence the need for interpolation when "rotating" images. The data values are re-computed for the new grid.
If you don't need the annotations to be adjustable after your input, one potential thing you could do would be to create an "as displayed" image after import prior rotation, and then rotate the image with the annotations "burnt in".
This is obviously only good for creating "final display images" though.
image before := realImage("Test",4,512,512)
before = abs(sin(6*PI()*icol/iwidth))*abs(cos(4*PI()*irow/iheight*iradius/150))
before.showimage()
before.ImageGetImageDisplay(0).ComponentAddChildAtEnd(NewArrowAnnotation(30,80,60,430))
before.ImageGetImageDisplay(0).ComponentAddChildAtEnd(NewArrowAnnotation(30,80,206,240))
before.ImageGetImageDisplay(0).ComponentAddChildAtEnd(NewOvalAnnotation(206,220,300,256))
// Create as-displayed image
Number t,l,b,r,ofx,ofy,scx,scy
before.ImageGetOrCreateImageDocument().ImageDocumentGetViewExtent(t,l,b,r)
before.ImageGetOrCreateImageDocument().ImageDocumentGetViewToWindowTransform(ofx,ofy,scx,scy)
image asShown := before.ImageGetOrCreateImageDocument().ImageDocumentCreateRGBImageFromDocument(round((r-l)*scx),round((b-t)*scy),0,0)
asShown.ShowImage()
number angle_deg = 8
image rotated := asShown.Rotate( angle_deg/180*PI() )
rotated.ShowImage()
FIB/SEM images have a text bar at the bottom of the image.
When imported into GMS, any contrast, gamma, .. adjustment also affects the text bar.
Is it possible to break up the image and have the data processing affect only the actual image - not the text bar?
The best you can do here is to break the actual image array into 2 separate images and then have the text-bar section displayed as a separate imageDisplay which you can add onto the imageDisplay of the data. You can shift/scale them with respect to each other, and you can also lock the added display so that it can not be shifted by mouse anymore. The following example should do what you need:
void CropAndMerge(Image img, number h){
number sx = img.ImageGetDimensionSize(0)
number sy = img.ImageGetDimensionSize(1)
image data := img.slice2(0,0,0,0,sx,1,1,sy-h,1).ImageClone() // note ":=", we sub-slice keeping tags and all
image anno = img.slice2(0,sy-h,0,0,sx,1,1,h,1) // note "=", we just want the data copy
imageDisplay disp
// Simple way to get imageDisplay. First show, then grab
// data.ShowImage()
// disp = data.ImageGetImageDisplay(0)
// Better alternative: No need to show
imageDocument doc = NewImageDocument( img.ImageGetName() )
doc.ImageDocumentAddImage( data )
// doc.ImageDocumentAddImage( anno ) // Use this to add 'side ordered' in case of page-view type. However, I'd rather not use page-mode.
disp = data.ImageGetImageDisplay(0)
disp.ImageDisplaySetColorTableByName( "Black Body Extended" ) // Just to show you can act on the display before actually showing it that way
// Add Annotation area as annotation on imageDisplay (all are components)
imageDisplay annoDisp = NewImageDisplay( anno, "best" )
disp.ComponentAddChildAtEnd( annoDisp )
// move out of the way
// ComponentPositionAroundPoint: Moves the annotation so the 'rel_x' horizontal point in the bounding rect is at 'new_x' (if bool horz is true), and for y accordinglye
number doVert = 1
number rel_y = 1.0 // bottom (relative coordinate!)
number new_y = sy // becomes bottom (absolute position)
annoDisp.ComponentPositionAroundPoint( 0,new_y,0,rel_y,0,doVert)
// make sure nobody messes with the annotation area
annoDisp.ComponentSetSelectable(0)
doc.ImageDocumentShow()
}
number sx = 1024
number sy = 1024
number h = 300
image in := realimage("Imported",4,sx,sy)
in = (icol%100 + iradius*sin(irow/iheight*5*Pi() + itheta )**2)
in.slice2(0,sy-h,0,0,sx,1,1,h,1) = (icol+irow)%50>45?max(in)+100:0
//in.showimage()
CropAndMerge(in.imageClone(),h)
I'm trying to my grid system to LESS.
I'm using fractions as strings (there's a reason) and need to interpolate them in the calc() expression.
In Sass I can do this...
#mixin move($fraction: '1/1') {
position: relative;
left: calc(99.999999% * #{$fraction});
}
In LESS when I try this...
.move(#fraction: '1/1') {
#_fraction: ~'#{fraction}';
left: calc(99.999999% * #_fraction);
}
It ends up throwing a Cannot read property 'numerator' of undefined error.
Apparently LESS can tell it's a fraction but then it poops out.
Can any LESS pros enlighten me?
You need to escape multiplication, addition, division, and subtraction in LESS or it will try output the calculated result. When less sees * in your code, it is trying to multiply 99.999999% and #_fraction.
For example when LESS sees calc(5px + 5px); it will output left: calc(10px);. You can except it like this ~'calc(5px + 5px);'.
Try using this code.
.move(#fraction: '1/1') {
#_fraction: ~'#{fraction}';
left: calc(~'99.999999% *' #_fraction);
}
I'm looking at the bootstrap LESS source code and there is the following piece there:
.calc-grid-column(#index, #class, #type) when (#type = width) and (#index > 0) {
.col-#{class}-#{index} {
width: percentage((#index / #grid-columns));
}
}
Why do they write -#{class} instead of simply -#class?
As already explained by #Damien_The_Unbeliever Less enables you to use selector names, property names, URLs and #import statements. (also attribute selectors?).
The curly braces {} are used to make the variable name clear. In the case of #ab you can read variable #a or #ab. #{a}b forces that the variable #a will be used and has a string b appended.
I have icon sprites image with each icon in a 20 by 20 pixel area. Each icon has several variants (black, colour, white small etc.). And have a significant amount of them. Instead of writing styles for each individual icon I'd rather just provide their names in my LESS file and let the processor generate styles for them.
This is what I came up with but it doesn't seem to work.
#icons: upvote,downvote,comment,new,notify,search,popup,eye,cross;
#array: ~`(function(i){ return (i + "").replace(/[\[\] ]/gi, "").split(","); })("#{icons}")`;
#count: ~`(function(i){ return i.split(",").length; })("#{icons}")`;
.iconize (#c) when (#c < #count) {
#val: ~`(function(a){ return a.replace(" ","").split(",")[0]; })("#{array}")`;
#array: ~`(function(a){ a = a.replace(" ","").split(","); a.splice(0, 1); return a; })("#{array}")`;
&.#{val} { background-position: (-20px * #c) 0; }
&.color.#{val} { background-position: (-20px * #c) -20px; }
&.white.#{val} { background-position: (-20px * #c) -40px; }
.iconize(#c + 1);
}
.iconize(#c) when (#c = #count) {}
.iconize(0);
The only thing I'd like to edit is the #icons variable where I just enter their names. And I'm using Web Essentials addin for Visual Studio 2013 to automatically process my LESS file on file save.
What am I doing wrong?
Pure LESS (assuming you're using Web Essentials 2013 which uses LESS 1.5.x):
#icons: upvote, downvote, comment, new, notify, search, popup, eye, cross;
.iconize();
.iconize(#i: length(#icons)) when (#i > 0) {
.iconize((#i - 1));
#value: extract(#icons, #i); // LESS arrays are 1-based
.#{value} {background-position: (-20px * (#i - 1)) 0}
.color.#{value} {background-position: (-20px * (#i - 1)) -20px}
.white.#{value} {background-position: (-20px * (#i - 1)) -40px}
}
I removed & from selector names since it has no effect when you generate these classes in the global scope (but put it back if you actually need .iconize to be nested in another ruleset). It is also possible to calculate array length in earlier LESS versions (that have no length function) w/o any javascript, but I don't list this method here since it's quite scary (and you don't need it anyway).
Your javascript based loop is in fact less or more correct but the problem is all values returned by LESS inline javascript are of so-called "anonymous value" type and not a numbers so that when (#c < #count) condition is always true and the loop becomes infinite. (basically the condition is expanded exactly as when (0 < ~'9') ... when (9 < ~'9') = true etc.)
I think it depends on the version of LESS you use. Different versions of LESS handle array like structures and their length different.
Since LESS 1.5 you can define an array with quotes, like:
#array: "value1","value2"; and calculate its length with length(#array).
For example see also:
Sprites LESS CSS Variable increment issue
With LESS 1.5 your code ends in an endless loop: "SyntaxError: Maximum call stack size exceeded in"