graphviz: circular layout while preserving node order - layout
Hey
I want to plot a graph of 128 nodes (labeled 1 to 128) in graphviz using circular layout. Circo does this, but I want the nodes to be placed in order of their label number instead of the order created by circo (based on the edges between them). Also, there may be nodes with no incoming or outgoing edges, but still have to be placed in the circular order.
I have tried fiddling with edge weight but it didn't affect anything. I could get the no-edge nodes to appear in the circo circle using invisible edges between adjacent nodes (e.g. 1->2, 2->3, ..., 128->1). But the order is still left wanting.
Is there any way to achieve this? I would really appreciate any help in this regard. Here is my code:
digraph{
size="8,6"
layout=circo
node [shape=square,fontsize=300,penwidth=2]
1->2 [style=invis]
2->3 [style=invis]
3->4 [style=invis]
4->5 [style=invis]
5->6 [style=invis]
6->7 [style=invis]
7->8 [style=invis]
8->9 [style=invis]
9->10 [style=invis]
10->11 [style=invis]
11->12 [style=invis]
12->13 [style=invis]
13->14 [style=invis]
14->15 [style=invis]
15->16 [style=invis]
16->17 [style=invis]
17->18 [style=invis]
18->19 [style=invis]
19->20 [style=invis]
20->21 [style=invis]
21->22 [style=invis]
22->23 [style=invis]
23->24 [style=invis]
24->25 [style=invis]
25->26 [style=invis]
26->27 [style=invis]
27->28 [style=invis]
28->29 [style=invis]
29->30 [style=invis]
30->31 [style=invis]
31->32 [style=invis]
32->33 [style=invis]
33->34 [style=invis]
34->35 [style=invis]
35->36 [style=invis]
36->37 [style=invis]
37->38 [style=invis]
38->39 [style=invis]
39->40 [style=invis]
40->41 [style=invis]
41->42 [style=invis]
42->43 [style=invis]
43->44 [style=invis]
44->45 [style=invis]
45->46 [style=invis]
46->47 [style=invis]
47->48 [style=invis]
48->49 [style=invis]
49->50 [style=invis]
50->51 [style=invis]
51->52 [style=invis]
52->53 [style=invis]
53->54 [style=invis]
54->55 [style=invis]
55->56 [style=invis]
56->57 [style=invis]
57->58 [style=invis]
58->59 [style=invis]
59->60 [style=invis]
60->61 [style=invis]
61->62 [style=invis]
62->63 [style=invis]
63->64 [style=invis]
64->65 [style=invis]
65->66 [style=invis]
66->67 [style=invis]
67->68 [style=invis]
68->69 [style=invis]
69->70 [style=invis]
70->71 [style=invis]
71->72 [style=invis]
72->73 [style=invis]
73->74 [style=invis]
74->75 [style=invis]
75->76 [style=invis]
76->77 [style=invis]
77->78 [style=invis]
78->79 [style=invis]
79->80 [style=invis]
80->81 [style=invis]
81->82 [style=invis]
82->83 [style=invis]
83->84 [style=invis]
84->85 [style=invis]
85->86 [style=invis]
86->87 [style=invis]
87->88 [style=invis]
88->89 [style=invis]
89->90 [style=invis]
90->91 [style=invis]
91->92 [style=invis]
92->93 [style=invis]
93->94 [style=invis]
94->95 [style=invis]
95->96 [style=invis]
96->97 [style=invis]
97->98 [style=invis]
98->99 [style=invis]
99->100 [style=invis]
100->101 [style=invis]
101->102 [style=invis]
102->103 [style=invis]
103->104 [style=invis]
104->105 [style=invis]
105->106 [style=invis]
106->107 [style=invis]
107->108 [style=invis]
108->109 [style=invis]
109->110 [style=invis]
110->111 [style=invis]
111->112 [style=invis]
112->113 [style=invis]
113->114 [style=invis]
114->115 [style=invis]
115->116 [style=invis]
116->117 [style=invis]
117->118 [style=invis]
118->119 [style=invis]
119->120 [style=invis]
120->121 [style=invis]
121->122 [style=invis]
122->123 [style=invis]
123->124 [style=invis]
124->125 [style=invis]
125->126 [style=invis]
126->127 [style=invis]
127->128 [style=invis]
128->1 [style=invis]
25->42 [penwidth=5]
25->71 [penwidth=7]
26->25 [penwidth=5]
26->40 [penwidth=6]
27->30 [penwidth=6]
29->25 [penwidth=9]
29->26 [penwidth=9]
29->27 [penwidth=6]
29->30 [penwidth=4]
29->32 [penwidth=4]
29->40 [penwidth=5]
29->80 [penwidth=5]
32->39 [penwidth=5]
33->28 [penwidth=5]
33->44 [penwidth=4]
33->74 [penwidth=6]
37->34 [penwidth=6]
37->66 [penwidth=5]
37->69 [penwidth=4]
38->60 [penwidth=4]
38->107 [penwidth=5]
40->100 [penwidth=5]
47->30 [penwidth=4]
48->35 [penwidth=6]
48->36 [penwidth=4]
50->35 [penwidth=5]
50->63 [penwidth=5]
51->50 [penwidth=5]
51->96 [penwidth=4]
52->50 [penwidth=8]
53->51 [penwidth=7]
53->96 [penwidth=4]
59->50 [penwidth=5]
59->51 [penwidth=6]
59->52 [penwidth=5]
59->60 [penwidth=5]
60->50 [penwidth=10]
60->63 [penwidth=4]
60->95 [penwidth=4]
67->74 [penwidth=4]
67->114 [penwidth=4]
68->74 [penwidth=5]
70->74 [penwidth=6]
70->126 [penwidth=4]
71->74 [penwidth=8]
71->86 [penwidth=4]
72->70 [penwidth=4]
75->39 [penwidth=4]
77->81 [penwidth=5]
79->73 [penwidth=6]
80->84 [penwidth=4]
82->78 [penwidth=5]
82->114 [penwidth=4]
86->115 [penwidth=5]
87->115 [penwidth=5]
87->121 [penwidth=5]
91->69 [penwidth=5]
91->87 [penwidth=5]
96->30 [penwidth=5]
96->114 [penwidth=5]
101->107 [penwidth=5]
102->108 [penwidth=5]
107->75 [penwidth=5]
107->78 [penwidth=6]
108->95 [penwidth=5]
108->103 [penwidth=4]
111->80 [penwidth=5]
111->114 [penwidth=5]
114->128 [penwidth=4]
115->114 [penwidth=4]
118->128 [penwidth=5]
119->103 [penwidth=5]
121->72 [penwidth=4]
123->116 [penwidth=5]
125->80 [penwidth=4]
126->122 [penwidth=7]
128->96 [penwidth=5]
}
I think, the only solution is to use a neato' layout and the pos attribut.
To do what you want, I start by creating a small Ruby script to calculate all nodes positions :
radius = 20
(1..128).each do |i|
x = Math.cos(((Math::PI*2)/128.0)*i.to_f)*radius
y = Math.sin(((Math::PI*2)/128.0)*i.to_f)*radius
puts " #{i}[label=\"#{i}\", pos=\"#{x},#{y}!\", shape = \"square\"];"
end
Then, I put the result in the graphviz script :
digraph G {
layout="neato"
1[label="1", pos="19.9759091241034,0.98135348654836!", shape = "square"];
2[label="2", pos="19.9036945334439,1.96034280659121!", shape = "square"];
3[label="3", pos="19.7835301992956,2.93460948910723!", shape = "square"];
4[label="4", pos="19.6157056080646,3.90180644032256!", shape = "square"];
5[label="5", pos="19.4006250638909,4.85960359806528!", shape = "square"];
6[label="6", pos="19.1388067146442,5.80569354508925!", shape = "square"];
7[label="7", pos="18.8308813036604,6.7377970678444!", shape = "square"];
8[label="8", pos="18.4775906502257,7.6536686473018!", shape = "square"];
9[label="9", pos="18.0797858624689,8.55110186860564!", shape = "square"];
10[label="10", pos="17.6384252869671,9.42793473651995!", shape = "square"];
11[label="11", pos="17.1545722000054,10.2820548838644!", shape = "square"];
12[label="12", pos="16.6293922460509,11.111404660392!", shape = "square"];
13[label="13", pos="16.0641506296129,11.9139860898487!", shape = "square"];
14[label="14", pos="15.4602090672547,12.6878656832729!", shape = "square"];
15[label="15", pos="14.8190225070992,13.4311790969404!", shape = "square"];
16[label="16", pos="14.142135623731,14.1421356237309!", shape = "square"];
17[label="17", pos="13.4311790969404,14.8190225070992!", shape = "square"];
18[label="18", pos="12.6878656832729,15.4602090672547!", shape = "square"];
19[label="19", pos="11.9139860898487,16.0641506296129!", shape = "square"];
20[label="20", pos="11.111404660392,16.6293922460509!", shape = "square"];
21[label="21", pos="10.2820548838644,17.1545722000054!", shape = "square"];
22[label="22", pos="9.42793473651996,17.6384252869671!", shape = "square"];
23[label="23", pos="8.55110186860564,18.0797858624689!", shape = "square"];
24[label="24", pos="7.6536686473018,18.4775906502257!", shape = "square"];
25[label="25", pos="6.7377970678444,18.8308813036604!", shape = "square"];
26[label="26", pos="5.80569354508925,19.1388067146442!", shape = "square"];
27[label="27", pos="4.85960359806528,19.4006250638909!", shape = "square"];
28[label="28", pos="3.90180644032257,19.6157056080646!", shape = "square"];
29[label="29", pos="2.93460948910723,19.7835301992956!", shape = "square"];
30[label="30", pos="1.96034280659122,19.9036945334439!", shape = "square"];
31[label="31", pos="0.981353486548363,19.9759091241034!", shape = "square"];
32[label="32", pos="1.22464679914735e-15,20.0!", shape = "square"];
33[label="33", pos="-0.98135348654836,19.9759091241034!", shape = "square"];
34[label="34", pos="-1.96034280659121,19.9036945334439!", shape = "square"];
35[label="35", pos="-2.93460948910723,19.7835301992956!", shape = "square"];
36[label="36", pos="-3.90180644032256,19.6157056080646!", shape = "square"];
37[label="37", pos="-4.85960359806528,19.4006250638909!", shape = "square"];
38[label="38", pos="-5.80569354508924,19.1388067146442!", shape = "square"];
39[label="39", pos="-6.7377970678444,18.8308813036604!", shape = "square"];
40[label="40", pos="-7.65366864730179,18.4775906502257!", shape = "square"];
41[label="41", pos="-8.55110186860564,18.0797858624689!", shape = "square"];
42[label="42", pos="-9.42793473651995,17.6384252869671!", shape = "square"];
43[label="43", pos="-10.2820548838644,17.1545722000054!", shape = "square"];
44[label="44", pos="-11.111404660392,16.6293922460509!", shape = "square"];
45[label="45", pos="-11.9139860898487,16.0641506296129!", shape = "square"];
46[label="46", pos="-12.6878656832729,15.4602090672547!", shape = "square"];
47[label="47", pos="-13.4311790969404,14.8190225070992!", shape = "square"];
48[label="48", pos="-14.1421356237309,14.142135623731!", shape = "square"];
49[label="49", pos="-14.8190225070992,13.4311790969404!", shape = "square"];
50[label="50", pos="-15.4602090672547,12.6878656832729!", shape = "square"];
51[label="51", pos="-16.0641506296129,11.9139860898487!", shape = "square"];
52[label="52", pos="-16.6293922460509,11.111404660392!", shape = "square"];
53[label="53", pos="-17.1545722000054,10.2820548838644!", shape = "square"];
54[label="54", pos="-17.6384252869671,9.42793473651996!", shape = "square"];
55[label="55", pos="-18.0797858624689,8.55110186860564!", shape = "square"];
56[label="56", pos="-18.4775906502257,7.6536686473018!", shape = "square"];
57[label="57", pos="-18.8308813036604,6.73779706784441!", shape = "square"];
58[label="58", pos="-19.1388067146442,5.80569354508925!", shape = "square"];
59[label="59", pos="-19.4006250638909,4.85960359806528!", shape = "square"];
60[label="60", pos="-19.6157056080646,3.90180644032257!", shape = "square"];
61[label="61", pos="-19.7835301992956,2.93460948910724!", shape = "square"];
62[label="62", pos="-19.9036945334439,1.96034280659122!", shape = "square"];
63[label="63", pos="-19.9759091241034,0.98135348654836!", shape = "square"];
64[label="64", pos="-20.0,2.44929359829471e-15!", shape = "square"];
65[label="65", pos="-19.9759091241034,-0.981353486548354!", shape = "square"];
66[label="66", pos="-19.9036945334439,-1.96034280659121!", shape = "square"];
67[label="67", pos="-19.7835301992956,-2.93460948910723!", shape = "square"];
68[label="68", pos="-19.6157056080646,-3.90180644032257!", shape = "square"];
69[label="69", pos="-19.4006250638909,-4.85960359806528!", shape = "square"];
70[label="70", pos="-19.1388067146442,-5.80569354508924!", shape = "square"];
71[label="71", pos="-18.8308813036604,-6.7377970678444!", shape = "square"];
72[label="72", pos="-18.4775906502257,-7.65366864730179!", shape = "square"];
73[label="73", pos="-18.0797858624689,-8.55110186860564!", shape = "square"];
74[label="74", pos="-17.6384252869671,-9.42793473651995!", shape = "square"];
75[label="75", pos="-17.1545722000054,-10.2820548838644!", shape = "square"];
76[label="76", pos="-16.6293922460509,-11.111404660392!", shape = "square"];
77[label="77", pos="-16.0641506296129,-11.9139860898487!", shape = "square"];
78[label="78", pos="-15.4602090672547,-12.6878656832729!", shape = "square"];
79[label="79", pos="-14.8190225070992,-13.4311790969404!", shape = "square"];
80[label="80", pos="-14.142135623731,-14.1421356237309!", shape = "square"];
81[label="81", pos="-13.4311790969404,-14.8190225070992!", shape = "square"];
82[label="82", pos="-12.6878656832729,-15.4602090672547!", shape = "square"];
83[label="83", pos="-11.9139860898487,-16.0641506296129!", shape = "square"];
84[label="84", pos="-11.111404660392,-16.6293922460509!", shape = "square"];
85[label="85", pos="-10.2820548838644,-17.1545722000054!", shape = "square"];
86[label="86", pos="-9.42793473651996,-17.6384252869671!", shape = "square"];
87[label="87", pos="-8.55110186860565,-18.0797858624689!", shape = "square"];
88[label="88", pos="-7.65366864730181,-18.4775906502257!", shape = "square"];
89[label="89", pos="-6.7377970678444,-18.8308813036604!", shape = "square"];
90[label="90", pos="-5.80569354508925,-19.1388067146442!", shape = "square"];
91[label="91", pos="-4.85960359806528,-19.4006250638909!", shape = "square"];
92[label="92", pos="-3.90180644032257,-19.6157056080646!", shape = "square"];
93[label="93", pos="-2.93460948910725,-19.7835301992956!", shape = "square"];
94[label="94", pos="-1.96034280659121,-19.9036945334439!", shape = "square"];
95[label="95", pos="-0.981353486548361,-19.9759091241034!", shape = "square"];
96[label="96", pos="-3.67394039744206e-15,-20.0!", shape = "square"];
97[label="97", pos="0.981353486548353,-19.9759091241034!", shape = "square"];
98[label="98", pos="1.9603428065912,-19.9036945334439!", shape = "square"];
99[label="99", pos="2.93460948910724,-19.7835301992956!", shape = "square"];
100[label="100", pos="3.90180644032257,-19.6157056080646!", shape = "square"];
101[label="101", pos="4.85960359806528,-19.4006250638909!", shape = "square"];
102[label="102", pos="5.80569354508924,-19.1388067146442!", shape = "square"];
103[label="103", pos="6.73779706784439,-18.8308813036604!", shape = "square"];
104[label="104", pos="7.6536686473018,-18.4775906502257!", shape = "square"];
105[label="105", pos="8.55110186860564,-18.0797858624689!", shape = "square"];
106[label="106", pos="9.42793473651995,-17.6384252869671!", shape = "square"];
107[label="107", pos="10.2820548838644,-17.1545722000054!", shape = "square"];
108[label="108", pos="11.111404660392,-16.6293922460509!", shape = "square"];
109[label="109", pos="11.9139860898487,-16.0641506296129!", shape = "square"];
110[label="110", pos="12.6878656832729,-15.4602090672547!", shape = "square"];
111[label="111", pos="13.4311790969404,-14.8190225070992!", shape = "square"];
112[label="112", pos="14.1421356237309,-14.142135623731!", shape = "square"];
113[label="113", pos="14.8190225070992,-13.4311790969404!", shape = "square"];
114[label="114", pos="15.4602090672547,-12.6878656832729!", shape = "square"];
115[label="115", pos="16.0641506296129,-11.9139860898487!", shape = "square"];
116[label="116", pos="16.6293922460509,-11.111404660392!", shape = "square"];
117[label="117", pos="17.1545722000054,-10.2820548838644!", shape = "square"];
118[label="118", pos="17.6384252869671,-9.42793473651996!", shape = "square"];
119[label="119", pos="18.0797858624689,-8.55110186860565!", shape = "square"];
120[label="120", pos="18.4775906502257,-7.65366864730181!", shape = "square"];
121[label="121", pos="18.8308813036604,-6.7377970678444!", shape = "square"];
122[label="122", pos="19.1388067146442,-5.80569354508925!", shape = "square"];
123[label="123", pos="19.4006250638909,-4.85960359806528!", shape = "square"];
124[label="124", pos="19.6157056080646,-3.90180644032257!", shape = "square"];
125[label="125", pos="19.7835301992956,-2.93460948910725!", shape = "square"];
126[label="126", pos="19.9036945334439,-1.96034280659121!", shape = "square"];
127[label="127", pos="19.9759091241034,-0.981353486548362!", shape = "square"];
128[label="128", pos="20.0,-4.89858719658941e-15!", shape = "square"];
25->42
25->71
26->25
26->40
27->30
29->25
29->26
29->27
29->30
29->32
29->40
29->80
32->39
33->28
33->44
33->74
37->34
37->66
37->69
38->60
38->107
40->100
47->30
48->35
48->36
50->35
50->63
51->50
51->96
52->50
53->51
53->96
59->50
59->51
59->52
59->60
60->50
60->63
60->95
67->74
67->114
68->74
70->74
70->126
71->74
71->86
72->70
75->39
77->81
79->73
80->84
82->78
82->114
86->115
87->115
87->121
91->69
91->87
96->30
96->114
101->107
102->108
107->75
107->78
108->95
108->103
111->80
111->114
114->128
115->114
118->128
119->103
121->72
123->116
125->80
126->122
128->96
}
Generating your own node positions is the best solution outside of coming up with a better algorithm or adding weighting to circo by altering the graphviz source.
However, it does defeat the purpose of generating arbitrary graphs with graphviz. This script will use graphviz itself to generate a circle of arbitrary size with user defined formatting, hardcode the position, and then add edges across the center.
#!/bin/bash
# loopgen.sh- generates a plain graphviz loop then hardcodes it and adds to it
# input file format -
# num of nodes
# prefixes for the generated file (format information, labels)
# (blank)
# postfixes for the final file (extra connections, inputscale)
# output - graph with nodes0 to nodex
file=$(<$1)
# trim filename to function name
fun=${1##*/}
fun=${fun%%.*}
# gen is generation function
gen="digraph $fun
{
layout=circo;"
# fin is final function
fin=""
# get the number of inputs
num=$(head -n 1 <<< "$file")
if [ $num -lt 2 ]; then
echo "Bad number of inputs."
exit -1
fi
# increment the lines of the file
file=$(tail -n +2 <<< "$file")
# add all lines up to the first blank line
gen="$gen
$(printf "$file" | awk '!p;/^$/{p=1}')"
# remove all lines before the first blank line
file=$(printf "$file" | awk '/^$/{p=1}p')
# begin producing character-based nodes
i=1
gen="$gen
node0 "
while [ $i -lt $num ]; do
gen="$gen -> node$i"
let i=i+1
done
#finish the loop
gen="$gen -> node0;
}"
# generate, replace circo layout reference, make positions absolute
gen=$(printf "$gen" | dot |
sed -e 's/layout=circo/layout=neato/' -e 's/\(pos=".*\)"/\1!"/g')
# remove trailing brace
gen=$(printf "$gen" | head -n -1)
fin="/* generated with loopgen.sh */
$gen
$file
}"
printf "$fin"
exit 0
Here is an example file.
6
node0 [label="bop it"];
node1 [label="twist it"];
node2 [label="pull it"];
node3 [label="flick it"];
node4 [label="spin it"];
node5 [label="throw it away"];
node2 -> node5 [constraint=false,weight=0];
// this keeps the program from running out of memory
inputscale=72
Running
loopgen.sh input | neato -Tpng > output.png
Results in
Where normally, the same layout would result in
Related
YoloV1 : how to turn predicted bounding boxe relative coordinates into absolute ones
I'm struggling to create a light YoloV1 (with only one bounding box) on MNIST dataset (I randomly paste 28x28 digit into a 75x75 black background). I can't figure out how to turn relative-to-cell coordinates into absolute coordinates. Since now, I'm using the groundtruth bounding boxes to retrieve the cell which should contain an object, then I save the i,j positions, then I use those positions to get back to absolute coordinates with my predictions. This method works but when it's time to detect a real image, I won't have the groundtruth coordinates and so, the i,j object position, and so the absolute position of the predicted bounding box. I provide some line of code : Encoding absolute coordinates of shape (N,4) to (N,S,S,5) def encode(self, box): """ box : torch.Tensor of shape (N,4) Absolute coordinates [xmin, ymin, w_bbox, h_bbox] """ ### Absolute box infos xmin, ymin, w_bbox, h_bbox = box ### Relative box infos rw = w_bbox / 75 rh = h_bbox / 75 rx_min = xmin / 75 ry_min = ymin / 75 ### x and y box center coords rxc = (rx_min + rw/2) ryc = (ry_min + rh/2) ### Object grid location i = (rxc / self.cell_size).ceil() - 1.0 j = (ryc / self.cell_size).ceil() - 1.0 i, j = int(i), int(j) ### x & y of the cell left-top corner x0 = i * self.cell_size y0 = j * self.cell_size ### x & y of the box on the cell, normalized from 0.0 to 1.0. x_norm = (rxc - x0) / self.cell_size y_norm = (ryc - y0) / self.cell_size box_target = torch.zeros(self.S, self.S, 4+1) box_target[j, i, :5] = torch.Tensor([x_norm, y_norm, rw, rh, 1.]) return box_target Convert relative-to-cell coordinates into absolute ones def relative2absolute(box_true:torch.Tensor, box_pred:torch.Tensor)->tuple: """ Turns bounding box relative to cell coordinates into absolute coordinates (pixels). Used to calculate IoU and to plot boxes. Args: box_true : torch.Tensor of shape (N, S, S, 5) Groundtruth bounding box coordinates to convert. box_pred : torch.Tensor of shape (N, S, S, 5) Predicted bounding box coordinates to convert. Return: box_true_absolute : torch.Tensor of shape (N, 4) box_pred_absolute : torch.Tensor of shape (N, 4) """ assert len(box_true.shape)==4 and len(box_pred.shape)==4, "Bbox should be of size (N,S,S,5)." SIZEHW = 75 S = 6 CELL_SIZE = 1/S ### Get non-zero coordinates cells_with_obj = box_true.nonzero()[::5] N, cells_i, cells_j, _ = cells_with_obj.permute(1,0) ### Retrieving box coordinates. TBM if nb_obj > 1 xrcell_true, yrcell_true, rw_true, rh_true = box_true[N, cells_i, cells_j, 0:4].permute(1,0) xrcell_pred, yrcell_pred, rw_pred, rh_pred = box_pred[N, cells_i, cells_j, 0:4].permute(1,0) ### Compute relative-to-image center coordinates xc_rimg_true = xrcell_true * CELL_SIZE + cells_j * CELL_SIZE xc_rimg_pred = xrcell_pred * CELL_SIZE + cells_j * CELL_SIZE yc_rimg_true = yrcell_true * CELL_SIZE + cells_i * CELL_SIZE yc_rimg_pred = yrcell_pred * CELL_SIZE + cells_i * CELL_SIZE ### Compute absolute top left coordinates xmin_true = (xc_rimg_true - rw_true/2) * SIZEHW xmin_pred = (xc_rimg_pred - rw_pred/2) * SIZEHW ymin_true = (yc_rimg_true - rh_true/2) * SIZEHW ymin_pred = (yc_rimg_pred - rh_pred/2) * SIZEHW ### Compute absolute bottom right coordinates xmax_true = xmin_true + rw_true*SIZEHW xmax_pred = xmin_pred + rw_pred*SIZEHW ymax_true = ymin_true + rh_true*SIZEHW ymax_pred = ymin_pred + rh_pred*SIZEHW ### Stacking box_true_absolute = torch.stack((xmin_true, ymin_true, xmax_true, ymax_true), dim=-1) box_pred_absolute = torch.stack((xmin_pred, ymin_pred, xmax_pred, ymax_pred), dim=-1) return box_true_absolute, box_pred_absolute
Create graphic from array
the following code does not work and I can not fix it, someone could help me? The error that comes out is as follows. What this should do is draw in a window all the points of the array, with time being the X and the value being Y, but when passing it through a loop it fails. pt = Point(array2[x], 100) IndexError: only integers, slices (`:`), ellipsis (`...`), numpy.newaxis (`None`) and integer or boolean arrays are valid indices Code import numpy as np from graphics import * arrayX = [] arrayY = [] win = GraphWin(width = 600, height = 300) # create a window arrayX = np.loadtxt("time.txt") arrayY = np.loadtxt("value.txt") array2 = arrayX.astype(int) print(type(array2[2])) for x in arrayX: pt = Point(x, arrayY[x]) pt.draw(win) win.getMouse() time.txt 1 1 1 1 1 1 7 100 22 value.txt 100 100 50 20 34 67 23 89 43
Drawing bounding rectangle around the tumor cv2
I am working on a project which predicts that the MRI has tumor or not, now the next step is to draw a bounding rectangle around the tumor. I was able to extract the tumor from the MRI, now I want to get the opposite corners of the rectangle to bound the tumor in original figure. EDIT: For some of the MRI images the I cannot separate the the tumor from MRI, calculated the threshold using OTSU method seperately but its not working properly. Thank you ! Computing threshold: path=r"ImageProc\Y54.jpg" img = cv.imread(path,0) blur = cv.GaussianBlur(img,(5,5),0) # find normalized_histogram, and its cumulative distribution function hist = cv.calcHist([blur],[0],None,[256],[0,256]) hist_norm = hist.ravel()/hist.sum() Q = hist_norm.cumsum() bins = np.arange(256) fn_min = np.inf thresh = -1 for i in range(1,256): p1,p2 = np.hsplit(hist_norm,[i]) # probabilities q1,q2 = Q[i],Q[255]-Q[i] # cum sum of classes if q1 < 1.e-6 or q2 < 1.e-6: continue b1,b2 = np.hsplit(bins,[i]) # weights # finding means and variances m1,m2 = np.sum(p1*b1)/q1, np.sum(p2*b2)/q2 v1,v2 = np.sum(((b1-m1)**2)*p1)/q1,np.sum(((b2-m2)**2)*p2)/q2 # calculates the minimization function fn = v1*q1 + v2*q2 if fn < fn_min: fn_min = fn thresh = i # find otsu's threshold value with OpenCV function ret, otsu = cv.threshold(blur,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU) print( "{} {}".format(thresh,ret) ) My progress so for through the code is: import cv2 import matplotlib.pyplot as plt def show_image(title, image): cv2.imshow(title, image) cv2.waitKey(0) cv2.destroyAllWindows() def show_image_plt(title, image, cmap = None): plt.figure(title) plt.imshow(image,cmap=cmap) plt.axis('off') plt.show() def cvt_image_colorspace(image, colorspace = cv2.COLOR_BGR2GRAY): return cv2.cvtColor(image, colorspace) def median_filtering(image, kernel_size=3): ''' :param image: grayscale image :param kernel_size: kernel size should be odd number :return: blurred image ''' return cv2.medianBlur(image, kernel_size) def apply_threshold(image, **kwargs): ''' :param image: image object :param kwargs: threshold parameters - dictionary :return: ''' threshold_method = kwargs['threshold_method'] max_value = kwargs['pixel_value'] threshold_flag = kwargs.get('threshold_flag', None) if threshold_flag is not None: ret, thresh1 = cv2.adaptiveThreshold(image, max_value, threshold_method,cv2.THRESH_BINARY, kwargs['block_size'], kwargs['const']) else: ret, thresh1 = cv2.threshold(image, kwargs['threshold'], max_value, threshold_method) return thresh1 def sobel_filter(img,x,y,kernel_size = 3): return cv2.Sobel(img, cv2.CV_8U, x,y, ksize=kernel_size) path=r"Imageproc\Y54.jpg" image = cv2.imread(path, 1) show_image('Original image', image) #Step one - grayscale the image grayscale_img = cvt_image_colorspace(image) #show_image('Grayscaled image', grayscale_img) #Step two - filter out image median_filtered = median_filtering(grayscale_img,5) #show_image('Median filtered', median_filtered) #testing threshold function bin_image = apply_threshold(median_filtered, **{"threshold" : 93, "pixel_value" : 255, "threshold_method" : cv2.THRESH_BINARY}) otsu_image = apply_threshold(median_filtered, **{"threshold" : 93, "pixel_value" : 255, "threshold_method" : cv2.THRESH_BINARY + cv2.THRESH_OTSU}) #Step 3a - apply Sobel filter img_sobelx = sobel_filter(median_filtered, 1, 0) img_sobely = sobel_filter(median_filtered, 0, 1) # Adding mask to the image img_sobel = img_sobelx + img_sobely+grayscale_img #show_image('Sobel filter applied', img_sobel) #Step 4 - apply threshold # Set threshold and maxValue threshold = 160 maxValue = 255 # Threshold the pixel values thresh = apply_threshold(img_sobel, **{"threshold" : 93, "pixel_value" : 255, "threshold_method" : cv2.THRESH_BINARY}) #show_image("Thresholded", thresh) #Step 3b - apply erosion + dilation #apply erosion and dilation to show only the part of the image having more intensity - tumor region #that we want to extract kernel=cv2.getStructuringElement(cv2.MORPH_RECT,(9,9)) erosion = cv2.morphologyEx(median_filtered, cv2.MORPH_ERODE, kernel) #show_image('Eroded image', erosion) dilation = cv2.morphologyEx(erosion, cv2.MORPH_DILATE, kernel) #show_image('Dilatated image', dilation) #Step 4 - apply thresholding threshold = 160 maxValue = 255 # apply thresholding new_thresholding = apply_threshold(dilation, **{"threshold" : 93, "pixel_value" : 255, "threshold_method" : cv2.THRESH_BINARY}) show_image('Threshold image after erosion + dilation', new_thresholding) The output image for given MRI is:
I think the best way is to know where pixels are not black pts = np.argwhere(new_thresholding>0) y1,x1 = pts.min(axis=0) y2,x2 = pts.max(axis=0) new_thresholding_rect= cv2.rectangle(new_thresholding,(x1,y1),(x2,y2),(255,0,0),2) show_image('Threshold image after erosion + dilation + Rectangle',new_thresholding_rect)
How to find the coordinates of a contour and crop it?
I'm trying to find out the contour with maximum area, get the coordinates of the rectangular area and crop out the area and display. ocean.jpg This is the code: import cv2 import numpy as np img = cv2.imread('ocean.jpg') gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) ret,thresh=cv2.threshold(gray,127,255,0) x,contours,hierarchy=cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) cnt = contours[0] cv2.drawContours(img,contours,-1,(0,255,0),1) cv2.imshow('Contours',img) a=[] for i in range (len(contours)): a.append(cv2.contourArea(contours[i])) z=(max(a)) print (contours[a.index(z)])
largestContourArea = 0 largestContour = 0 for cnt in contours: contourArea = cv2.contourArea(cnt) if( contourArea > largestContourArea): largestContour = cnt largestContourArea = contourArea # This finds the bounding rectangle # x,y are the co-ordinates of left-top point and w,h are width and height respectively x,y,w,h = cv2.boundingRect(largestContour) # This is simple slicing to get the "Region of Interest" ROI = img[y:y+h,x:x+w] cv2.namedWindow("Largest Contour",cv2.WINDOW_NORMAL) cv2.imshow("Largest Contour",ROI) cv2.waitKey(0)
How do I match text location to randomly placed text on my Canvas?
I have the function: StoreItems = random.sample(set(['sword','pickaxe','toothpick','hammer','torch','saw']), 5) #Selects 5 Random strings from the list. ^ XBASE, YBASE, DISTANCE = 300, 320, 50 for i, word in enumerate(StoreItems): canvas.create_text( (XBASE, YBASE + i * DISTANCE), text=word, activefill="Medium Turquoise", anchor=W, fill="White", font=('Anarchistic',40), tags=word) canvas.tag_bind('sword', '<ButtonPress-1>', BuySword) canvas.tag_bind('pickaxe', '<ButtonPress-1>', BuyPick) canvas.tag_bind('toothpick', '<ButtonPress-1>', BuyTooth) canvas.tag_bind('hammer', '<ButtonPress-1>', BuyHammer) canvas.tag_bind('torch', '<ButtonPress-1>', BuyTorch) canvas.tag_bind('saw', '<ButtonPress-1>', BuySaw) Which randomly selects from the list: StoreItems[] and puts 5 of them on my canvas in a random order. If I wanted my tag binds to create text next to them how would I do that? I have the event functions: def BuySword(event): if 'sword' in StoreItems: sword = canvas.create_text((420,350), text="test", fill="White", font=('Anarchistic',40)) But I want the location of this created text to follow the random placement of the corresponding word from my list.
you can use the bbox method to get the position of a tagOrId: Returns a tuple (x1, y1, x2, y2) describing a rectangle that encloses all the objects specified by tagOrId. If the argument is omitted, returns a rectangle enclosing all objects on the canvas. The top left corner of the rectangle is (x1, y1) and the bottom right corner is (x2, y2). although since you are doing relative position all you need is the first two: def BuySword(event): if 'sword' in StoreItems: x,y,_,_ = canvas.bbox("sword") relative_position = (x+420,y) sword = canvas.create_text(relative_position, text="test", fill="White", font=('Anarchistic',40), anchor=N+W) #the anchor is needed to line up with the north west corner of the original text