
    BVhA                        d Z ddlZddlZddlZddlZddlZej                  j                  e	      d   j                  ej                  j                  dd            sddlmZ nddlmZ dZdZd Zd	 Zd
 Z G d d      Z G d d      Z G d d      Zd ZddZd Zd Zd ZddZd Zedk(  r eej@                         yy)z}This tool creates an html visualization of a TensorFlow Lite graph.

Example usage:

python visualize.py foo.tflite foo.html
    Ntflite_runtime	visualize)schema_py_generateda  
<html>
<head>
<style>
body {font-family: sans-serif; background-color: #fa0;}
table {background-color: #eca;}
th {background-color: black; color: white;}
h1 {
  background-color: ffaa00;
  padding:5px;
  color: black;
}

svg {
  margin: 10px;
  border: 2px;
  border-style: solid;
  border-color: black;
  background: white;
}

div {
  border-radius: 5px;
  background-color: #fec;
  padding:5px;
  margin:5px;
}

.tooltip {color: blue;}
.tooltip .tooltipcontent  {
    visibility: hidden;
    color: black;
    background-color: yellow;
    padding: 5px;
    border-radius: 4px;
    position: absolute;
    z-index: 1;
}
.tooltip:hover .tooltipcontent {
    visibility: visible;
}

.edges line {
  stroke: #333;
}

text {
  font-weight: bold;
}

.nodes text {
  color: black;
  pointer-events: none;
  font-family: sans-serif;
  font-size: 11px;
}
</style>

<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
a  
  <script>
    function buildGraph() {
      // Build graph data
      var graph = %s;

      var svg = d3.select("#subgraph%d")
      var width = svg.attr("width");
      var height = svg.attr("height");
      // Make the graph scrollable.
      svg = svg.call(d3.zoom().on("zoom", function() {
        svg.attr("transform", d3.event.transform);
      })).append("g");


      var color = d3.scaleOrdinal(d3.schemeDark2);

      var simulation = d3.forceSimulation()
          .force("link", d3.forceLink().id(function(d) {return d.id;}))
          .force("charge", d3.forceManyBody())
          .force("center", d3.forceCenter(0.5 * width, 0.5 * height));

      var edge = svg.append("g").attr("class", "edges").selectAll("line")
        .data(graph.edges).enter().append("path").attr("stroke","black").attr("fill","none")

      // Make the node group
      var node = svg.selectAll(".nodes")
        .data(graph.nodes)
        .enter().append("g")
        .attr("x", function(d){return d.x})
        .attr("y", function(d){return d.y})
        .attr("transform", function(d) {
          return "translate( " + d.x + ", " + d.y + ")"
        })
        .attr("class", "nodes")
          .call(d3.drag()
              .on("start", function(d) {
                if(!d3.event.active) simulation.alphaTarget(1.0).restart();
                d.fx = d.x;d.fy = d.y;
              })
              .on("drag", function(d) {
                d.fx = d3.event.x; d.fy = d3.event.y;
              })
              .on("end", function(d) {
                if (!d3.event.active) simulation.alphaTarget(0);
                d.fx = d.fy = null;
              }));
      // Within the group, draw a box for the node position and text
      // on the side.

      var node_width = 150;
      var node_height = 30;

      node.append("rect")
          .attr("r", "5px")
          .attr("width", node_width)
          .attr("height", node_height)
          .attr("rx", function(d) { return d.group == 1 ? 1 : 10; })
          .attr("stroke", "#000000")
          .attr("fill", function(d) { return d.group == 1 ? "#dddddd" : "#000000"; })
      node.append("text")
          .text(function(d) { return d.name; })
          .attr("x", 5)
          .attr("y", 20)
          .attr("fill", function(d) { return d.group == 1 ? "#000000" : "#eeeeee"; })
      // Setup force parameters and update position callback


      var node = svg.selectAll(".nodes")
        .data(graph.nodes);

      // Bind the links
      var name_to_g = {}
      node.each(function(data, index, nodes) {
        console.log(data.id)
        name_to_g[data.id] = this;
      });

      function proc(w, t) {
        return parseInt(w.getAttribute(t));
      }
      edge.attr("d", function(d) {
        function lerp(t, a, b) {
          return (1.0-t) * a + t * b;
        }
        var x1 = proc(name_to_g[d.source],"x") + node_width /2;
        var y1 = proc(name_to_g[d.source],"y") + node_height;
        var x2 = proc(name_to_g[d.target],"x") + node_width /2;
        var y2 = proc(name_to_g[d.target],"y");
        var s = "M " + x1 + " " + y1
            + " C " + x1 + " " + lerp(.5, y1, y2)
            + " " + x2 + " " + lerp(.5, y1, y2)
            + " " + x2  + " " + y2
      return s;
    });

  }
  buildGraph()
</script>
c                 v    t         j                  j                  j                         D ]  \  }}|| k(  s|c S  y)z4Converts a numerical enum to a readable tensor type.N)	schema_fb
TensorType__dict__items)tensor_typenamevalues      O/home/dcms/DCMS/lib/python3.12/site-packages/tensorflow/lite/tools/visualize.pyTensorTypeToNamer      s<    ))2288: kdEk 
    c                 v    t         j                  j                  j                         D ]  \  }}|| k(  s|c S  y)z3Converts a builtin op code enum to a readable name.N)r   BuiltinOperatorr	   r
   )coder   r   s      r   BuiltinCodeToNamer      s;    ..77==? kdE}k 
r   c                 n    t        | t              r| S d}| | D ]  }|t        t        |            z   } |S )z;Converts a list of integers to the equivalent ASCII string. )
isinstancestrchrint)	name_listresultvals      r   NameListToStringr      sC    	3F (##c#h-'(Mr   c                       e Zd ZdZd Zd Zy)OpCodeMapperz#Maps an opcode index to an op name.c                     i | _         t        |d         D ]N  \  }}t        |d         | j                   |<   | j                   |   dk(  s4t        |d         | j                   |<   P y )Noperator_codesbuiltin_codeCUSTOMcustom_code)code_to_name	enumerater   r   )selfdataidxds       r   __init__zOpCodeMapper.__init__   sq    DD!123 DQ0>1BCd			3	8	+!1!M2B!C#Dr   c                 P    || j                   vrd}n| j                   |   }d||fz  S )Nz	<UNKNOWN>z%s (%d))r&   )r(   xss      r   __call__zOpCodeMapper.__call__   s4    !!!
a


A
a1vr   N__name__
__module____qualname____doc__r,   r0    r   r   r    r       s    +Dr   r    c                       e Zd ZdZd Zy)DataSizeMapperz(For buffers, report the number of bytes.c                 $    |dt        |      z  S y)Nz%d bytesz--)len)r(   r.   s     r   r0   zDataSizeMapper.__call__   s    }#a&  r   N)r2   r3   r4   r5   r0   r6   r   r   r8   r8      s
    0r   r8   c                       e Zd ZdZd Zd Zy)TensorMapperzGMaps a list of tensor indices to a tooltip hoverable indicator of more.c                     || _         y N)r)   )r(   subgraph_datas     r   r,   zTensorMapper.__init__  s	    DIr   c                 R   d}||S |dz  }|D ]~  }| j                   d   |   }|t        |      dz   z  }|t        |d         dz   z  }|t        |d         dz   z  }|d|v rt	        |d         ndz  }|d	|v rt	        |d	         ndd
z   z  } |dz  }|t	        |      z  }|dz  }|S )Nr   z3<span class='tooltip'><span class='tooltipcontent'>tensors r   typeshapez[]shape_signaturez<br>z</span>)r)   r   r   r   repr)r(   r.   htmlitensors        r   r0   zTensorMapper.__call__  s    DykAAD Byy#A&f
c!fsld
vf~.44d
vf~.44d
6(9tF7O$tDd
#v- F,-.376B BdB 	IDDGODIDKr   Nr1   r6   r   r   r<   r<     s    Or   r<   c           
      
   d }d }g }g }i }i }d}	d}
t        |d   xs g       D ]  \  }}|d   Pt        |d         D ]?  \  }}||vr|dz
  dz   |	z  |dz   |
z  f||<   |j                   ||       ||      d	       A |d
   Pt        |d
         D ]?  \  }}||vr|dz   dz   |	z  |dz   |
z  f||<   |j                   ||       ||      d       A |j                   ||       ||d         d|	|dz   |	z  d        t        |d         D ]L  \  }}||v r||   n
||v r||   nd}|j                   ||      dt        |dg       |fz  d|d   |d   d       N t        j                  ||d      }t
        || fz  }|S )zAProduces the HTML required to have a d3 visualization of the dag.c                     d| z  S )Nzt%dr6   r*   s    r   
TensorNamez!GenerateGraph.<locals>.TensorName"      3;r   c                     d| z  S )Nzo%dr6   rL   s    r   OpNamezGenerateGraph.<locals>.OpName%  rN   r         	operatorsinputsg      ?   )sourcetargetoutputs)rW   rV   opcode_index   )idr   groupr.   yrA   )r   r   z%r (%d)rD   r   )nodesedges)r'   appendgetattrjsondumps_D3_HTML_TEMPLATE)subgraph_idxgopcode_mapperrM   rP   r_   r^   firstsecond
pixel_mult
width_multop_indexoptensor_input_positiontensor_indextensor_output_positionrI   	initial_y	graph_strrG   s                       r   GenerateGraphrs     s0    %
%
%&**+ 4"5 lh	(|1:2h<1H 
-
u$"*S.1"4
!B"7!";z!I!K%
 .X&
 		 
)} 2;ByM2J 
.
 ,v%#+c>A#5"C#9A#="K"M&
 .X&
 		 
LLXb01lj( )6 ()5 lF+u4l , 6|F  
LL&WVWb9<HHq\q\  jj559:)	i6	6$	+r   c                    d}|dz  }|dz  }|r|dz  }|D ]  \  }}|d|z  z  } |dz  }t        |       D ]B  \  }}|dz  }|r|d|z  z  }|D ]$  \  }}||v r||   nd}||n ||      }|d	|z  z  }& |dz  }D |d
z  }|S )a  Given a list of object values and keys to print, make an HTML table.

  Args:
    items: Items to print an array of dicts.
    keys_to_print: (key, display_fn). `key` is a key in the object. i.e.
      items[0][key] should exist. display_fn is the mapping function on display.
      i.e. the displayed html cell will have the string returned by
      `mapping_fn(items[0][key])`.
    display_index: add a column which is the index of each row in `items`.

  Returns:
    An html table.
  r   z<table><tr>
z<tr>
z<th>index</th>z<th>%s</th>z</tr>
z<td>%d</td>Nz<td>%s</td>
	</table>
)r'   )	r
   keys_to_printdisplay_indexrG   hmapperr*   rI   r   s	            r   GenerateTableHtmlrz   [  s     
$/$($D  iaMAD)$u% 
kc6HD
mc!!d" $	6fF1I$c>Cvc{c
o##d$
 	ID
 ,$	+r   c                 z    t        j                  dd|       }t        j                  dd|      j                         S )z2Converts an identifier in CamelCase to snake_case.z(.)([A-Z][a-z]+)z\1_\2z([a-z0-9])([A-Z]))resublower)camel_case_inputs1s     r   CamelCaseToSnakeCaser     s3    	vv (,<="	#Xr	2	8	8	::r   c                    t        | t              s t        | t              st        | t              r| S t	        | d      r]i }t        |       D ]K  }| j                  |      }t        |      r |d   dk7  s)t        |      }|dk(  rdn|}t        ||      ||<   M |S t        | t        j                        r|r| S | j                         S t	        | d      r| D cg c]  }t        ||       c}S | S c c}w )a  Converts a hierarchy of FB objects into a nested dict.

  We avoid transforming big parts of the flat buffer into python arrays. This
  speeds conversion from ten minutes to a few seconds on big graphs.

  Args:
    fb: a flat buffer structure. (i.e. ModelT)
    preserve_as_numpy: true if all downstream np.arrays should be preserved.
      false if all downstream np.array should become python arrays
  Returns:
    A dictionary representing the flatbuffer rather than a flatbuffer object.
  r	   r   _buffersT__len__)r   r   floatr   hasattrdir__getattribute__callabler   FlatbufferToDictnpndarraytolist)fbpreserve_as_numpyr   attribute_name	attribute
snake_namepreserveentrys           r   r   r     s     CJr51ZC5HIr:Fb' C%%n5ii ^A%6#%=).9
)Y64<M-iBzC M"bjj!"23		3r9DFG5U$56GGI Hs   C2c                     t         j                  j                  | d      }t         j                  j	                  |      }t        |d      S )Nr   F)r   )r   ModelGetRootAsModelModelTInitFromObjr   )buffer_data	model_objmodels      r   CreateDictFromFlatbufferr     s;    oo,,[!<)



&
&y
1%	%5	99r   c           
         |rt         j                  j                  |       st        d| z        | j	                  d      s| j	                  d      r:t        | d      5 }t        |j                               }ddd       t              }nF| j	                  d      rt        j                  t        |             }nt        d      t        |       }d}|t        z  }|d	z  }|r| nd
|d<   g d}|dz  }|D ]+  \  }}|sd }|d|d ||j                  |            dz  }- |dz  }dt               fg}	dt        fdt        fdg}
|d   D ]  }t!        |d   |d         |d<    t#        |d         D ]  \  }}|dz  }t%        |      }t'        |      }d|fd|fdd|fg}dt        fd t(        fd!d"d#d$g}|d%|z  z  }|d&z  }|t+        |d   |d   d'gd|fd|fgd()      z  }|d*z  }|t+        |d+   |      z  }|d,   r|d-z  }|t+        |d,   |      z  }|d.|fz  z  }|t-        |||      z  }|d/z  } |d0z  }|t+        |d1   |	      z  }|d2z  }|t+        |d   |
      z  }|d3z  }|S # 1 sw Y   xY w)4aH  Returns html description with the given tflite model.

  Args:
    tflite_input: TFLite flatbuffer model path or model object.
    input_is_filepath: Tells if tflite_input is a model path or a model object.

  Returns:
    Dump of the given tflite model in HTML format.

  Raises:
    RuntimeError: If the input is not valid.
  zInvalid filename %rz.tflitez.binrbNz.jsonz#Input file was not .tflite or .jsonr   z<h1>TensorFlow Lite Model</h2>zNull (used model object)filename))r   NversionN)descriptionNz<table>
c                     | S r>   r6   )r.   s    r   <lambda>zcreate_html.<locals>.<lambda>  s    ! r   z<tr><th>z	</th><td>z</td></tr>
ru   r)   r#   r%   r   r"   deprecated_builtin_code	subgraphsz<div class='subgraph'>rT   rX   )builtin_optionsNrY   r   rC   )rD   N)rE   N)bufferN)quantizationNz<h2>Subgraph %d</h2>
z<h3>Inputs/Outputs</h3>
)rT   rX   F)rw   z<h3>Tensors</h3>
rA   rS   z<h3>Ops</h3>
z6<svg id='subgraph%d' width='1600' height='900'></svg>
z</div>z<h2>Buffers</h2>
r   z<h2>Operator Codes</h2>
z</body></html>
)ospathexistsRuntimeErrorendswithopen	bytearrayreadr   rb   load_CSSgetr8   r   r   maxr'   r<   r    r   rz   rs   )tflite_inputinput_is_filepathfile_handle	file_datar)   rG   toplevel_stuffkeymappingbuffer_keys_to_displayoperator_keys_to_displayr+   re   rf   tensor_mapperrg   op_keys_to_displaytensor_keys_to_displays                     r   create_htmlr     sl     77>>,'.=>>Y'<+@+@+Hd# 2{k..01	2%i0d			w	'YYtL)*d>??#L1D	$$,$
**$%6\  z+. +$$ Plc7g38NOODP ,$ $^%567-/@A,.>?/1
  ! MaAn-q1J/KLAnM #4#45 $olA$$D OM &M#]3i5O3)=9;  &'78%'78/79I46
 	$|33D 	''DH+Y<   M	"Y$>?,1	3 3D 	  Da	l,BCCD 	~
d
+0BCCd 	EI  DM,=99DHDI$N 
$
DO-C
DD$ 
%%$
D!124L
MM$
$	+i2 2s   I++I5c                     	 | d   }| d   }t        |      }t        |d      5 }|j                  |       d d d        y # 1 sw Y   y xY w# t        $ r t	        d| d   z         Y y w xY w)NrU   rZ   wz&Usage: %s <input tflite> <output html>r   )r   r   write
IndexErrorprint)argvr   html_outputrG   output_files        r   mainr     sx    7Lq'K |$D	k3	 ;  	 
 @	
2d1g
>?@s   
A
 >A
A'&A'__main__)T)!r5   rb   r   r|   sysnumpyr   r   splitext__file__r   jointensorflow.lite.pythonr   r   r   r   rd   r   r   r   r    r8   r<   rs   rz   r   r   r   r   r   r2   r   r6   r   r   <module>r      s      	 	 
  
ww!!$--GGLL!;/1 F >>@c L	 $  49x#L;@:hV	 zsxx. r   