Skip to content

Commit ffd3a7b

Browse files
generatedunixname2124183968404147meta-codesync[bot]
authored andcommitted
xplat/ocean/impl/ocean/cv/FrameInverter.cpp
Differential Revision: D110163079 fbshipit-source-id: 7fa437a65423f0e9a5406a6517058ec1e125b1fa
1 parent d03a017 commit ffd3a7b

1 file changed

Lines changed: 165 additions & 0 deletions

File tree

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include <gtest/gtest.h>
9+
10+
#include "ocean/cv/FrameInverter.h"
11+
12+
#include <cstdint>
13+
#include <vector>
14+
15+
using namespace Ocean;
16+
using namespace Ocean::CV;
17+
18+
namespace {
19+
20+
// Build a single-plane Y8 Frame with deterministic content equal to
21+
// (x + y * width) modulo 256. This exercises both the Frame data layout and
22+
// the FrameInverter::invert pixel transform.
23+
Frame makeY8Frame(unsigned int width, unsigned int height)
24+
{
25+
Frame frame(FrameType(width, height, FrameType::FORMAT_Y8, FrameType::ORIGIN_UPPER_LEFT));
26+
for (unsigned int y = 0u; y < height; ++y)
27+
{
28+
uint8_t* row = frame.row<uint8_t>(y);
29+
for (unsigned int x = 0u; x < width; ++x)
30+
{
31+
row[x] = static_cast<uint8_t>((x + y * width) & 0xFFu);
32+
}
33+
}
34+
return frame;
35+
}
36+
37+
} // namespace
38+
39+
// FrameInverter + Frame integration: invert a Y8 frame into a separate target
40+
// Frame and verify every output pixel equals 0xFF - source pixel.
41+
TEST(FrameInverterFrameIntegrationTest, InvertY8FrameProducesComplementedPixels)
42+
{
43+
constexpr unsigned int kWidth = 17u;
44+
constexpr unsigned int kHeight = 11u;
45+
46+
Frame source = makeY8Frame(kWidth, kHeight);
47+
Frame target;
48+
49+
ASSERT_TRUE(FrameInverter::invert(source, target, /*worker=*/nullptr));
50+
ASSERT_TRUE(target.isValid());
51+
EXPECT_EQ(target.width(), kWidth);
52+
EXPECT_EQ(target.height(), kHeight);
53+
EXPECT_EQ(target.channels(), 1u);
54+
EXPECT_EQ(target.pixelFormat(), FrameType::FORMAT_Y8);
55+
56+
for (unsigned int y = 0u; y < kHeight; ++y)
57+
{
58+
const uint8_t* srcRow = source.constrow<uint8_t>(y);
59+
const uint8_t* dstRow = target.constrow<uint8_t>(y);
60+
for (unsigned int x = 0u; x < kWidth; ++x)
61+
{
62+
EXPECT_EQ(dstRow[x], static_cast<uint8_t>(0xFF - srcRow[x]))
63+
<< "Mismatch at (" << x << ", " << y << ")";
64+
}
65+
}
66+
}
67+
68+
// FrameInverter in-place + Frame integration: applying invert twice on the
69+
// same Frame must round-trip back to the original pixel values, and timestamp
70+
// metadata must be preserved across the in-place operation.
71+
TEST(FrameInverterFrameIntegrationTest, InvertInPlaceTwiceRoundTripsAndPreservesTimestamp)
72+
{
73+
constexpr unsigned int kWidth = 8u;
74+
constexpr unsigned int kHeight = 6u;
75+
76+
Frame original = makeY8Frame(kWidth, kHeight);
77+
Frame frame = makeY8Frame(kWidth, kHeight);
78+
79+
const Timestamp ts(123.5);
80+
const Timestamp relativeTs(0.25);
81+
frame.setTimestamp(ts);
82+
frame.setRelativeTimestamp(relativeTs);
83+
84+
ASSERT_TRUE(FrameInverter::invert(frame, /*worker=*/nullptr));
85+
EXPECT_EQ(frame.timestamp(), ts);
86+
EXPECT_EQ(frame.relativeTimestamp(), relativeTs);
87+
88+
// After a single inversion the bytes must differ from the original for at
89+
// least one non-zero source pixel.
90+
bool sawDifference = false;
91+
for (unsigned int y = 0u; y < kHeight && !sawDifference; ++y)
92+
{
93+
const uint8_t* origRow = original.constrow<uint8_t>(y);
94+
const uint8_t* curRow = frame.constrow<uint8_t>(y);
95+
for (unsigned int x = 0u; x < kWidth; ++x)
96+
{
97+
if (origRow[x] != curRow[x])
98+
{
99+
sawDifference = true;
100+
break;
101+
}
102+
}
103+
}
104+
EXPECT_TRUE(sawDifference);
105+
106+
ASSERT_TRUE(FrameInverter::invert(frame, /*worker=*/nullptr));
107+
EXPECT_EQ(frame.timestamp(), ts);
108+
EXPECT_EQ(frame.relativeTimestamp(), relativeTs);
109+
110+
for (unsigned int y = 0u; y < kHeight; ++y)
111+
{
112+
const uint8_t* origRow = original.constrow<uint8_t>(y);
113+
const uint8_t* curRow = frame.constrow<uint8_t>(y);
114+
for (unsigned int x = 0u; x < kWidth; ++x)
115+
{
116+
EXPECT_EQ(curRow[x], origRow[x])
117+
<< "Round-trip mismatch at (" << x << ", " << y << ")";
118+
}
119+
}
120+
}
121+
122+
// FrameInverter raw-buffer overload integration: drive the public
123+
// invert8BitPerChannel entry point (which internally dispatches to
124+
// invert8BitPerChannelSubset) and confirm it handles padding strides
125+
// correctly when source and target have different padding.
126+
TEST(FrameInverterFrameIntegrationTest, Invert8BitPerChannelSubsetRespectsPadding)
127+
{
128+
constexpr unsigned int kHorizontal = 10u;
129+
constexpr unsigned int kRows = 4u;
130+
constexpr unsigned int kSrcPadding = 3u;
131+
constexpr unsigned int kDstPadding = 5u;
132+
133+
const unsigned int srcStride = kHorizontal + kSrcPadding;
134+
const unsigned int dstStride = kHorizontal + kDstPadding;
135+
136+
std::vector<uint8_t> srcBuf(srcStride * kRows, 0u);
137+
std::vector<uint8_t> dstBuf(dstStride * kRows, 0xAAu); // sentinel pattern
138+
139+
for (unsigned int y = 0u; y < kRows; ++y)
140+
{
141+
for (unsigned int x = 0u; x < kHorizontal; ++x)
142+
{
143+
srcBuf[y * srcStride + x] = static_cast<uint8_t>((x * 7u + y * 13u) & 0xFFu);
144+
}
145+
}
146+
147+
FrameInverter::invert8BitPerChannel(
148+
srcBuf.data(), dstBuf.data(), /*width=*/kHorizontal, /*height=*/kRows,
149+
/*channels=*/1u, kSrcPadding, kDstPadding, /*worker=*/nullptr);
150+
151+
for (unsigned int y = 0u; y < kRows; ++y)
152+
{
153+
for (unsigned int x = 0u; x < kHorizontal; ++x)
154+
{
155+
EXPECT_EQ(dstBuf[y * dstStride + x],
156+
static_cast<uint8_t>(0xFF - srcBuf[y * srcStride + x]))
157+
<< "Active pixel mismatch at (" << x << ", " << y << ")";
158+
}
159+
for (unsigned int x = kHorizontal; x < dstStride; ++x)
160+
{
161+
EXPECT_EQ(dstBuf[y * dstStride + x], 0xAAu)
162+
<< "Padding byte must not be written at (" << x << ", " << y << ")";
163+
}
164+
}
165+
}

0 commit comments

Comments
 (0)