Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
Greenplum
Opencv
提交
b907bfe3
O
Opencv
项目概览
Greenplum
/
Opencv
大约 1 年 前同步成功
通知
7
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
O
Opencv
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
b907bfe3
编写于
8月 16, 2018
作者:
A
Alexander Alekhin
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #12222 from NCBee:master
上级
b996b618
98c5ce93
变更
2
显示空白变更内容
内联
并排
Showing
2 changed file
with
125 addition
and
97 deletion
+125
-97
modules/imgproc/src/intersection.cpp
modules/imgproc/src/intersection.cpp
+65
-25
modules/imgproc/test/test_intersection.cpp
modules/imgproc/test/test_intersection.cpp
+60
-72
未找到文件。
modules/imgproc/src/intersection.cpp
浏览文件 @
b907bfe3
...
@@ -51,12 +51,13 @@ int rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& r
...
@@ -51,12 +51,13 @@ int rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& r
{
{
CV_INSTRUMENT_REGION
()
CV_INSTRUMENT_REGION
()
const
float
samePointEps
=
0.00001
f
;
// used to test if two points are the same
// L2 metric
const
float
samePointEps
=
std
::
max
(
1e-16
f
,
1e-6
f
*
(
float
)
std
::
max
(
rect1
.
size
.
area
(),
rect2
.
size
.
area
()));
Point2f
vec1
[
4
],
vec2
[
4
];
Point2f
vec1
[
4
],
vec2
[
4
];
Point2f
pts1
[
4
],
pts2
[
4
];
Point2f
pts1
[
4
],
pts2
[
4
];
std
::
vector
<
Point2f
>
intersection
;
std
::
vector
<
Point2f
>
intersection
;
intersection
.
reserve
(
24
);
rect1
.
points
(
pts1
);
rect1
.
points
(
pts1
);
rect2
.
points
(
pts2
);
rect2
.
points
(
pts2
);
...
@@ -219,41 +220,80 @@ int rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& r
...
@@ -219,41 +220,80 @@ int rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& r
}
}
}
}
// Get rid of dupes and order points.
int
N
=
(
int
)
intersection
.
size
();
for
(
int
i
=
0
;
i
<
(
int
)
intersection
.
size
()
-
1
;
i
++
)
if
(
N
==
0
)
{
{
float
dx1
=
intersection
[
i
+
1
].
x
-
intersection
[
i
].
x
;
return
INTERSECT_NONE
;
float
dy1
=
intersection
[
i
+
1
].
y
-
intersection
[
i
].
y
;
}
for
(
size_t
j
=
i
+
1
;
j
<
intersection
.
size
();
j
++
)
{
float
dx
=
intersection
[
j
].
x
-
intersection
[
i
].
x
;
float
dy
=
intersection
[
j
].
y
-
intersection
[
i
].
y
;
double
d2
=
dx
*
dx
+
dy
*
dy
;
// can be a really small number, need double here
if
(
d2
<
samePointEps
*
samePointEps
)
// Get rid of duplicated points
int
Nstride
=
N
;
cv
::
AutoBuffer
<
float
,
100
>
distPt
(
N
*
N
);
cv
::
AutoBuffer
<
int
>
ptDistRemap
(
N
);
for
(
int
i
=
0
;
i
<
N
;
++
i
)
{
const
Point2f
pt0
=
intersection
[
i
];
ptDistRemap
[
i
]
=
i
;
for
(
int
j
=
i
+
1
;
j
<
N
;
)
{
{
// Found a dupe, remove it
const
Point2f
pt1
=
intersection
[
j
];
std
::
swap
(
intersection
[
j
],
intersection
.
back
());
float
d2
=
normL2Sqr
<
float
>
(
pt1
-
pt0
);
intersection
.
pop_back
();
if
(
d2
<=
samePointEps
)
j
--
;
// restart check
{
if
(
j
<
N
-
1
)
intersection
[
j
]
=
intersection
[
N
-
1
];
N
--
;
continue
;
}
}
else
if
(
dx1
*
dy
-
dy1
*
dx
<
0
)
distPt
[
i
*
Nstride
+
j
]
=
d2
;
++
j
;
}
}
while
(
N
>
8
)
// we still have duplicate points after samePointEps threshold (eliminate closest points)
{
{
std
::
swap
(
intersection
[
i
+
1
],
intersection
[
j
]);
int
minI
=
0
;
dx1
=
dx
;
int
minJ
=
1
;
dy1
=
dy
;
float
minD
=
distPt
[
1
];
for
(
int
i
=
0
;
i
<
N
-
1
;
++
i
)
{
float
*
pDist
=
distPt
.
data
()
+
Nstride
*
ptDistRemap
[
i
];
for
(
int
j
=
i
+
1
;
j
<
N
;
++
j
)
{
float
d
=
pDist
[
ptDistRemap
[
j
]];
if
(
d
<
minD
)
{
minD
=
d
;
minI
=
i
;
minJ
=
j
;
}
}
}
}
}
}
CV_Assert
(
fabs
(
normL2Sqr
<
float
>
(
intersection
[
minI
]
-
intersection
[
minJ
])
-
minD
)
<
1e-6
);
// ptDistRemap is not corrupted
if
(
intersection
.
empty
()
)
// drop minJ point
if
(
minJ
<
N
-
1
)
{
{
return
INTERSECT_NONE
;
intersection
[
minJ
]
=
intersection
[
N
-
1
];
ptDistRemap
[
minJ
]
=
ptDistRemap
[
N
-
1
];
}
N
--
;
}
}
// If this check fails then it means we're getting dupes, increase samePointEps
// order points
CV_Assert
(
intersection
.
size
()
<=
8
);
for
(
int
i
=
0
;
i
<
N
-
1
;
++
i
)
{
Point2f
diffI
=
intersection
[
i
+
1
]
-
intersection
[
i
];
for
(
int
j
=
i
+
2
;
j
<
N
;
++
j
)
{
Point2f
diffJ
=
intersection
[
j
]
-
intersection
[
i
];
if
(
diffI
.
cross
(
diffJ
)
<
0
)
{
std
::
swap
(
intersection
[
i
+
1
],
intersection
[
j
]);
diffI
=
diffJ
;
}
}
}
intersection
.
resize
(
N
);
Mat
(
intersection
).
copyTo
(
intersectingRegion
);
Mat
(
intersection
).
copyTo
(
intersectingRegion
);
return
ret
;
return
ret
;
...
...
modules/imgproc/test/test_intersection.cpp
浏览文件 @
b907bfe3
...
@@ -49,29 +49,18 @@ namespace opencv_test { namespace {
...
@@ -49,29 +49,18 @@ namespace opencv_test { namespace {
#define ACCURACY 0.00001
#define ACCURACY 0.00001
class
CV_RotatedRectangleIntersectionTest
:
public
cvtest
::
ArrayTest
// See pics/intersection.png for the scenarios we are testing
{
public:
// Test the following scenarios:
// 1 - no intersection
protected:
// 2 - partial intersection, rectangle translated
void
run
(
int
);
// 3 - partial intersection, rectangle rotated 45 degree on the corner, forms a triangle intersection
// 4 - full intersection, rectangles of same size directly on top of each other
private:
// 5 - partial intersection, rectangle on top rotated 45 degrees
void
test1
();
// 6 - partial intersection, rectangle on top of different size
void
test2
();
// 7 - full intersection, rectangle fully enclosed in the other
void
test3
();
// 8 - partial intersection, rectangle corner just touching. point contact
void
test4
();
// 9 - partial intersetion. rectangle side by side, line contact
void
test5
();
void
test6
();
void
test7
();
void
test8
();
void
test9
();
void
test10
();
void
test11
();
void
test12
();
void
test13
();
void
test14
();
};
static
void
compare
(
const
std
::
vector
<
Point2f
>&
test
,
const
std
::
vector
<
Point2f
>&
target
)
static
void
compare
(
const
std
::
vector
<
Point2f
>&
test
,
const
std
::
vector
<
Point2f
>&
target
)
{
{
...
@@ -80,45 +69,12 @@ static void compare(const std::vector<Point2f>& test, const std::vector<Point2f>
...
@@ -80,45 +69,12 @@ static void compare(const std::vector<Point2f>& test, const std::vector<Point2f>
ASSERT_TRUE
(
target
.
size
()
<
4
||
isContourConvex
(
target
));
ASSERT_TRUE
(
target
.
size
()
<
4
||
isContourConvex
(
target
));
for
(
size_t
i
=
0
;
i
<
test
.
size
();
i
++
)
for
(
size_t
i
=
0
;
i
<
test
.
size
();
i
++
)
{
{
double
dx
=
test
[
i
].
x
-
target
[
i
].
x
;
double
r
=
sqrt
(
normL2Sqr
<
double
>
(
test
[
i
]
-
target
[
i
]));
double
dy
=
test
[
i
].
y
-
target
[
i
].
y
;
double
r
=
sqrt
(
dx
*
dx
+
dy
*
dy
);
ASSERT_LT
(
r
,
ACCURACY
);
ASSERT_LT
(
r
,
ACCURACY
);
}
}
}
}
void
CV_RotatedRectangleIntersectionTest
::
run
(
int
)
TEST
(
Imgproc_RotatedRectangleIntersection
,
accuracy_1
)
{
// See pics/intersection.png for the scenarios we are testing
// Test the following scenarios:
// 1 - no intersection
// 2 - partial intersection, rectangle translated
// 3 - partial intersection, rectangle rotated 45 degree on the corner, forms a triangle intersection
// 4 - full intersection, rectangles of same size directly on top of each other
// 5 - partial intersection, rectangle on top rotated 45 degrees
// 6 - partial intersection, rectangle on top of different size
// 7 - full intersection, rectangle fully enclosed in the other
// 8 - partial intersection, rectangle corner just touching. point contact
// 9 - partial intersetion. rectangle side by side, line contact
test1
();
test2
();
test3
();
test4
();
test5
();
test6
();
test7
();
test8
();
test9
();
test10
();
test11
();
test12
();
test13
();
test14
();
}
void
CV_RotatedRectangleIntersectionTest
::
test1
()
{
{
// no intersection
// no intersection
RotatedRect
rect1
(
Point2f
(
0
,
0
),
Size2f
(
2
,
2
),
12.0
f
);
RotatedRect
rect1
(
Point2f
(
0
,
0
),
Size2f
(
2
,
2
),
12.0
f
);
...
@@ -131,7 +87,7 @@ void CV_RotatedRectangleIntersectionTest::test1()
...
@@ -131,7 +87,7 @@ void CV_RotatedRectangleIntersectionTest::test1()
CV_Assert
(
vertices
.
empty
());
CV_Assert
(
vertices
.
empty
());
}
}
void
CV_RotatedRectangleIntersectionTest
::
test2
(
)
TEST
(
Imgproc_RotatedRectangleIntersection
,
accuracy_2
)
{
{
// partial intersection, rectangles translated
// partial intersection, rectangles translated
RotatedRect
rect1
(
Point2f
(
0
,
0
),
Size2f
(
2
,
2
),
0.0
f
);
RotatedRect
rect1
(
Point2f
(
0
,
0
),
Size2f
(
2
,
2
),
0.0
f
);
...
@@ -150,7 +106,7 @@ void CV_RotatedRectangleIntersectionTest::test2()
...
@@ -150,7 +106,7 @@ void CV_RotatedRectangleIntersectionTest::test2()
compare
(
vertices
,
targetVertices
);
compare
(
vertices
,
targetVertices
);
}
}
void
CV_RotatedRectangleIntersectionTest
::
test3
(
)
TEST
(
Imgproc_RotatedRectangleIntersection
,
accuracy_3
)
{
{
// partial intersection, rectangles rotated 45 degree on the corner, forms a triangle intersection
// partial intersection, rectangles rotated 45 degree on the corner, forms a triangle intersection
RotatedRect
rect1
(
Point2f
(
0
,
0
),
Size2f
(
2
,
2
),
0.0
f
);
RotatedRect
rect1
(
Point2f
(
0
,
0
),
Size2f
(
2
,
2
),
0.0
f
);
...
@@ -168,7 +124,7 @@ void CV_RotatedRectangleIntersectionTest::test3()
...
@@ -168,7 +124,7 @@ void CV_RotatedRectangleIntersectionTest::test3()
compare
(
vertices
,
targetVertices
);
compare
(
vertices
,
targetVertices
);
}
}
void
CV_RotatedRectangleIntersectionTest
::
test4
(
)
TEST
(
Imgproc_RotatedRectangleIntersection
,
accuracy_4
)
{
{
// full intersection, rectangles of same size directly on top of each other
// full intersection, rectangles of same size directly on top of each other
RotatedRect
rect1
(
Point2f
(
0
,
0
),
Size2f
(
2
,
2
),
0.0
f
);
RotatedRect
rect1
(
Point2f
(
0
,
0
),
Size2f
(
2
,
2
),
0.0
f
);
...
@@ -187,7 +143,7 @@ void CV_RotatedRectangleIntersectionTest::test4()
...
@@ -187,7 +143,7 @@ void CV_RotatedRectangleIntersectionTest::test4()
compare
(
vertices
,
targetVertices
);
compare
(
vertices
,
targetVertices
);
}
}
void
CV_RotatedRectangleIntersectionTest
::
test5
(
)
TEST
(
Imgproc_RotatedRectangleIntersection
,
accuracy_5
)
{
{
// partial intersection, rectangle on top rotated 45 degrees
// partial intersection, rectangle on top rotated 45 degrees
RotatedRect
rect1
(
Point2f
(
0
,
0
),
Size2f
(
2
,
2
),
0.0
f
);
RotatedRect
rect1
(
Point2f
(
0
,
0
),
Size2f
(
2
,
2
),
0.0
f
);
...
@@ -210,7 +166,7 @@ void CV_RotatedRectangleIntersectionTest::test5()
...
@@ -210,7 +166,7 @@ void CV_RotatedRectangleIntersectionTest::test5()
compare
(
vertices
,
targetVertices
);
compare
(
vertices
,
targetVertices
);
}
}
void
CV_RotatedRectangleIntersectionTest
::
test6
(
)
TEST
(
Imgproc_RotatedRectangleIntersection
,
accuracy_6
)
{
{
// 6 - partial intersection, rectangle on top of different size
// 6 - partial intersection, rectangle on top of different size
RotatedRect
rect1
(
Point2f
(
0
,
0
),
Size2f
(
2
,
2
),
0.0
f
);
RotatedRect
rect1
(
Point2f
(
0
,
0
),
Size2f
(
2
,
2
),
0.0
f
);
...
@@ -229,7 +185,7 @@ void CV_RotatedRectangleIntersectionTest::test6()
...
@@ -229,7 +185,7 @@ void CV_RotatedRectangleIntersectionTest::test6()
compare
(
vertices
,
targetVertices
);
compare
(
vertices
,
targetVertices
);
}
}
void
CV_RotatedRectangleIntersectionTest
::
test7
(
)
TEST
(
Imgproc_RotatedRectangleIntersection
,
accuracy_7
)
{
{
// full intersection, rectangle fully enclosed in the other
// full intersection, rectangle fully enclosed in the other
RotatedRect
rect1
(
Point2f
(
0
,
0
),
Size2f
(
12.34
f
,
56.78
f
),
0.0
f
);
RotatedRect
rect1
(
Point2f
(
0
,
0
),
Size2f
(
12.34
f
,
56.78
f
),
0.0
f
);
...
@@ -248,7 +204,7 @@ void CV_RotatedRectangleIntersectionTest::test7()
...
@@ -248,7 +204,7 @@ void CV_RotatedRectangleIntersectionTest::test7()
compare
(
vertices
,
targetVertices
);
compare
(
vertices
,
targetVertices
);
}
}
void
CV_RotatedRectangleIntersectionTest
::
test8
(
)
TEST
(
Imgproc_RotatedRectangleIntersection
,
accuracy_8
)
{
{
// intersection by a single vertex
// intersection by a single vertex
RotatedRect
rect1
(
Point2f
(
0
,
0
),
Size2f
(
2
,
2
),
0.0
f
);
RotatedRect
rect1
(
Point2f
(
0
,
0
),
Size2f
(
2
,
2
),
0.0
f
);
...
@@ -261,7 +217,7 @@ void CV_RotatedRectangleIntersectionTest::test8()
...
@@ -261,7 +217,7 @@ void CV_RotatedRectangleIntersectionTest::test8()
compare
(
vertices
,
vector
<
Point2f
>
(
1
,
Point2f
(
1.0
f
,
1.0
f
)));
compare
(
vertices
,
vector
<
Point2f
>
(
1
,
Point2f
(
1.0
f
,
1.0
f
)));
}
}
void
CV_RotatedRectangleIntersectionTest
::
test9
(
)
TEST
(
Imgproc_RotatedRectangleIntersection
,
accuracy_9
)
{
{
// full intersection, rectangle fully enclosed in the other
// full intersection, rectangle fully enclosed in the other
RotatedRect
rect1
(
Point2f
(
0
,
0
),
Size2f
(
2
,
2
),
0.0
f
);
RotatedRect
rect1
(
Point2f
(
0
,
0
),
Size2f
(
2
,
2
),
0.0
f
);
...
@@ -278,7 +234,7 @@ void CV_RotatedRectangleIntersectionTest::test9()
...
@@ -278,7 +234,7 @@ void CV_RotatedRectangleIntersectionTest::test9()
compare
(
vertices
,
targetVertices
);
compare
(
vertices
,
targetVertices
);
}
}
void
CV_RotatedRectangleIntersectionTest
::
test10
(
)
TEST
(
Imgproc_RotatedRectangleIntersection
,
accuracy_10
)
{
{
// three points of rect2 are inside rect1.
// three points of rect2 are inside rect1.
RotatedRect
rect1
(
Point2f
(
0
,
0
),
Size2f
(
2
,
2
),
0.0
f
);
RotatedRect
rect1
(
Point2f
(
0
,
0
),
Size2f
(
2
,
2
),
0.0
f
);
...
@@ -298,7 +254,7 @@ void CV_RotatedRectangleIntersectionTest::test10()
...
@@ -298,7 +254,7 @@ void CV_RotatedRectangleIntersectionTest::test10()
compare
(
vertices
,
targetVertices
);
compare
(
vertices
,
targetVertices
);
}
}
void
CV_RotatedRectangleIntersectionTest
::
test11
(
)
TEST
(
Imgproc_RotatedRectangleIntersection
,
accuracy_11
)
{
{
RotatedRect
rect1
(
Point2f
(
0
,
0
),
Size2f
(
4
,
2
),
0.0
f
);
RotatedRect
rect1
(
Point2f
(
0
,
0
),
Size2f
(
4
,
2
),
0.0
f
);
RotatedRect
rect2
(
Point2f
(
0
,
0
),
Size2f
(
2
,
2
),
-
45.0
f
);
RotatedRect
rect2
(
Point2f
(
0
,
0
),
Size2f
(
2
,
2
),
-
45.0
f
);
...
@@ -318,7 +274,7 @@ void CV_RotatedRectangleIntersectionTest::test11()
...
@@ -318,7 +274,7 @@ void CV_RotatedRectangleIntersectionTest::test11()
compare
(
vertices
,
targetVertices
);
compare
(
vertices
,
targetVertices
);
}
}
void
CV_RotatedRectangleIntersectionTest
::
test12
(
)
TEST
(
Imgproc_RotatedRectangleIntersection
,
accuracy_12
)
{
{
RotatedRect
rect1
(
Point2f
(
0
,
0
),
Size2f
(
2
,
2
),
0.0
f
);
RotatedRect
rect1
(
Point2f
(
0
,
0
),
Size2f
(
2
,
2
),
0.0
f
);
RotatedRect
rect2
(
Point2f
(
0
,
1
),
Size2f
(
1
,
1
),
0.0
f
);
RotatedRect
rect2
(
Point2f
(
0
,
1
),
Size2f
(
1
,
1
),
0.0
f
);
...
@@ -336,7 +292,7 @@ void CV_RotatedRectangleIntersectionTest::test12()
...
@@ -336,7 +292,7 @@ void CV_RotatedRectangleIntersectionTest::test12()
compare
(
vertices
,
targetVertices
);
compare
(
vertices
,
targetVertices
);
}
}
void
CV_RotatedRectangleIntersectionTest
::
test13
(
)
TEST
(
Imgproc_RotatedRectangleIntersection
,
accuracy_13
)
{
{
RotatedRect
rect1
(
Point2f
(
0
,
0
),
Size2f
(
1
,
3
),
0.0
f
);
RotatedRect
rect1
(
Point2f
(
0
,
0
),
Size2f
(
1
,
3
),
0.0
f
);
RotatedRect
rect2
(
Point2f
(
0
,
1
),
Size2f
(
3
,
1
),
0.0
f
);
RotatedRect
rect2
(
Point2f
(
0
,
1
),
Size2f
(
3
,
1
),
0.0
f
);
...
@@ -354,7 +310,7 @@ void CV_RotatedRectangleIntersectionTest::test13()
...
@@ -354,7 +310,7 @@ void CV_RotatedRectangleIntersectionTest::test13()
compare
(
vertices
,
targetVertices
);
compare
(
vertices
,
targetVertices
);
}
}
void
CV_RotatedRectangleIntersectionTest
::
test14
(
)
TEST
(
Imgproc_RotatedRectangleIntersection
,
accuracy_14
)
{
{
const
int
kNumTests
=
100
;
const
int
kNumTests
=
100
;
const
float
kWidth
=
5
;
const
float
kWidth
=
5
;
...
@@ -376,6 +332,38 @@ void CV_RotatedRectangleIntersectionTest::test14()
...
@@ -376,6 +332,38 @@ void CV_RotatedRectangleIntersectionTest::test14()
}
}
}
}
TEST
(
Imgproc_RotatedRectangleIntersection
,
accuracy
)
{
CV_RotatedRectangleIntersectionTest
test
;
test
.
safe_run
();
}
TEST
(
Imgproc_RotatedRectangleIntersection
,
regression_12221_1
)
{
RotatedRect
r1
(
Point2f
(
259.65081787109375
,
51.58895492553711
),
Size2f
(
5487.8779296875
,
233.8921661376953
),
-
29.488616943359375
);
RotatedRect
r2
(
Point2f
(
293.70465087890625
,
112.10154724121094
),
Size2f
(
5487.8896484375
,
234.87368774414062
),
-
31.27001953125
);
std
::
vector
<
Point2f
>
intersections
;
int
interType
=
cv
::
rotatedRectangleIntersection
(
r1
,
r2
,
intersections
);
EXPECT_EQ
(
INTERSECT_PARTIAL
,
interType
);
EXPECT_LE
(
intersections
.
size
(),
(
size_t
)
8
);
}
TEST
(
Imgproc_RotatedRectangleIntersection
,
regression_12221_2
)
{
RotatedRect
r1
(
Point2f
(
239.78500366210938
,
515.72021484375
),
Size2f
(
70.23420715332031
,
39.74684524536133
),
-
42.86162567138672
);
RotatedRect
r2
(
Point2f
(
242.4205322265625
,
510.1195373535156
),
Size2f
(
66.85948944091797
,
61.46455383300781
),
-
9.840961456298828
);
std
::
vector
<
Point2f
>
intersections
;
int
interType
=
cv
::
rotatedRectangleIntersection
(
r1
,
r2
,
intersections
);
EXPECT_EQ
(
INTERSECT_PARTIAL
,
interType
);
EXPECT_LE
(
intersections
.
size
(),
(
size_t
)
8
);
}
}}
// namespace
}}
// namespace
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录