Add generate_mask_labels_op to support Mask-RCNN and refine some code. (#15371)
* Add generate_mask_labels_op to support Mask-RCNN. * Refine sigmoid_cross_entropy to support nomalize mode. * Fix generator_proposals_label. * Use DeviceTemporaryAllocator in roi_pool and roi_algin. * Remove shape check in data_feeder.inference-pre-release-gpu
parent
9f5108a673
commit
07dc5a1506
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,229 @@
|
||||
/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License. */
|
||||
|
||||
#include "paddle/fluid/operators/detection/mask_util.h"
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
#include "paddle/fluid/memory/memory.h"
|
||||
|
||||
namespace paddle {
|
||||
namespace operators {
|
||||
|
||||
uint32_t UMax(uint32_t a, uint32_t b) { return (a > b) ? a : b; }
|
||||
|
||||
static inline int Compare(const void* a, const void* b) {
|
||||
uint32_t c = *(reinterpret_cast<const uint32_t*>(a));
|
||||
uint32_t d = *(reinterpret_cast<const uint32_t*>(b));
|
||||
return c > d ? 1 : c < d ? -1 : 0;
|
||||
}
|
||||
|
||||
void Decode(const uint32_t* cnts, int m, uint8_t* mask) {
|
||||
uint8_t v = 0;
|
||||
for (int j = 0; j < m; j++) {
|
||||
for (uint32_t k = 0; k < cnts[j]; k++) {
|
||||
*(mask++) = v;
|
||||
}
|
||||
v = !v;
|
||||
}
|
||||
}
|
||||
|
||||
typedef uint32_t uint;
|
||||
void Poly2Mask(const float* xy, int k, int h, int w, uint8_t* mask) {
|
||||
int j, m = 0;
|
||||
double scale = 5;
|
||||
int *x, *y, *u, *v;
|
||||
uint *a, *b;
|
||||
platform::CPUPlace cpu;
|
||||
auto xptr = memory::Alloc(cpu, sizeof(int) * (k + 1) * 2);
|
||||
x = reinterpret_cast<int*>(xptr->ptr());
|
||||
y = x + (k + 1);
|
||||
|
||||
for (j = 0; j < k; j++) x[j] = static_cast<int>(scale * xy[j * 2 + 0] + .5);
|
||||
x[k] = x[0];
|
||||
for (j = 0; j < k; j++) y[j] = static_cast<int>(scale * xy[j * 2 + 1] + .5);
|
||||
y[k] = y[0];
|
||||
for (j = 0; j < k; j++) {
|
||||
m += UMax(abs(x[j] - x[j + 1]), abs(y[j] - y[j + 1])) + 1;
|
||||
}
|
||||
auto vptr = memory::Alloc(cpu, sizeof(int) * m * 2);
|
||||
u = reinterpret_cast<int*>(vptr->ptr());
|
||||
v = u + m;
|
||||
m = 0;
|
||||
for (j = 0; j < k; j++) {
|
||||
int xs = x[j], xe = x[j + 1], ys = y[j], ye = y[j + 1], dx, dy, t, d;
|
||||
int flip;
|
||||
double s;
|
||||
dx = abs(xe - xs);
|
||||
dy = abs(ys - ye);
|
||||
flip = (dx >= dy && xs > xe) || (dx < dy && ys > ye);
|
||||
if (flip) {
|
||||
t = xs;
|
||||
xs = xe;
|
||||
xe = t;
|
||||
t = ys;
|
||||
ys = ye;
|
||||
ye = t;
|
||||
}
|
||||
if (dx >= dy) {
|
||||
s = dx == 0 ? 0 : static_cast<double>(ye - ys) / dx;
|
||||
for (d = 0; d <= dx; d++) {
|
||||
t = flip ? dx - d : d;
|
||||
u[m] = t + xs;
|
||||
v[m] = static_cast<int>(ys + s * t + .5);
|
||||
m++;
|
||||
}
|
||||
} else {
|
||||
s = dy == 0 ? 0 : static_cast<double>(xe - xs) / dy;
|
||||
for (d = 0; d <= dy; d++) {
|
||||
t = flip ? dy - d : d;
|
||||
v[m] = t + ys;
|
||||
u[m] = static_cast<int>(xs + s * t + .5);
|
||||
m++;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* get points along y-boundary and downsample */
|
||||
k = m;
|
||||
m = 0;
|
||||
double xd, yd;
|
||||
auto xyptr = memory::Alloc(cpu, sizeof(int) * k * 2);
|
||||
x = reinterpret_cast<int*>(xyptr->ptr());
|
||||
y = x + k;
|
||||
for (j = 1; j < k; j++) {
|
||||
if (u[j] != u[j - 1]) {
|
||||
xd = static_cast<double>(u[j] < u[j - 1] ? u[j] : u[j] - 1);
|
||||
xd = (xd + .5) / scale - .5;
|
||||
if (floor(xd) != xd || xd < 0 || xd > w - 1) continue;
|
||||
yd = static_cast<double>(v[j] < v[j - 1] ? v[j] : v[j - 1]);
|
||||
yd = (yd + .5) / scale - .5;
|
||||
if (yd < 0)
|
||||
yd = 0;
|
||||
else if (yd > h)
|
||||
yd = h;
|
||||
yd = ceil(yd);
|
||||
x[m] = static_cast<int>(xd);
|
||||
y[m] = static_cast<int>(yd);
|
||||
m++;
|
||||
}
|
||||
}
|
||||
/* compute rle encoding given y-boundary points */
|
||||
k = m;
|
||||
auto aptr = memory::Alloc(cpu, sizeof(uint) * (k + 1));
|
||||
a = reinterpret_cast<uint*>(aptr->ptr());
|
||||
for (j = 0; j < k; j++) a[j] = static_cast<uint>(x[j] * h + y[j]);
|
||||
a[k++] = static_cast<uint>(h * w);
|
||||
|
||||
qsort(a, k, sizeof(uint), Compare);
|
||||
uint p = 0;
|
||||
for (j = 0; j < k; j++) {
|
||||
uint t = a[j];
|
||||
a[j] -= p;
|
||||
p = t;
|
||||
}
|
||||
auto bptr = memory::Alloc(cpu, sizeof(uint32_t) * k);
|
||||
b = reinterpret_cast<uint32_t*>(bptr->ptr());
|
||||
j = m = 0;
|
||||
b[m++] = a[j++];
|
||||
while (j < k) {
|
||||
if (a[j] > 0) {
|
||||
b[m++] = a[j++];
|
||||
} else {
|
||||
j++;
|
||||
if (j < k) b[m - 1] += a[j++];
|
||||
}
|
||||
}
|
||||
|
||||
// convert to mask
|
||||
auto mskptr = memory::Alloc(cpu, sizeof(uint8_t) * h * w);
|
||||
uint8_t* msk = reinterpret_cast<uint8_t*>(mskptr->ptr());
|
||||
Decode(b, m, msk);
|
||||
|
||||
for (int ii = 0; ii < h; ++ii) {
|
||||
for (int jj = 0; jj < w; ++jj) {
|
||||
mask[ii * w + jj] = msk[jj * h + ii];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Poly2Boxes(const std::vector<std::vector<std::vector<float>>>& polys,
|
||||
float* boxes) {
|
||||
// lists
|
||||
for (size_t i = 0; i < polys.size(); ++i) {
|
||||
float x0 = std::numeric_limits<float>::max();
|
||||
float x1 = std::numeric_limits<float>::min();
|
||||
float y0 = std::numeric_limits<float>::max();
|
||||
float y1 = std::numeric_limits<float>::min();
|
||||
// each list may have more than one polys
|
||||
for (size_t j = 0; j < polys[i].size(); ++j) {
|
||||
for (size_t k = 0; k < polys[i][j].size() / 2; ++k) {
|
||||
x0 = std::min(x0, polys[i][j][2 * k]);
|
||||
x1 = std::max(x1, polys[i][j][2 * k]);
|
||||
y0 = std::min(y0, polys[i][j][2 * k + 1]);
|
||||
y1 = std::max(y1, polys[i][j][2 * k + 1]);
|
||||
}
|
||||
}
|
||||
boxes[i * 4] = x0;
|
||||
boxes[i * 4 + 1] = y0;
|
||||
boxes[i * 4 + 2] = x1;
|
||||
boxes[i * 4 + 3] = y1;
|
||||
}
|
||||
}
|
||||
|
||||
void Polys2MaskWrtBox(const std::vector<std::vector<float>>& polygons,
|
||||
const float* box, int M, uint8_t* mask) {
|
||||
float w = box[2] - box[0];
|
||||
float h = box[3] - box[1];
|
||||
w = std::max(w, static_cast<float>(1.));
|
||||
h = std::max(h, static_cast<float>(1.));
|
||||
|
||||
uint8_t* msk = nullptr;
|
||||
if (polygons.size() == 1UL) {
|
||||
msk = mask;
|
||||
} else {
|
||||
msk = reinterpret_cast<uint8_t*>(
|
||||
malloc(M * M * polygons.size() * sizeof(uint8_t)));
|
||||
}
|
||||
for (size_t i = 0; i < polygons.size(); ++i) {
|
||||
int k = polygons[i].size() / 2;
|
||||
std::vector<float> p;
|
||||
for (int j = 0; j < k; ++j) {
|
||||
float pw = (polygons[i][2 * j] - box[0]) * M / w;
|
||||
float ph = (polygons[i][2 * j + 1] - box[1]) * M / h;
|
||||
p.push_back(pw);
|
||||
p.push_back(ph);
|
||||
}
|
||||
uint8_t* msk_i = msk + i * M * M;
|
||||
Poly2Mask(p.data(), k, M, M, msk_i);
|
||||
}
|
||||
|
||||
if (polygons.size() > 1UL) {
|
||||
for (size_t i = 0; i < polygons.size(); ++i) {
|
||||
uint8_t* msk_i = msk + i * M * M;
|
||||
for (int j = 0; j < M * M; ++j) {
|
||||
if (i == 0) {
|
||||
mask[j] = msk_i[j];
|
||||
} else {
|
||||
mask[j] = (mask[j] + msk_i[j]) > 0 ? 1 : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(msk);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace operators
|
||||
} // namespace paddle
|
@ -0,0 +1,30 @@
|
||||
/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License. */
|
||||
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
namespace paddle {
|
||||
namespace operators {
|
||||
|
||||
void Poly2Mask(const float* ploy, int k, int h, int w, uint8_t* mask);
|
||||
|
||||
void Poly2Boxes(const std::vector<std::vector<std::vector<float>>>& polys,
|
||||
float* boxes);
|
||||
|
||||
void Polys2MaskWrtBox(const std::vector<std::vector<float>>& polygons,
|
||||
const float* box, int M, uint8_t* mask);
|
||||
} // namespace operators
|
||||
} // namespace paddle
|
@ -0,0 +1,115 @@
|
||||
/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License. */
|
||||
|
||||
#include "paddle/fluid/operators/detection/mask_util.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include "paddle/fluid/memory/memory.h"
|
||||
|
||||
namespace paddle {
|
||||
namespace operators {
|
||||
|
||||
template <typename T>
|
||||
void Compare(const T* a, const T* b, const int n) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
EXPECT_EQ(a[i], b[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MaskUtil, Poly2MaskTest) {
|
||||
float polys[] = {1.97f, 1.88f, 5.81f, 1.88f, 1.69f,
|
||||
6.53f, 5.94f, 6.38f, 1.97f, 1.88f};
|
||||
int h = 8, w = 8;
|
||||
int k = 5; // length(polys) / 2
|
||||
// clang-format off
|
||||
uint8_t expect_mask[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 1, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 1, 0, 0, 0, 0,
|
||||
0, 0, 1, 1, 1, 0, 0, 0,
|
||||
0, 0, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
// the groud-truth mask is computed by coco API:
|
||||
//
|
||||
// import pycocotools.mask as mask_util
|
||||
// import numpy as np
|
||||
// segm = [1.97, 1.88, 5.81, 1.88, 1.69, 6.53, 5.94, 6.38, 1.97, 1.88]
|
||||
// rles = mask_util.frPyObjects([segm], im_h, im_w)
|
||||
// mask = mask_util.decode(rles)
|
||||
// print mask
|
||||
platform::CPUPlace cpu;
|
||||
auto allocation = memory::Alloc(cpu, sizeof(expect_mask));
|
||||
uint8_t* mask = reinterpret_cast<uint8_t*>(allocation->ptr());
|
||||
Poly2Mask(polys, k, h, w, mask);
|
||||
Compare<uint8_t>(expect_mask, mask, h * w);
|
||||
}
|
||||
|
||||
TEST(MaskUtil, Poly2BoxesTest) {
|
||||
// clang-format off
|
||||
std::vector<std::vector<std::vector<float>>> polys = {
|
||||
{{1.97f, 1.88f, 5.81f, 1.88f, 1.69f, 6.53f, 5.94f, 6.38f, 1.97f, 1.88f}},
|
||||
{{2.97f, 1.88f, 3.81f, 1.68f, 1.69f, 6.63f, 6.94f, 6.58f, 2.97f, 0.88f}}
|
||||
};
|
||||
float expect_boxes[] = {
|
||||
1.69f, 1.88f, 5.94f, 6.53f,
|
||||
1.69f, 0.88f, 6.94f, 6.63f
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
platform::CPUPlace cpu;
|
||||
auto allocation = memory::Alloc(cpu, sizeof(expect_boxes));
|
||||
float* boxes = reinterpret_cast<float*>(allocation->ptr());
|
||||
Poly2Boxes(polys, boxes);
|
||||
Compare<float>(expect_boxes, boxes, 8);
|
||||
}
|
||||
|
||||
TEST(MaskUtil, Polys2MaskWrtBoxTest) {
|
||||
// clang-format off
|
||||
std::vector<std::vector<std::vector<float>>> polys = {{
|
||||
{1.97f, 1.88f, 5.81f, 1.88f, 1.69f, 6.53f, 5.94f, 6.38f, 1.97f, 1.88f},
|
||||
{2.97f, 1.88f, 3.81f, 1.68f, 1.69f, 6.63f, 6.94f, 6.58f, 2.97f, 0.88f}}};
|
||||
float expect_boxes[] = {
|
||||
1.69f, 0.88f, 6.94f, 6.63f
|
||||
};
|
||||
uint8_t expect_mask[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 1, 1, 1, 1, 1, 0, 0,
|
||||
0, 0, 1, 1, 1, 0, 0, 0,
|
||||
0, 0, 1, 1, 1, 0, 0, 0,
|
||||
0, 0, 1, 1, 1, 0, 0, 0,
|
||||
0, 1, 1, 1, 1, 1, 0, 0,
|
||||
0, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
platform::CPUPlace cpu;
|
||||
auto allocation = memory::Alloc(cpu, sizeof(expect_boxes));
|
||||
float* boxes = reinterpret_cast<float*>(allocation->ptr());
|
||||
Poly2Boxes(polys, boxes);
|
||||
Compare<float>(expect_boxes, boxes, 4);
|
||||
|
||||
auto allocat_mask = memory::Alloc(cpu, sizeof(expect_mask));
|
||||
uint8_t* mask = reinterpret_cast<uint8_t*>(allocat_mask->ptr());
|
||||
int M = 8;
|
||||
Polys2MaskWrtBox(polys[0], expect_boxes, M, mask);
|
||||
Compare<uint8_t>(expect_mask, mask, M * M);
|
||||
}
|
||||
|
||||
} // namespace operators
|
||||
} // namespace paddle
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue