Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#790 Add Ops\Boxes #867

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/Native/LibTorchSharp/THSVision.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -358,4 +358,14 @@ void THSVision_RGB_BRGA(const uint8_t* inputBytes, uint8_t* outBytes, int64_t in
}
outBytes[outputAlpha + j] = inputHasAlpha ? inputBytes[inputBlue + i] : 255;
}
}

Tensor THSVision_nms(const Tensor dets, const Tensor scores, double iou_threshold)
{
typedef at::Tensor (*TorchVisionFunc)(at::Tensor&, at::Tensor&, double);
auto nms = (TorchVisionFunc)LoadNativeSymbol("libtorchvision.dll", "?nms@ops@vision@@YA?AVTensor@at@@AEBV34@0N@Z");
if (nms == NULL)
return NULL;

CATCH_TENSOR(nms(*dets, *scores, iou_threshold));
}
4 changes: 3 additions & 1 deletion src/Native/LibTorchSharp/THSVision.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@ EXPORT_API(void) THSVision_ComputeOutputSize(const float* matrix, const int64_t
EXPORT_API(void) THSVision_BRGA_RGB(const uint8_t* inputBytes, uint8_t* redBytes, uint8_t* greenBytes, uint8_t* blueBytes, int64_t inputChannelCount, int64_t imageSize);
EXPORT_API(void) THSVision_BRGA_RGBA(const uint8_t* inputBytes, uint8_t* redBytes, uint8_t* greenBytes, uint8_t* blueBytes, uint8_t* alphaBytes, int64_t inputChannelCount, int64_t imageSize);

EXPORT_API(void) THSVision_RGB_BRGA(const uint8_t* inputBytes, uint8_t* outBytes, int64_t inputChannelCount, int64_t imageSize);
EXPORT_API(void) THSVision_RGB_BRGA(const uint8_t* inputBytes, uint8_t* outBytes, int64_t inputChannelCount, int64_t imageSize);

EXPORT_API(Tensor) THSVision_nms(const Tensor dets, const Tensor scores, double iou_threshold);
81 changes: 68 additions & 13 deletions src/Native/LibTorchSharp/Utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,38 @@
#pragma once

#include <string>
#include<iostream>
#include<stdio.h>

#include "torch/torch.h"

extern thread_local char *torch_last_err;

typedef torch::Tensor *Tensor;
typedef torch::Scalar *Scalar;
#if _WIN32
#include <Windows.h>
#include <tchar.h>
#include <direct.h>
#define GetCurrentDir _getcwd
#else
#include <dlfcn.h>
#include <unistd.h>
#define GetCurrentDir getcwd
#endif

extern thread_local char* torch_last_err;

typedef torch::Tensor* Tensor;
typedef torch::Scalar* Scalar;
typedef torch::Generator* Generator;
typedef c10::Storage* Storage;
typedef torch::nn::utils::rnn::PackedSequence* PackedSequence;

typedef std::shared_ptr<torch::nn::Module> * NNModule;
typedef std::shared_ptr<torch::nn::AnyModule> * NNAnyModule;
typedef std::shared_ptr<torch::optim::Optimizer> * Optimizer;
typedef std::shared_ptr<torch::jit::CompilationUnit> * JITCompilationUnit;
typedef std::shared_ptr<torch::nn::Module>* NNModule;
typedef std::shared_ptr<torch::nn::AnyModule>* NNAnyModule;
typedef std::shared_ptr<torch::optim::Optimizer>* Optimizer;
typedef std::shared_ptr<torch::jit::CompilationUnit>* JITCompilationUnit;
typedef std::shared_ptr<torch::jit::Module>* JITModule;
typedef std::shared_ptr<torch::jit::Method>* JITMethod;
typedef std::shared_ptr<torch::jit::Function> * JITFunction;
typedef std::shared_ptr<c10::Type> * JITType;
typedef std::shared_ptr<torch::jit::Function>* JITFunction;
typedef std::shared_ptr<c10::Type>* JITType;
typedef std::shared_ptr<c10::TensorType>* JITTensorType;

//typedef std::shared_ptr<torch::jit::DimensionedTensorType>* JITDimensionedTensorType;
Expand Down Expand Up @@ -49,7 +62,7 @@ typedef std::shared_ptr<c10::TensorType>* JITTensorType;
#define CATCH_RETURN_Tensor(stmt) CATCH_RETURN_RES(Tensor, NULL, stmt)

// Return undefined tensors as NULL to C#
inline Tensor ResultTensor(const at::Tensor & res)
inline Tensor ResultTensor(const at::Tensor& res)
{
if (res.defined())
return new torch::Tensor(res);
Expand Down Expand Up @@ -82,11 +95,11 @@ inline Tensor ResultTensor(const at::Tensor & res)


// Utility method used to built sharable strings.
const char * make_sharable_string(const std::string str);
const char* make_sharable_string(const std::string str);

// Method concerting arrays of tensor pointers into arrays of tensors.
template<class T>
std::vector<T> toTensors(torch::Tensor ** tensorPtrs, const int length)
std::vector<T> toTensors(torch::Tensor** tensorPtrs, const int length)
{
std::vector<T> tensors;

Expand Down Expand Up @@ -296,4 +309,46 @@ torch::nn::init::NonlinearityType get_nl_type(const int64_t nl)
case 9: return torch::kReLU;
case 10: return torch::kLeakyReLU;
}
}

inline
void* LoadNativeSymbol(const std::string libName, const std::string symbolName)
{
void* lib = NULL;
#if _WIN32
#ifdef UNICODE
auto fullName = libName;
std::wstring widestr = std::wstring(fullName.begin(), fullName.end());
lib = LoadLibrary(widestr.c_str());
#else
lib = LoadLibrary(libName.c_str());
#endif // !UNICODE
if (lib == NULL)
{
char buff[FILENAME_MAX];
GetCurrentDir(buff, FILENAME_MAX);
std::string current_working_dir(buff);

torch_last_err = strdup(("Failed to load library: " + libName + " " +
std::to_string(GetLastError()) + " " + current_working_dir).c_str());
return NULL;
}
#else
lib = dlopen((libName + ".so").c_str(), RTLD_LAZY);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't work on Mac, I believe. There, it's '.dylib'

#endif

void* symbol = NULL;
#if _WIN32
symbol = (void*)GetProcAddress((HMODULE)lib, symbolName.c_str());
if (symbol == NULL)
{
torch_last_err = strdup(("Cannot find symbol: " + symbolName + " " +
std::to_string(GetLastError())).c_str());
return NULL;
}
#else
symbol = dlsym(lib, symbolName.c_str());
#endif

return symbol;
}
18 changes: 16 additions & 2 deletions src/TorchSharp/Tensor/Tensor.Math.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2754,7 +2754,14 @@ public static Tensor einsum(string equation, params Tensor[] tensors)
/// </summary>
/// <param name="input">The first input tensor</param>
/// <param name="other">The second input tensor</param>
public static Tensor maximum(Tensor input, Tensor other) => input.maximum(other);
public static Tensor max(Tensor input, Tensor other) => maximum(input, other);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is an overload at the bottom of the 'max‘ page

torch.max(input, other, *, out=None) → Tensor
See torch.maximum().


/// <summary>
/// Computes the element-wise maximum of input and other.
/// </summary>
/// <param name="input">The first input tensor</param>
/// <param name="other">The second input tensor</param>
static public Tensor maximum(Tensor input, Tensor other) => input.maximum(other);

/// <summary>
/// Returns a named tuple (values, indexes) where values is the maximum value of each row of the input tensor in the given dimension dim.
Expand Down Expand Up @@ -2797,7 +2804,14 @@ public static Tensor einsum(string equation, params Tensor[] tensors)
/// </summary>
/// <param name="input">The first input tensor</param>
/// <param name="other">The second input tensor</param>
public static Tensor minimum(Tensor input, Tensor other) => input.minimum(other);
public static Tensor min(Tensor input, Tensor other) => minimum(input, other);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment on 'max'


/// <summary>
/// Computes the element-wise minimum of input and other.
/// </summary>
/// <param name="input">The first input tensor</param>
/// <param name="other">The second input tensor</param>
static public Tensor minimum(Tensor input, Tensor other) => input.minimum(other);

/// <summary>
/// Returns a named tuple (values, indexes) where values is the minimum value of each row of the input tensor in the given dimension dim.
Expand Down
2 changes: 1 addition & 1 deletion src/TorchSharp/Tensor/Tensor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public partial class Tensor : IDisposable

internal DisposeScope? OwningDisposeScope { get; set; }

internal Tensor(IntPtr handle)
public Tensor(IntPtr handle)
{
this.handle = handle;
System.Threading.Interlocked.Increment(ref _totalCount);
Expand Down
8 changes: 8 additions & 0 deletions src/TorchSharp/Tensor/Tensor.torch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -678,5 +678,13 @@ public static Tensor _sample_dirichlet(Tensor input, torch.Generator generator =
/// </summary>
public static Tensor argsort(Tensor input, long dim = -1, bool descending = false) => input.argsort(dim, descending);

/// <summary>
/// Returns the unique elements of the input tensor.
/// </summary>
/// <returns></returns>
public static Tensor unique(Tensor input)
{
return input.unique().output;
}
}
}
31 changes: 31 additions & 0 deletions src/TorchSharp/Utils/DeconstructExtension.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TorchSharp.Utils
{
/// <summary>
/// Converts IEnumerable to tuple.
/// </summary>
public static class DeconstructExtension
{
public static void Deconstruct<T>(this IEnumerable<T> seq, out T first, out IEnumerable<T> rest)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doc comments on all public APIs, please.

{
first = seq.FirstOrDefault();
rest = seq.Skip(1);
}

public static void Deconstruct<T>(this IEnumerable<T> seq, out T first, out T second, out IEnumerable<T> rest)
=> (first, (second, rest)) = seq;

public static void Deconstruct<T>(this IEnumerable<T> seq, out T first, out T second, out T third, out IEnumerable<T> rest)
=> (first, second, (third, rest)) = seq;

public static void Deconstruct<T>(this IEnumerable<T> seq, out T first, out T second, out T third, out T fourth, out IEnumerable<T> rest)
=> (first, second, third, (fourth, rest)) = seq;

public static void Deconstruct<T>(this IEnumerable<T> seq, out T first, out T second, out T third, out T fourth, out T fifth, out IEnumerable<T> rest)
=> (first, second, third, fourth, (fifth, rest)) = seq;
}
}
2 changes: 1 addition & 1 deletion src/TorchVision/Ops.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public static Tensor sigmoid_focal_loss(Tensor inputs, Tensor targets, float alp
/// <param name="scores">Scores (Tensor[N]) for each one of the boxes.</param>
/// <param name="iou_threshold">Discards all overlapping boxes with IoU > iou_threshold.</param>
/// <returns>The indices (Tensor) of the elements that have been kept by NMS, sorted in decreasing order of scores.</returns>
public static Tensor nms(Tensor boxes, Tensor scores, double iou_threshold = 0.5)
public static Tensor nms_custom(Tensor boxes, Tensor scores, double iou_threshold = 0.5)
{
using (var _ = torch.NewDisposeScope()) {
var x1 = boxes.select(1, 0);
Expand Down
95 changes: 95 additions & 0 deletions src/TorchVision/Ops/BoxConvert.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. See LICENSE in the project root for license information.

// A number of implementation details in this file have been translated from the Python version of torchvision,
// largely located in the files found in this folder:
//
// https://github.com/pytorch/vision/blob/3d60f498e71ba63b428edb184c9ac38fa3737fa6/torchvision/ops/_box_convert.py
//
// The origin has the following copyright notice and license:
//
// https://github.com/pytorch/vision/blob/main/LICENSE
//

using System;
using static TorchSharp.torch;
using TorchSharp.Utils;

#nullable enable
namespace TorchSharp
{
public static partial class torchvision
{
public static partial class ops
{
/// <summary>
/// Converts bounding boxes from (cx, cy, w, h) format to (x1, y1, x2, y2) format.
/// (cx, cy) refers to center of bounding box
/// (w, h) are width and height of bounding box
/// </summary>
/// <param name="boxes">boxes (Tensor[N, 4]): boxes in (cx, cy, w, h) format which will be converted.</param>
/// <returns>boxes (Tensor(N, 4)): boxes in (x1, y1, x2, y2) format.</returns>
internal static Tensor _box_cxcywh_to_xyxy(Tensor boxes)
{
//# We need to change all 4 of them so some temporary variable is needed.
var (cx, cy, w, h, _) = boxes.unbind(-1);
var x1 = cx - 0.5 * w;
var y1 = cy - 0.5 * h;
var x2 = cx + 0.5 * w;
var y2 = cy + 0.5 * h;

boxes = torch.stack(new Tensor[] { x1, y1, x2, y2 }, dim: -1);
return boxes;
}

/// <summary>
/// Converts bounding boxes from (x1, y1, x2, y2) format to (cx, cy, w, h) format.
/// (x1, y1) refer to top left of bounding box
/// (x2, y2) refer to bottom right of bounding box
/// </summary>
/// <param name="boxes">boxes (Tensor[N, 4]): boxes in (x1, y1, x2, y2) format which will be converted.</param>
/// <returns>boxes (Tensor(N, 4)): boxes in (cx, cy, w, h) format.</returns>
internal static Tensor _box_xyxy_to_cxcywh(Tensor boxes)
{
var (x1, y1, x2, y2, _) = boxes.unbind(-1);
var cx = (x1 + x2) / 2;
var cy = (y1 + y2) / 2;
var w = x2 - x1;
var h = y2 - y1;

boxes = torch.stack(new Tensor[] { cx, cy, w, h }, dim: -1);

return boxes;
}

/// <summary>
/// Converts bounding boxes from (x, y, w, h) format to (x1, y1, x2, y2) format.
/// (x, y) refers to top left of bouding box.
/// (w, h) refers to width and height of box.
/// </summary>
/// <param name="boxes">boxes (Tensor[N, 4]): boxes in (x, y, w, h) which will be converted.</param>
/// <returns>boxes (Tensor[N, 4]): boxes in (x1, y1, x2, y2) format.</returns>
internal static Tensor _box_xywh_to_xyxy(Tensor boxes)
{
var (x, y, w, h, _) = boxes.unbind(-1);
boxes = torch.stack(new Tensor[] { x, y, x + w, y + h }, dim: -1);
return boxes;
}

/// <summary>
/// Converts bounding boxes from (x1, y1, x2, y2) format to (x, y, w, h) format.
/// (x1, y1) refer to top left of bounding box
/// (x2, y2) refer to bottom right of bounding box
/// </summary>
/// <param name="boxes">boxes (Tensor[N, 4]): boxes in (x1, y1, x2, y2) which will be converted.</param>
/// <returns>boxes (Tensor[N, 4]): boxes in (x, y, w, h) format.</returns>
internal static Tensor _box_xyxy_to_xywh(Tensor boxes)
{
var (x1, y1, x2, y2, _) = boxes.unbind(-1);
var w = x2 - x1;
var h = y2 - y1;
boxes = torch.stack(new Tensor[] { x1, y1, w, h }, dim: -1);
return boxes;
}
}
}
}
Loading