我一直在自学C++,最近我创建了一个使用X张量库的简单Image类。使用我的Image类,用户可以轻松地读取和写入PNG或JPG图像,并将RGB图像转换为灰度图像。
如果您感兴趣,您可以在这个存储库中为自己检查代码。请原谅代码中的大量评论--我还在学习C++。
Image类声明
为了便于阅读和查看这里的内容,我从代码中删除了一些广泛的注释。
#ifndef IMAGE_XTENSOR_HPP
#define IMAGE_XTENSOR_HPP
#include
#include
#include
#include
#include
namespace mypackage::image {
struct ImageXTensor {
ImageXTensor();
explicit ImageXTensor(std::string file_path);
explicit ImageXTensor(int c, int h, int w);
explicit ImageXTensor(const xt::xtensor &input_matrix);
explicit ImageXTensor(const ImageXTensor &other);
ImageXTensor(ImageXTensor &&other);
ImageXTensor &operator=(const ImageXTensor &other);
ImageXTensor &operator=(ImageXTensor &&other);
~ImageXTensor();
bool operator==(const ImageXTensor &other) const;
int channels; // aka comp in std_image
int height;
int width;
int size;
std::unique_ptr> pixels;
bool save(std::string file_path);
void swap(ImageXTensor &other);
};
ImageXTensor rgb_to_grayscale_xtensor(const ImageXTensor &img);
xt::xarray rgb_to_grayscale_xtensor(const xt::xarray &pixels);
} // namespace mypackage::image
#endif我希望您发现Image类的声明很容易理解。但是,我有一个关于move构造函数ImageXTensor(ImageXTensor &&other);的问题。如果我将explicit关键字附加到移动构造函数(如explicit ImageXTensor(ImageXTensor &&other); ),将导致如下所示的编译错误:
/home/lai/cmake_template/src/mypackage/image/image_xtensor.cpp: In function ‘mypackage::image::ImageXTensor mypackage::image::rgb_to_grayscale_xtensor(const mypackage::image::ImageXTensor&)’:
/home/lai/cmake_template/src/mypackage/image/image_xtensor.cpp:309:10: error: no matching function for call to ‘mypackage::image::ImageXTensor::ImageXTensor(mypackage::image::ImageXTensor&)’
309 | return gray;
| ^~~~
/home/lai/cmake_template/src/mypackage/image/image_xtensor.cpp:18:1: note: candidate: ‘mypackage::image::ImageXTensor::ImageXTensor()’
18 | ImageXTensor::ImageXTensor()
| ^~~~~~~~~~~~
/home/lai/cmake_template/src/mypackage/image/image_xtensor.cpp:18:1: note: candidate expects 0 arguments, 1 provided
make[2]: *** [src/mypackage/image/CMakeFiles/image_xtensor.dir/build.make:76: src/mypackage/image/CMakeFiles/image_xtensor.dir/image_xtensor.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:506: src/mypackage/image/CMakeFiles/image_xtensor.dir/all] Error 2
make: *** [Makefile:146: all] Error 2我试图从这个所以问题中理解这个错误,但我仍然不太清楚为什么会发生这种情况。我知道explicit是用来防止编译器进行隐式转换的,而且由于相信显式总是比隐式好,所以我认为在任何地方使用显式都应该是首选的。因此,我将感谢您的任何见解或建议。
图像类的K220定义
以下是Image类的完整定义。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace mypackage::image {
ImageXTensor::ImageXTensor()
: channels{0}, height{0}, width{0}, size{0}, pixels{nullptr} {
std::clog << "The default constructor takes no paramters.\n";
}
ImageXTensor::ImageXTensor(std::string file_path) {
std::clog << "The constructor takes a file path.\n";
unsigned char *img_data =
stbi_load(file_path.c_str(), &width, &height, &channels, 0);
if (img_data == nullptr) {
const char *error_msg = stbi_failure_reason();
std::cerr << "Failed to load image: " << file_path.c_str() << "\n";
std::cerr << "Error msg (stb_image): " << error_msg << "\n";
std::exit(1);
}
pixels = std::make_unique>(
xt::zeros({channels, height, width}));
size = pixels->size();
std::clog << "The image shape: " << channels << " x " << height << " x "
<< width << '\n';
assert(size == channels * height * width);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
for (int c = 0; c < channels; c++) {
// PNG's pixels order is mysterious for me.
std::size_t src_idx = y * width * channels + x * channels + c;
// Rescale uint8 to float 0-1.
(*pixels)(c, y, x) = img_data[src_idx] / 255.;
}
}
}
if (channels == 4)
channels = 3; // ignore alpha channel
stbi_image_free(img_data);
}
ImageXTensor::ImageXTensor(int c, int h, int w)
: channels{c}, height{h}, width{w}, size{c * h * w},
pixels{std::make_unique>(
xt::zeros({c, h, w}))} {}
ImageXTensor::ImageXTensor(const xt::xtensor &input_matrix) {
channels = input_matrix.shape(0);
height = input_matrix.shape(1);
width = input_matrix.shape(2);
size = input_matrix.size();
pixels = std::make_unique>(input_matrix);
}
ImageXTensor::ImageXTensor(const ImageXTensor &other)
: channels{other.channels}, height{other.height}, width{other.width},
size{other.size}, pixels{std::make_unique>(
xt::zeros(
{other.channels, other.height, other.width}))} {
std::clog << "Copy Constructor\n";
*pixels = *other.pixels;
}
ImageXTensor &ImageXTensor::operator=(const ImageXTensor &other) {
std::clog << "Copy Assignment Operator\n";
if (this != &other) {
channels = other.channels;
height = other.height;
width = other.width;
size = other.size;
pixels = std::make_unique>(
xt::zeros({other.channels, other.height, other.width}));
*pixels = *other.pixels;
}
return *this;
}
ImageXTensor::ImageXTensor(ImageXTensor &&other)
: channels{other.channels}, height{other.height}, width{other.width},
size{other.size}, pixels{std::move(other.pixels)} {
std::clog << "Move Constructor\n";
swap(other);
}
ImageXTensor &ImageXTensor::operator=(ImageXTensor &&other) {
std::clog << "Move Assignment Operator\n";
swap(other);
return *this;
}
ImageXTensor::~ImageXTensor() { std::clog << "Destruct Image.\n"; }
bool ImageXTensor::operator==(const ImageXTensor &other) const {
return (width == other.width) && (height == other.height) &&
(channels == other.channels) && (size == other.size) &&
(*pixels == *other.pixels);
}
bool ImageXTensor::save(std::string file_path) {
auto file_extension = std::filesystem::path(file_path).extension();
unsigned char *out_data = new unsigned char[width * height * channels];
/** NOTE: There seems to be no easy way to unfold a 3D array into a 1D array
* with the desired order.
*/
for (auto x = 0; x < width; x++) {
for (auto y = 0; y < height; y++) {
for (auto c = 0; c < channels; c++) {
int dst_idx = y * width * channels + x * channels + c;
// Fill out_data with uint8 values range 0-255.
out_data[dst_idx] = std::roundf((*pixels)(c, y, x) * 255.);
}
}
}
bool success{false};
if (file_extension == std::string(".jpg") ||
file_extension == std::string(".JPG")) {
auto quality = 100;
success = stbi_write_jpg(file_path.c_str(), width, height, channels,
out_data, quality);
} else if (file_extension == std::string(".png") ||
file_extension == std::string(".png")) {
auto stride_in_bytes = width * channels;
success = stbi_write_png(file_path.c_str(), width, height, channels,
out_data, stride_in_bytes);
} else {
std::cerr << "Unsupported file format: " << file_extension << "\n";
}
if (!success)
std::cerr << "Failed to save image: " << file_path << "\n";
delete[] out_data;
return true;
}
void ImageXTensor::swap(ImageXTensor &other) {
std::swap(channels, other.channels);
std::swap(height, other.height);
std::swap(width, other.width);
std::swap(size, other.size);
std::swap(pixels, other.pixels);
}
ImageXTensor rgb_to_grayscale_xtensor(const ImageXTensor &img) {
assert(img.channels >= 3);
ImageXTensor gray(1, img.height, img.width);
xt::xarray red = xt::view(*img.pixels, 0, xt::all(), xt::all());
xt::xarray green = xt::view(*img.pixels, 1, xt::all(), xt::all());
xt::xarray blue = xt::view(*img.pixels, 2, xt::all(), xt::all());
xt::view(*gray.pixels, 0, xt::all(), xt::all()) =
0.299 * red + 0.587 * green + 0.114 * blue;
return gray;
}
xt::xarray rgb_to_grayscale_xtensor(const xt::xarray &pixels) {
assert(pixels.shape(0) >= 3);
auto height = pixels.shape(1);
auto width = pixels.shape(2);
xt::xarray::shape_type shape = {1, height, width};
xt::xarray gray(shape);
xt::xarray red = xt::view(pixels, 0, xt::all(), xt::all());
xt::xarray green = xt::view(pixels, 1, xt::all(), xt::all());
xt::xarray blue = xt::view(pixels, 2, xt::all(), xt::all());
xt::view(gray, 0, xt::all(), xt::all()) =
0.299 * red + 0.587 * green + 0.114 * blue;
return gray;
}
} // namespace mypackage::image我希望您发现Image类的实现是简单明了的。
Image类(您可以找到代码这里)rgb_to_grayscale_xtensor:bool rgb2gray_image_xtensor(std::string input, std::string output) {
mypackage::image::ImageXTensor in_img{input};
auto out_img = mypackage::image::rgb_to_grayscale_xtensor(in_img);
out_img.save(output);
return 0;
}xt::xarray传递给rgb_to_grayscale_xtensor:bool rgb2gray_image_xtensor_PassByTensor(std::string input,
std::string output) {
mypackage::image::ImageXTensor in_img{input};
mypackage::image::ImageXTensor out_img{in_img.channels, in_img.height,
in_img.width};
*out_img.pixels = mypackage::image::rgb_to_grayscale_xtensor(*in_img.pixels);
out_img.save(output);
return 0;
}#include
#include
#include
#include
#include
TEST(ImageXTensor, ClassAssertion) {
mypackage::image::ImageXTensor test_img1(2, 3, 4);
int counter = 0;
for (int x = 0; x < test_img1.width; x++) {
for (int y = 0; y < test_img1.height; y++) {
for (int c = 0; c < test_img1.channels; c++) {
(*test_img1.pixels)(c, y, x) = counter;
++counter;
}
}
}
std::clog << "test_img1:\n" << (*test_img1.pixels) << '\n';
std::clog << "Test Copy Constructor.\n";
mypackage::image::ImageXTensor test_img2{test_img1};
std::clog << "test_img2:\n" << (*test_img2.pixels) << '\n';
EXPECT_EQ(test_img2, test_img1);
std::clog << "Test Copy Assignment Operator.\n";
mypackage::image::ImageXTensor test_img3;
test_img3 = test_img1;
std::clog << "test_img3\n" << (*test_img3.pixels) << '\n';
EXPECT_EQ(test_img3, test_img1);
std::clog << "Test Move Assignment Operator.\n";
mypackage::image::ImageXTensor test_img4;
test_img4 = std::move(test_img1);
// test_img1 becomes unspecified after it's moved from.
EXPECT_EQ(test_img1.pixels, nullptr);
EXPECT_EQ(test_img4, test_img2);
}由于我仍然在学习C++,所以我试图专注于现代C++,并坚持最佳的编码实践。如果不是太麻烦的话,我希望您能够考虑到现代C++ (尤其是来自C++17和其他地方的代码)的代码。此外,任何关于以下C++最佳编码实践的提示都将不胜感激。谢谢。
发布于 2023-04-10 20:52:52
https://codereview.stackexchange.com/questions/284388
复制相似问题