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

How to convert Matft array to OpenCV mat array #53

Open
Eumentis-Jayashree opened this issue May 9, 2024 · 25 comments
Open

How to convert Matft array to OpenCV mat array #53

Eumentis-Jayashree opened this issue May 9, 2024 · 25 comments

Comments

@Eumentis-Jayashree
Copy link

I have created Matft array from float array let matftArray = MfArray(floatArrayValues) now I want to convert this array to OpenCV mat array so that I can use this array for processing.

@jjjkkkjjj
Copy link
Owner

@Eumentis-Jayashree
According to OpenCV's code, how about like this? (The below is psuedo code.)

void  MfarrayToMat(const MfArray array, cv::Mat& m) {
    // you should assert array is row contiguous
    Int cols = array.shape[0], rows = array.shape[1], channel = array.shape[2];

    if (channel == 1)
    {
        m.create(rows, cols, CV_32FC1);
        memcpy(array.mfdata.data_real, m.data, array.storedSize);
    }
    else if (channel == 3)
    {
        m.create(rows, cols, CV_32FC4); 
        memcpy(array.mfdata.data_real, m.data, array.storedSize);
    }
    else
    {
        m.create(rows, cols, CV_32FC4);
        memcpy(array.mfdata.data_real, m.data, array.storedSize);
    }
}

@Eumentis-Jayashree
Copy link
Author

Hey @jjjkkkjjj thank you for the response.

Actually, I am getting the result by the ONNX model. So the output I get is in ORTValue form, which has this shape [1, 359, 8400].

  1. I converted that bytedata output to the float array
    let floatValuesForOutput = rawOutputData.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) -> [Float] in let floatBuffer = buffer.bindMemory(to: Float.self) let count = rawOutputData.count / MemoryLayout<Float>.stride return Array(UnsafeBufferPointer(start: floatBuffer.baseAddress, count: count)) }

  2. After that I have converted floatValues to Matft array
    let arr = MfArray(floatValuesForOutput)

So, according to your solution, if I print the shape of the arr, then I only get Shape: [3015600], which is equal to 359 * 8400.

@jjjkkkjjj
Copy link
Owner

@Eumentis-Jayashree

The reason of the shape: [3015600] is you pass the "1d" array to MfArray.
Namely, floatValuesForOutput is 1d array.

So, when you want MfArray with shape: [1, 359, 8400], you should reshape it by;

let arr = MfArray(floatValuesForOutput, shape: [1, -1, 8400])

Caution:
The conversion method you mentioned is asserted that rawOutputData is row contiguous. If you pass the NOT row contiguous rawOutputData, the MfArray will be different from it.

To prevent this, you should manage not only data but also shape and strides. Does ORTValue have strides property?

@jjjkkkjjj
Copy link
Owner

Oh, I'm sorry, the above code is invalid due to not supporting negative dimension in init...
Instead of init, you can do it like this;

let arr = MfArray(floatValuesForOutput).reshape([1, -1, 8400])

@Eumentis-Jayashree
Copy link
Author

@Eumentis-Jayashree

The reason of the shape: [3015600] is you pass the "1d" array to MfArray. Namely, floatValuesForOutput is 1d array.

So, when you want MfArray with shape: [1, 359, 8400], you should reshape it by;

let arr = MfArray(floatValuesForOutput, shape: [1, -1, 8400])

Caution: The conversion method you mentioned is asserted that rawOutputData is row contiguous. If you pass the NOT row contiguous rawOutputData, the MfArray will be different from it.

To prevent this, you should manage not only data but also shape and strides. Does ORTValue have strides property?

No, ORTValue doesn't have strides property. It only has tensorData(), typeInfo(), tensorTypeAndShapeInfo() and tensorStringData() properties.

@jjjkkkjjj
Copy link
Owner

I see, then you can get same data by above code.
The ORTValue may preserve the row contiguous.

@jjjkkkjjj
Copy link
Owner

@Eumentis-Jayashree
Did you solve it?
And also, I'm interested in the actual code converting MfArray into cv::Mat. If possible, could you share it to me? (I'd like to integrate it into main branch)

@Eumentis-Jayashree
Copy link
Author

@Eumentis-Jayashree Did you solve it? And also, I'm interested in the actual code converting MfArray into cv::Mat. If possible, could you share it to me? (I'd like to integrate it into main branch)

Yeah, I solved it. But I am not sure if it's right approach. I have converted the Matft array to a Swift array and then converted it into an OpenCV mat array.

@Eumentis-Jayashree
Copy link
Author

Hey @jjjkkkjjj thank you for the response.

Actually, I am getting the result by the ONNX model. So the output I get is in ORTValue form, which has this shape [1, 359, 8400].

  1. I converted that bytedata output to the float array
    let floatValuesForOutput = rawOutputData.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) -> [Float] in let floatBuffer = buffer.bindMemory(to: Float.self) let count = rawOutputData.count / MemoryLayout<Float>.stride return Array(UnsafeBufferPointer(start: floatBuffer.baseAddress, count: count)) }
  2. After that I have converted floatValues to Matft array
    let arr = MfArray(floatValuesForOutput)

So, according to your solution, if I print the shape of the arr, then I only get Shape: [3015600], which is equal to 359 * 8400.

Here I get rawOutputData from ORTValue using .tensorData() property which data type is Data. Can I directly convert this rawOutputData into Matft array? so that I don't need above conversion.

@Eumentis-Jayashree
Copy link
Author

@jjjkkkjjj The output which I get is in shape of [1, 359, 8400]. Here 359 are rows and 8400 are columns. There is no channel like image.

@jjjkkkjjj
Copy link
Owner

Yeah, I solved it. But I am not sure if it's right approach. I have converted the Matft array to a Swift array and then converted it into an OpenCV mat array.

OK, but it is not efficient due to coping the data twice as you mentioned.

get rawOutputData from ORTValue using .tensorData() property which data type is Data. Can I directly convert this rawOutputData into Matft array?

That's why we should use same data or copy it once. Fortunately, I implemented borrowing data system by this;

let shape = // get from ORTValue
let size = // calculate the size from product all dimension = 1*359*8400
let floatValuesForOutput = rawOutputData.withUnsafeBytes { 
    (buffer: UnsafeRawBufferPointer) -> MfArray in 
    // copy data automatically
    let mfdata = MfData(source: nil, data_real_ptr: buffer.baseAddress!, data_imag_ptr: nil, storedSize: size, mftype: .Float, offset: 0)
    let mfstructure = MfStructure(shape: shape)
    return MfArray(mfdata: mfdata, mfstructure: mfstructure)
}

If the ORTValue conform to MfDataBasable, you can share data without copying, so that it is very efficient!

@Eumentis-Jayashree
Copy link
Author

Yeah, I solved it. But I am not sure if it's right approach. I have converted the Matft array to a Swift array and then converted it into an OpenCV mat array.

OK, but it is not efficient due to coping the data twice as you mentioned.

get rawOutputData from ORTValue using .tensorData() property which data type is Data. Can I directly convert this rawOutputData into Matft array?

That's why we should use same data or copy it once. Fortunately, I implemented borrowing data system by this;

let shape = // get from ORTValue
let size = // calculate the size from product all dimension = 1*359*8400
let floatValuesForOutput = rawOutputData.withUnsafeBytes { 
    (buffer: UnsafeRawBufferPointer) -> MfArray in 
    // copy data automatically
    let mfdata = MfData(source: nil, data_real_ptr: buffer.baseAddress!, data_imag_ptr: nil, storedSize: size, mftype: .Float, offset: 0)
    let mfstructure = MfStructure(shape: shape)
    return MfArray(mfdata: mfdata, mfstructure: mfstructure)
}

If the ORTValue conform to MfDataBasable, you can share data without copying, so that it is very efficient!

@jjjkkkjjj Thank you :). But the this constructor is define as internal, I am unable to access it.

@jjjkkkjjj
Copy link
Owner

OMG!
I'll change it into public ASAP.
Wait for a moment...

jjjkkkjjj pushed a commit that referenced this issue May 15, 2024
@jjjkkkjjj
Copy link
Owner

@Eumentis-Jayashree
I've changed it! Please check.

@jjjkkkjjj
Copy link
Owner

This one is no-copy (share version)

extension ORTValue: MfDataBasable{
}

let rawOutputData // ORTValue
let shape = // get from ORTValue
let size = // calculate the size from product all dimension = 1*359*8400
let floatValuesForOutput = rawOutputData.withUnsafeBytes { 
    (buffer: UnsafeRawBufferPointer) -> MfArray in 
    // share data
    let mfdata = MfData(source: rawOutputData, data_real_ptr: buffer.baseAddress!, data_imag_ptr: nil, storedSize: size, mftype: .Float, offset: 0)
    let mfstructure = MfStructure(shape: shape)
    return MfArray(mfdata: mfdata, mfstructure: mfstructure)
}

@Eumentis-Jayashree
Copy link
Author

let floatValuesForOutput = rawOutputData.withUnsafeBytes { 
    (buffer: UnsafeRawBufferPointer) -> MfArray in 
    // copy data automatically
    let mfdata = MfData(source: nil, data_real_ptr: buffer.baseAddress!, data_imag_ptr: nil, storedSize: size, mftype: .Float, offset: 0)
    let mfstructure = MfStructure(shape: shape)
    return MfArray(mfdata: mfdata, mfstructure: mfstructure)
}

Yeah, I solved it. But I am not sure if it's right approach. I have converted the Matft array to a Swift array and then converted it into an OpenCV mat array.

OK, but it is not efficient due to coping the data twice as you mentioned.

get rawOutputData from ORTValue using .tensorData() property which data type is Data. Can I directly convert this rawOutputData into Matft array?

That's why we should use same data or copy it once. Fortunately, I implemented borrowing data system by this;

let shape = // get from ORTValue
let size = // calculate the size from product all dimension = 1*359*8400
let floatValuesForOutput = rawOutputData.withUnsafeBytes { 
    (buffer: UnsafeRawBufferPointer) -> MfArray in 
    // copy data automatically
    let mfdata = MfData(source: nil, data_real_ptr: buffer.baseAddress!, data_imag_ptr: nil, storedSize: size, mftype: .Float, offset: 0)
    let mfstructure = MfStructure(shape: shape)
    return MfArray(mfdata: mfdata, mfstructure: mfstructure)
}

If the ORTValue conform to MfDataBasable, you can share data without copying, so that it is very efficient!

@jjjkkkjjj Thank you :). But the this constructor is define as internal, I am unable to access it.

I am getting error Cannot convert value of type 'UnsafeRawPointer' to expected argument type 'UnsafeMutableRawPointer' at data_real_ptr: buffer.baseAddress! , because constructor accepts this data type data_real_ptr: UnsafeMutableRawPointer
Do I need to convert this (buffer: UnsafeRawBufferPointer) to UnsafeMutableRawPointer?

@jjjkkkjjj
Copy link
Owner

@Eumentis-Jayashree
Does rawOutputData have withUnsafeMutableBytes method? If so, when you replace withUnsafeBytes into withUnsafeMutableBytes, the error will be suppressed.
Otherwise, I guess you can convert UnsafeRawPointer into UnsafeMutableRawPointer by;

let mfdata = MfData(source: nil, data_real_ptr: UnsafeMutableRawPointer(mutating: buffer.baseAddress!), data_imag_ptr: nil, storedSize: size, mftype: .Float, offset: 0)

@Eumentis-Jayashree
Copy link
Author

I have converted UnsafeRawBufferPointer to UnsafeMutableRawPointer it's giving multiple errors at the time of build.

Showing All Messages
../Matft/core/util/typeconversion.swift:73:19: Incorrect argument label in call (have 'from:count:', expected 'repeating:count:')

../Matft/core/util/typeconversion.swift:73:40: Cannot convert value of type 'UnsafePointer' to expected argument type 'T'

../Matft/core/util/typeconversion.swift:134:24: Incorrect argument label in call (have 'from:count:', expected 'repeating:count:')

../Matft/core/util/typeconversion.swift:134:45: Cannot convert value of type 'UnsafeMutablePointer' to expected argument type 'Float'

../Matft/core/util/typeconversion.swift:144:18: Value of type 'UnsafeMutablePointer' has no member 'moveUpdate'

../Matft/core/util/typeconversion.swift:154:24: Incorrect argument label in call (have 'from:count:', expected 'repeating:count:')

../Matft/core/util/typeconversion.swift:154:72: Cannot convert value of type 'UnsafePointer' to expected argument type 'Float'

../Matft/core/util/typeconversion.swift:234:18: Value of type 'UnsafeMutablePointer' has no member 'moveUpdate'

../Matft/core/util/typeconversion.swift:241:24: Incorrect argument label in call (have 'from:count:', expected 'repeating:count:')

../Matft/core/util/typeconversion.swift:241:45: Cannot convert value of type 'UnsafeMutablePointer' to expected argument type 'Double'

../Matft/core/util/typeconversion.swift:256:24: Incorrect argument label in call (have 'from:count:', expected 'repeating:count:')

../Matft/core/util/typeconversion.swift:256:73: Cannot convert value of type 'UnsafePointer' to expected argument type 'Double'

../Matft/core/util/typeconversion.swift:269:27: Value of type 'UnsafeMutablePointer' has no member 'moveUpdate'

@jjjkkkjjj
Copy link
Owner

jjjkkkjjj commented May 15, 2024

Which swift version was used?
I guess mismatch swift version was caused

@Eumentis-Jayashree
Copy link
Author

Eumentis-Jayashree commented May 15, 2024

Xcode -> target -> Build Settings -> Swift Compiler Language -> Swift Language Version: swift 5

from terminal I get:
swift-driver version: 1.62.15 Apple Swift version 5.7.2 (swiftlang-5.7.2.135.5 clang-1400.0.29.51)
Target: arm64-apple-macosx13.0

@jjjkkkjjj
Copy link
Owner

Hmm, It's strange.
My code can build correctly.

let d: [Float] = [2, 3] // I didn't install ORTValue. So I use a simple array as workaround
let size = 2
let shape = [2,1]
let floatValuesForOutput = d.withUnsafeBytes {
                (buffer: UnsafeRawBufferPointer) -> MfArray in
                // copy data automatically
                let mfdata = MfData(source: nil, data_real_ptr: UnsafeMutableRawPointer(mutating: buffer.baseAddress!), data_imag_ptr: nil, storedSize: size, mftype: .Float, offset: 0)
                let mfstructure = MfStructure(shape: shape, mforder: .Row)
                return MfArray(mfdata: mfdata, mfstructure: mfstructure)
            }

@jjjkkkjjj
Copy link
Owner

jjjkkkjjj commented May 15, 2024

@Eumentis-Jayashree
Which Matft version did you use? Is it latest version?
And, can you share the all codes about conversion?

@Eumentis-Jayashree
Copy link
Author

I figured out what's happening. When I install Matft's specific version (Rules -> Exact -> 0.3.3) 0.3.3 then it's working fine. But when I install it using Rules -> Branch -> master, then it gives these errors.

Screenshot 2024-05-16 at 12 14 44 PM

jjjkkkjjj pushed a commit that referenced this issue May 16, 2024
@jjjkkkjjj
Copy link
Owner

@Eumentis-Jayashree
Namely, the codes updated after 0.3.3 caused this problem...
This commit may be the reason.
I reverted it on the revert_duplicate branch. Could you try the codes again in revert_duplicate branch?

@Eumentis-Jayashree
Copy link
Author

@jjjkkkjjj It's working on the revert_duplicate branch. Thank you :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants