Skip to content

Commit c4ae504

Browse files
authored
[DIP] Add support for dip.rotate_2d (buddy-compiler#23)
1 parent abaffd2 commit c4ae504

File tree

7 files changed

+622
-10
lines changed

7 files changed

+622
-10
lines changed

examples/DIPDialect/CMakeLists.txt

+10-6
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,16 @@ endif()
1212

1313
message(STATUS "Spliting size: ${SPLITING_SIZE}")
1414

15-
add_custom_command(OUTPUT corr2d.o
16-
COMMAND ${BUDDY_BINARY_DIR}/buddy-opt ${BUDDY_EXAMPLES_DIR}/DIPDialect/corr2d.mlir -lower-dip="DIP-strip-mining=${SPLITING_SIZE}" -lower-affine -convert-scf-to-cf -convert-vector-to-llvm -convert-memref-to-llvm -convert-func-to-llvm='emit-c-wrappers=1' -reconcile-unrealized-casts |
15+
add_custom_command(OUTPUT dip.o
16+
COMMAND ${BUDDY_BINARY_DIR}/buddy-opt ${BUDDY_EXAMPLES_DIR}/DIPDialect/dip.mlir -lower-dip="DIP-strip-mining=${SPLITING_SIZE}" -arith-expand -lower-affine -convert-scf-to-cf -convert-math-to-llvm -convert-vector-to-llvm -convert-memref-to-llvm -convert-func-to-llvm='emit-c-wrappers=1' -reconcile-unrealized-casts |
1717
${LLVM_MLIR_BINARY_DIR}/mlir-translate --mlir-to-llvmir |
18-
${LLVM_MLIR_BINARY_DIR}/llc -mtriple=${BUDDY_TARGET_TRIPLE} -mattr=${BUDDY_OPT_ATTR} --filetype=obj -o ${BUDDY_BINARY_DIR}/../examples/DIPDialect/corr2d.o
18+
${LLVM_MLIR_BINARY_DIR}/llc -mtriple=${BUDDY_OPT_TRIPLE} -mattr=${BUDDY_OPT_ATTR} --filetype=obj -o ${BUDDY_BINARY_DIR}/../examples/DIPDialect/dip.o
1919
DEPENDS buddy-opt)
2020

21-
add_library(Corr2D STATIC corr2d.o)
21+
add_library(DIP STATIC dip.o)
2222

2323
SET_TARGET_PROPERTIES(
24-
Corr2D
24+
DIP
2525
PROPERTIES
2626
LINKER_LANGUAGE C)
2727

@@ -30,4 +30,8 @@ include_directories(${OpenCV_INCLUDE_DIRS})
3030

3131
add_executable(correlation2D correlation2D.cpp)
3232
add_dependencies(correlation2D buddy-opt)
33-
target_link_libraries(correlation2D ${OpenCV_LIBS} Corr2D)
33+
target_link_libraries(correlation2D ${OpenCV_LIBS} DIP)
34+
35+
add_executable(rotation2D rotation2D.cpp)
36+
add_dependencies(rotation2D buddy-opt)
37+
target_link_libraries(rotation2D ${OpenCV_LIBS} DIP)

examples/DIPDialect/corr2d.mlir renamed to examples/DIPDialect/dip.mlir

+6
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,9 @@ func.func @corr_2d_replicate_padding(%inputImage : memref<?x?xf32>, %kernel : me
99
dip.corr_2d REPLICATE_PADDING %inputImage, %kernel, %outputImage, %centerX, %centerY , %constantValue : memref<?x?xf32>, memref<?x?xf32>, memref<?x?xf32>, index, index, f32
1010
return
1111
}
12+
13+
func.func @rotate_2d(%inputImage : memref<?x?xf32>, %angle : f32, %outputImage : memref<?x?xf32>)
14+
{
15+
dip.rotate_2d %inputImage, %angle, %outputImage : memref<?x?xf32>, f32, memref<?x?xf32>
16+
return
17+
}

examples/DIPDialect/rotation2D.cpp

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
//====- rotation2D.cpp - Example of buddy-opt tool ===========================//
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
//
17+
// This file implements a 2D rotation example with dip.rotate_2d operation.
18+
// The dip.rotate_2d operation will be compiled into an object file with the
19+
// buddy-opt tool.
20+
// This file will be linked with the object file to generate the executable
21+
// file.
22+
//
23+
//===----------------------------------------------------------------------===//
24+
25+
#include <opencv2/imgcodecs.hpp>
26+
#include <opencv2/opencv.hpp>
27+
28+
#include <Interface/buddy/dip/dip.h>
29+
#include <Interface/buddy/dip/memref.h>
30+
#include <iostream>
31+
32+
using namespace cv;
33+
using namespace std;
34+
35+
bool testImages(cv::Mat img1, cv::Mat img2) {
36+
if (img1.rows != img2.rows || img1.cols != img2.cols) {
37+
std::cout << "Dimensions not equal\n";
38+
return 0;
39+
}
40+
41+
for (std::ptrdiff_t i = 0; i < img1.cols; ++i) {
42+
for (std::ptrdiff_t j = 0; j < img1.rows; ++j) {
43+
if (img1.at<uchar>(i, j) != img2.at<uchar>(i, j)) {
44+
std::cout << "Pixels not equal at : (" << i << "," << j << ")\n";
45+
std::cout << (int)img1.at<uchar>(i, j) << "\n";
46+
std::cout << (int)img2.at<uchar>(i, j) << "\n\n";
47+
48+
std::cout << img1 << "\n\n";
49+
std::cout << img2 << "\n\n";
50+
return 0;
51+
}
52+
}
53+
}
54+
return 1;
55+
}
56+
57+
bool testImplementation(int argc, char *argv[]) {
58+
// Read as grayscale image.
59+
Mat image = imread(argv[1], IMREAD_GRAYSCALE);
60+
if (image.empty()) {
61+
cout << "Could not read the image: " << argv[1] << endl;
62+
}
63+
64+
int inputSize = image.rows * image.cols;
65+
66+
// Define the input with the image.
67+
float *inputAlign = (float *)malloc(inputSize * sizeof(float));
68+
for (int i = 0; i < image.rows; i++) {
69+
for (int j = 0; j < image.cols; j++) {
70+
inputAlign[image.rows * i + j] = (float)image.at<uchar>(i, j);
71+
}
72+
}
73+
74+
// Define allocated, sizes, and strides fields for the MemRef_descriptor.
75+
float *allocated = (float *)malloc(1 * sizeof(float));
76+
intptr_t sizesInput[2] = {image.rows, image.cols};
77+
intptr_t stridesInput[2] = {image.rows, image.cols};
78+
79+
// Define memref descriptors.
80+
MemRef_descriptor input =
81+
MemRef_Descriptor(allocated, inputAlign, 0, sizesInput, stridesInput);
82+
MemRef_descriptor output = dip::Rotate2D(input, 45, dip::ANGLE_TYPE::DEGREE);
83+
84+
// Define a cv::Mat with the output of Rotate2D.
85+
Mat outputImageRotate2D(output->sizes[0], output->sizes[1], CV_32FC1,
86+
output->aligned);
87+
imwrite(argv[2], outputImageRotate2D);
88+
89+
free(input);
90+
free(inputAlign);
91+
92+
return 1;
93+
}
94+
95+
int main(int argc, char *argv[]) {
96+
testImplementation(argc, argv);
97+
98+
return 0;
99+
}

examples/README.md

+9
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,15 @@ Of course, you can also use your own configuration assigning values `-DBUDDY_DIP
115115

116116
*Note: Maximum allowed value of `BUDDY_DIP_OPT_STRIP_MINING` for producing correct result is equal to image width.*
117117

118+
- Rotation example:
119+
```
120+
$ cd buddy-mlir/build
121+
$ cmake -G Ninja .. -DBUDDY_EXAMPLES=ON -DBUDDY_ENABLE_OPENCV=ON
122+
$ ninja rotation2D
123+
$ cd bin
124+
$ ./rotation2D ../../examples/ConvOpt/images/YuTu.png result-dip-rotate.png
125+
```
126+
118127
We also provide the performance comparison between our `buddy-opt` tool and other state-of-the-art approaches.
119128
For more details, please see [the benchamrk in the buddy-benchmark repo](https://github.com/buddy-compiler/buddy-benchmark#image-processing-benchmark).
120129

include/Dialect/DIP/DIPOps.td

+30
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,34 @@ def DIP_Corr2DOp : DIP_Op<"corr_2d">
7979
}];
8080
}
8181

82+
def DIP_Rotate2DOp : DIP_Op<"rotate_2d"> {
83+
let summary = [{This operation intends to provide utility for rotating images via the DIP dialect.
84+
Image rotation has many applications such as data augmentation, alignment adjustment, etc. and
85+
can thus be used in native MLIR pipelines involving above mentioned uses.
86+
87+
Standard affine rotation matrix [cosθ sinθ] is used whenever |tan(θ/2)| > 8.1 for the user defined θ.
88+
[-sinθ cosθ]
89+
In all other cases, the 3 shear method is used to simplify calculations and minimize generated
90+
artifacts. The equivalent 3 shear matrix combination is as follows :
91+
[1 -tan(θ/2)] * [ 1 0] * [1 -tan(θ/2)]
92+
[0 1 ] [sinθ 1] [0 1 ]
93+
94+
For example:
95+
96+
```mlir
97+
dip.rotate_2d %inputImage, %angle, %outputImage : memref<?x?xf32>, f32, memref<?x?xf32>
98+
```
99+
}];
100+
101+
let arguments = (ins Arg<AnyRankedOrUnrankedMemRef, "inputMemref",
102+
[MemRead]>:$memrefI,
103+
F32 : $angle,
104+
Arg<AnyRankedOrUnrankedMemRef, "outputMemref",
105+
[MemRead]>:$memrefO);
106+
107+
let assemblyFormat = [{
108+
$memrefI `,` $angle `,` $memrefO attr-dict `:` type($memrefI) `,` type($angle) `,` type($memrefO)
109+
}];
110+
}
111+
82112
#endif // DIP_DIPOPS_TD

include/Interface/buddy/dip/dip.h

+37
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,14 @@ void _mlir_ciface_corr_2d_constant_padding(
3636
void _mlir_ciface_corr_2d_replicate_padding(
3737
MemRef_descriptor input, MemRef_descriptor kernel, MemRef_descriptor output,
3838
unsigned int centerX, unsigned int centerY, float constantValue);
39+
40+
void _mlir_ciface_rotate_2d(MemRef_descriptor input, float angleValue,
41+
MemRef_descriptor output);
3942
}
4043
} // namespace detail
4144

4245
enum class BOUNDARY_OPTION { CONSTANT_PADDING, REPLICATE_PADDING };
46+
enum class ANGLE_TYPE { DEGREE, RADIAN };
4347

4448
void Corr2D(MemRef_descriptor input, MemRef_descriptor kernel,
4549
MemRef_descriptor output, unsigned int centerX,
@@ -53,6 +57,39 @@ void Corr2D(MemRef_descriptor input, MemRef_descriptor kernel,
5357
centerX, centerY, 0);
5458
}
5559
}
60+
61+
MemRef_descriptor Rotate2D(MemRef_descriptor input, float angle,
62+
ANGLE_TYPE angleType) {
63+
float angleRad;
64+
65+
if (angleType == ANGLE_TYPE::DEGREE)
66+
angleRad = M_PI * angle / 180;
67+
else
68+
angleRad = angle;
69+
70+
float sinAngle = std::sin(angleRad);
71+
float cosAngle = std::cos(angleRad);
72+
73+
int outputRows = std::round(std::abs(input->sizes[0] * cosAngle) +
74+
std::abs(input->sizes[1] * sinAngle)) + 1;
75+
int outputCols = std::round(std::abs(input->sizes[1] * cosAngle) +
76+
std::abs(input->sizes[0] * sinAngle)) + 1;
77+
float *outputAlign = (float *)malloc(outputRows * outputCols * sizeof(float));
78+
79+
for (int i = 0; i < outputRows; ++i)
80+
for (int j = 0; j < outputCols; ++j)
81+
outputAlign[i * outputRows + j] = 0;
82+
83+
float *allocated = (float *)malloc(1 * sizeof(float));
84+
intptr_t sizesOutput[2] = {outputRows, outputCols};
85+
intptr_t stridesOutput[2] = {outputRows, outputCols};
86+
MemRef_descriptor output =
87+
MemRef_Descriptor(allocated, outputAlign, 0, sizesOutput, stridesOutput);
88+
89+
detail::_mlir_ciface_rotate_2d(input, angleRad, output);
90+
91+
return output;
92+
}
5693
} // namespace dip
5794

5895
#endif

0 commit comments

Comments
 (0)