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

Resources