Exemple #1
0
def test_merge():
    """
    Functional test of merge().
    """
    # Subgraph 1
    sg1 = empty_graph("Graph 1")
    n1 = node('Add', inputs=['x1', 'x2'], outputs=['sum'])
    sg1 = add_node(sg1, n1)
    sg1 = add_input(sg1, 'x1', "FLOAT", [1])
    sg1 = add_input(sg1, 'x2', "FLOAT", [1])
    sg1 = add_output(sg1, 'sum', "FLOAT", [1])

    # Subgraph 2
    sg2 = empty_graph("Graph 2")
    sg2 = add_constant(sg2, "const", np.array([7]), "FLOAT")
    n2 = node("Equal", inputs=['sum', 'const'], outputs=['equal'])
    sg2 = add_node(sg2, n2)

    sg2 = add_input(sg2, 'sum', "FLOAT", [1])
    sg2 = add_output(sg2, 'equal', "BOOL", [1])

    g = merge(sg1, sg2, outputs=["sum"], inputs=["sum"])

    data = {
        "x1": np.array([2]).astype(np.float32),
        "x2": np.array([5]).astype(np.float32)
    }
    result = run(g, inputs=data, outputs=["equal"])
    assert result[
        0], "Sum of 2 and 5 should be equal to constant 7. Merged failed."
Exemple #2
0
def test_concat():
    """
    Functional test for concat
    """
    g1 = empty_graph("G1")
    n1 = node('Add',
              inputs=['x1_1', 'x1_2'],
              outputs=['sum_1'],
              name="node_name")
    g1 = add_input(g1, 'x1_1', "FLOAT", [1])
    g1 = add_input(g1, 'x1_2', "FLOAT", [1])
    g1 = add_output(g1, 'sum_1', "FLOAT", [1])
    g1 = add_node(g1, n1)

    g2 = empty_graph("G2")
    n2 = node('Add',
              inputs=['x2_1', 'x2_2'],
              outputs=['sum_2'],
              name="node_name")
    g2 = add_input(g2, 'x2_2', "FLOAT", [1])
    g2 = add_output(g2, 'sum_2', "FLOAT", [1])
    g2 = add_node(g2, n2)

    g = concat(g1, g2, False, True, edge_match=[("x1_2", "x2_1")])

    data = {
        "x1_1": np.array([2]).astype(np.float32),
        "x1_2": np.array([5]).astype(np.float32),
        "x2_2": np.array([5]).astype(np.float32)
    }
    result = run(g, inputs=data, outputs=["sum_1", "sum_2"])
    assert result[
        0], "Sum of 2 and 5 should be equal to constant 7. Concat failed."
Exemple #3
0
def test_check():

    # Invalid, no input/output:
    g = empty_graph()
    n1 = node('Add', inputs=['x1', 'x2'], outputs=['sum'])
    g = add_node(g, n1)
    assert not check(g), "Graph is not complete."

    # Valid graph
    g = add_input(g, 'x1', "FLOAT", [1])
    g = add_input(g, 'x2', "FLOAT", [1])
    g = add_output(g, 'sum', "FLOAT", [1])
    assert check(g), "Graph should pass checks."

    # Invalid: None operator:
    g = empty_graph()
    n1 = node('None', inputs=['x1', 'x2'], outputs=['sum'])
    g = add_node(g, n1)
    g = add_input(g, 'x1', "FLOAT", [1])
    g = add_input(g, 'x2', "FLOAT", [1])
    g = add_output(g, 'sum', "FLOAT", [1])
    assert not check(g), "Graph should not pass checks."

    # Invalid: Dynamic size input
    g = empty_graph()
    n1 = node('Add', inputs=['x1', 'x2'], outputs=['sum'])
    g = add_node(g, n1)
    g = add_input(g, 'x1', "FLOAT", [])
    g = add_input(g, 'x2', "FLOAT", [1])
    g = add_output(g, 'sum', "FLOAT", [1])
    assert not check(g), "Graph should not pass checks."

    check(g, _sclbl_check=False, _onnx_check=False)
    check(g, _onnx_check=False)  # Operator check.
Exemple #4
0
def test__parse_element():
    g = empty_graph()
    dims = [4, 3, 7]
    g = add_output(g, 'sum', "FLOAT", dims)
    for elem in g.output:
        _, elem_type, _ = _parse_element(elem)
    assert elem_type == "FLOAT", "Element not properly parsed."
Exemple #5
0
def test_clean():
    g = empty_graph()
    n1 = node('Add', inputs=['x1', 'x2'], outputs=['sum'])
    g = add_node(g, n1)
    g = add_input(g, 'x1', "FLOAT", [1])
    g = add_input(g, 'x2', "FLOAT", [1])
    g = add_output(g, 'sum', "FLOAT", [1])
    g = clean(g)
    assert type(g) == xpb2.GraphProto, "Clean failed."
Exemple #6
0
def test_replace_output():
    g = empty_graph()
    g = add_output(g, "test", "FLOAT", [0])
    g = replace_output(g, "test", "FLOAT", [10,10])

    type = getattr(g.output[0], "type", False)  # get the output type
    ttype = getattr(type, "tensor_type", False)
    shape = getattr(ttype, "shape", False)
    dim = getattr(shape, "dim", False)
    dim_val = getattr(dim[0], "dim_value", False)

    assert dim_val == 10, "New dimension should be 10"
Exemple #7
0
def test_split():
    """
    Functional test for split
    """
    g1 = empty_graph("G1")
    n1 = node('Add', inputs=['x1_1', 'x1_2'], outputs=['sum_1'], name="n1")
    g1 = add_input(g1, 'x1_1', "FLOAT", [1])
    g1 = add_input(g1, 'x1_2', "FLOAT", [1])
    g1 = add_output(g1, 'sum_1', "FLOAT", [1])
    g1 = add_node(g1, n1)

    g2 = empty_graph("G2")
    n2 = node('Add', inputs=['x2_1', 'x2_2'], outputs=['sum_2'], name="n2")
    g2 = add_input(g2, 'x2_1', "FLOAT", [1])
    g2 = add_input(g2, 'x2_2', "FLOAT", [1])
    g2 = add_output(g2, 'sum_2', "FLOAT", [1])
    g2 = add_node(g2, n2)

    g3 = empty_graph("G3")
    n3 = node('Add', inputs=['x3_1', 'x3_2'], outputs=['sum_3'], name="n3")
    g3 = add_input(g3, 'x3_1', "FLOAT", [1])
    g3 = add_input(g3, 'x3_2', "FLOAT", [1])
    g3 = add_output(g3, 'sum_3', "FLOAT", [1])
    g3 = add_node(g3, n3)

    g = split(g1,
              g2,
              g3,
              cg1_match=[("sum_1", "x2_2")],
              cg2_match=[("sum_1", "x3_1")])

    data = {
        "x1_1": np.array([1]).astype(np.float32),
        "x1_2": np.array([2]).astype(np.float32),
        "x2_1": np.array([3]).astype(np.float32),
        "x3_2": np.array([4]).astype(np.float32),
    }
    result = run(g, inputs=data, outputs=["sum_2", "sum_3"])
    assert result[
        0], "Sum of 1,2, and 3 should be equal to constant 6. Split failed."
Exemple #8
0
def test_add_constant():

    # Simple add graph
    g = empty_graph()
    n1 = node('Add', inputs=['x1', 'x2'], outputs=['sum'])
    g = add_node(g, n1)

    # Add input and constant
    g = add_constant(g, 'x1', np.array([1]), "INT64")
    g = add_constant(g, 'x2', np.array([5]), "INT64")

    # Output:
    g = add_output(g, 'sum', "INT64", [1])

    # This works, but seems to fail for other data types...
    result = run(g, inputs={}, outputs=["sum"])
    assert result[0] == 6, "Add constant failed."
    # todo(McK): Does not work for INT16 / INT8, check?
Exemple #9
0
# Run the model to see the outputs:
result = so.run(g, inputs={"input": img_input}, outputs=["output"])
print(result)

# Add and arg_max node to find the highest output in the output vector
# Note the keepdims and axis; the output of the Argmax node should align with the defined output.
n1 = so.node('ArgMax',
             inputs=['output'],
             outputs=['argmax'],
             keepdims=0,
             axis=1)
g = so.add_node(
    g, n1)  # Note, this adds the node, but the output is still "output"
g = so.delete_output(g, "output")  # Remove the old output
g = so.add_output(g, 'argmax', "INT64",
                  [1])  # Add the new output (for testing only)

# Test:
result = so.run(g, inputs={"input": img_input}, outputs=["argmax"])
print(result)

# So, this works. Let's remove the output argmax again before we continue:
g = so.delete_output(g, 'argmax')

# Because the if statement to switch between values of 100 and 0 requires a boolean input condition, we add
# a constant node with the value of 7, and add an equals node:
g = so.add_constant(g, "cut", np.array([7]), "INT64")
n2 = so.node("Equal", inputs=['argmax', 'cut'], outputs=['seven'])
g = so.add_node(g, n2)

# Lets again test:
Exemple #10
0
# Add a node to the graph.
# Please note the list of available operators at: https://github.com/onnx/onnx/blob/master/docs/Operators.md
# Run so.list_data_types() and / or so.list_operators() so. to see all Scailable supported data types.
n1 = so.node('Add', inputs=['x1', 'x2'], outputs=['sum'])
g = so.add_node(g, n1)

# We should explicitly specify the named inputs to the graph -- note that the names determine the graph topology.
# Also, we should specify the data type and dimensions of any input.
# Use so.list_data_types() to see available data types.
g = so.add_input(g, 'x1', "FLOAT", [1])
g = so.add_input(g, 'x2', "FLOAT", [1])

# Similarly, we add the named output with its corresponding type and dimension.
# Note that types will need to "match", as do dimensions. Please see the operator docs for more info.
g = so.add_output(g, 'sum', "FLOAT", [1])

# so.check() checks the current graph to see if it matches Scailable's upload criteria for .wasm conversion.
so.check(g)

# Now, a few tricks to sanitize the graph which are always useful.
# so.clean() provides lossless reduction of the graph. If successful cleaned graph is returned.
g = so.clean(g)

# so.display() tries to open the graph using Netron to inspect it. This worsk on most systems if Netron is installed.
# Get Netron at https://github.com/onnx/onnx/blob/master/docs/Operators.md
so.display(g)

# Now, use the default ONNX runtime to do a test run of the graph.
# Note that the inputs dimensions and types need to match the specification of the graph.
# The outputs returns all the outputs named in the list.
Exemple #11
0
print(large_img.shape)  # 450x600x3

# First subgraph for resize:
sg1 = so.empty_graph("resize_graph")
sg1 = so.add_input(sg1, "large_image", "INT32", [450, 600, 3])  # Add the input

# The resize node:
e1 = so.constant("roi", np.array([]),
                 "FLOAT")  # Note the empty fields for roi and scales.
e2 = so.constant("scales", np.array([]), "FLOAT")
c1 = so.constant("size", np.array([300, 400, 3]), "INT64")
n1 = so.node("Resize",
             inputs=['large_image', 'roi', 'scales', 'size'],
             outputs=['small_image'])
sg1 = so.add_nodes(sg1, [e1, e2, c1, n1])
sg1 = so.add_output(sg1, "small_image", "INT32", [300, 400, 3])

# Check and clean
sg1 = so.clean(sg1)
so.check(sg1)

# Test the resize graph:
large_input = {"large_image": large_img.astype(np.int32)}
result = so.run(sg1, inputs=large_input, outputs=['small_image'])

# Round values in array and cast as 8-bit integer to store back as JPG:
img_arr = np.array(np.round(result[0]), dtype=np.uint8)
out = Image.fromarray(img_arr, mode="RGB")
out.save("images/1-Resized.JPG")  # Yes, this works.

# Store the resize onnx:
Exemple #12
0
             keepdims=0)  # Note the keepdims additional parameter.
g = so.add_nodes(g, [n1, n2, n3])

# And, we need to add the threshold (constant c2):
threshold = np.array([3000000]).astype(np.int32)
g = so.add_constant(g, "c2", threshold, "INT32")

# Add the less node. Please note that the nodes have to be added in topological order:
n4 = so.node("Less", inputs=['sum', 'c2'], outputs=['result'])
g = so.add_node(g, n4)

# Check provides an error stating that no outputs have been specified (which is true at this point)
so.check(g)

# Add output:
g = so.add_output(g, "result", "BOOL", [1])

# After which is passes all the checks
so.check(g)

# Let's inspect:
so.display(g)

# Let's clean:
g = so.clean(g)

# Let's try it out for the first image:
img_data = np.array(Image.open("images/1.JPG"), dtype=np.int32)
example = {"in": img_data.astype(np.int32)}
result = so.run(g, inputs=example, outputs=['result'])
Exemple #13
0
def test__output_details():
    g = empty_graph()
    g = add_output(g, 'sum', "FLOAT", [4, 3, 7])
    out_det = _output_details(g)
    assert out_det['sum'][
        'data_type'] == "FLOAT", "Output details not correct."
Exemple #14
0
def test_delete_output():
    g = empty_graph()
    g = add_output(g, "test", "FLOAT", [0])
    g = delete_output(g, "test")
    assert len(g.output) == 0, "There should not be any outputs after delete."
Exemple #15
0
def test_list_outputs():
    g = empty_graph()
    assert list_outputs(g), "No outputs listed."
    g = add_output(g, "test", "FLOAT", [0])
    list_outputs(g)
    assert not list_outputs(False), "List outputs should be false."
Exemple #16
0
resulting graph is "complete" (i.e., it is a valid onnx graph including input and output). Concat itself is more
flexible and can be used for intermediate merging/concatenation of partial graphs (i.e., graphs that are not yet
finished).

Below we provide a number of examples of each of the functions. We recommend using so.display() throughout to visualize
the resulting graphs and truly understand how the graphs are joined together. Examples are all very simple (small graphs,
scalar operations, etc.), but don't underestimate the complexities involved; with larger graphs the behavior of
the concat function can be challenging.
"""

# # Lets start by creating a few simple (and complete) graphs which we will use throughout:
# # Simple absolute value graph:
g1 = so.empty_graph("g_1")
n1 = so.node('Abs', inputs=['in_1_1'], outputs=['out_1_1'], name="node_1_1")
g1 = so.add_input(g1, 'in_1_1', "FLOAT", [1])
g1 = so.add_output(g1, 'out_1_1', "FLOAT", [1])
g1 = so.add_node(g1, n1)
# so.display(g1)
# data = {"in_1_1": np.array([2]).astype(np.float32)}
# print(so.run(g1, inputs=data, outputs=["out_1_1"]))

# # Simple max value graph:
g2 = so.empty_graph("g_2")
n2 = so.node('Max',
             inputs=['in_2_1', 'in_2_2'],
             outputs=['out_2_1'],
             name="node_2_1")
g2 = so.add_input(g2, 'in_2_1', "FLOAT", [1])
g2 = so.add_constant(g2, "in_2_2", np.array([10]), "FLOAT")
g2 = so.add_output(g2, 'out_2_1', "FLOAT", [1])
g2 = so.add_node(g2, n2)
Exemple #17
0
def test_rename_output():
    g = empty_graph()
    g = add_output(g, "test", "FLOAT", [0])
    g = rename_output(g, "test", "new_name")
    name = getattr(g.output[0], "name", False)  # get the first output name:
    assert name == "new_name", "New name should be in list of outputs."
Exemple #18
0
def test_add_output():
    g = empty_graph()
    g = add_output(g, "test", "FLOAT", [0])
    name = getattr(g.output[0], "name", False)  # get the first output name:
    assert name == "test", "'test' should be in list of outputs."