Я работаю над оптимизацией и хочу преобразовать некоторые части с python на c++.
Можно ли преобразовать этот код в C++ с помощью opencv
?
В коде Python используется numpy
import numpy as np
from PIL import Image
pil_img = Image.open(input_filename)
img = np.array(pil_img)
pixels = img.reshape((-1, 3))
num_pixels = pixels.shape[0]
num_samples = int(num_pixels*5)
idx = np.arange(num_pixels)
np.random.shuffle(idx)
samples = pixels[idx[:num_samples]]
std::vector<uchar> sample_pixels(const cv::Mat& m, int sample_percent=5){
assert(m.isContinuous());
const auto* input = m.ptr<uchar>();
int
num_pixels = m.total(),
num_samples = num_pixels * sample_percent;
std::cout
<< "num pixels: " << num_pixels << '\n'
<< "num samples: " << num_samples << '\n';
std::vector<uchar> samples(num_samples);
// Fills idx with sequentially increasing values
std::vector<int> idx(num_pixels);
std::iota(idx.begin(), idx.end(), 0);
// Shuffle idx
std::mt19937 engine(0);
std::shuffle(idx.begin(), idx.end(), engine);
for(int i = 0; i < num_samples; i++){
//samples[i] = input[idx[i]];
}
//auto output_mat = cv::Mat(samples, false);
//cv::imwrite("enhance-samples.png", output_mat);
return samples;
}
да это возможно. Однако вы можете не получить никакого улучшения производительности.
opencv
можно использовать с python
, а также с его родным C++. Таким образом, первым шагом может быть замена использования PIL на opencv
. Существует множество примеров SO с использованием opencv
. Любое дальнейшее использование С++ зависит от вашего знакомства с языком.
Это эквивалентный код в C++11. Это должно быть в несколько раз быстрее, чем ваш код на Python.
#include <random>
#include <numeric>
#include <opencv2/opencv.hpp>
void shuffling(const std::string &input_filename, const std::string &output_filename) {
// ========== UPDATE ==========
const cv::Mat plain_input_mat = cv::imread(input_filename, -1);
// Equivalent to img.reshape((-1, 3))
const cv::Mat input_mat = plain_input_mat.reshape(3);
// ============================
// By doing this, you can access the pixels without any extra checks.
assert(input_mat.isContinuous());
const auto *input = input_mat.ptr<cv::Vec3b>();
const auto num_samples = input_mat.total();
std::vector<cv::Vec3b> output(num_samples);
std::vector<int> idx(input_mat.total());
std::iota(idx.begin(), idx.end(), 0); // Equivalent to arange.
// Note: numpy uses PCG64 which does not exist in the std library.
std::mt19937 engine(0);
std::shuffle(idx.begin(), idx.end(), engine);
for (int i = 0; i < num_samples; i++) {
output[i] = input[idx[i]];
}
// Save as an image if necessary.
auto output_mat = cv::Mat(output, false);
cv::imwrite(output_filename, output_mat);
}
Есть пара дополнительных замечаний.
Примечание 1. Из-за разницы в алгоритме перетасовки между python и std результаты не совсем совпадают.
Примечание 2. В вашем коде num_samples
не может быть больше, чем количество пикселей во входном изображении, что кажется ошибкой. Пожалуйста, проверьте длину samples
.
Примечание 3: В обеих реализациях самая дорогая часть — shuffle
. Здесь тратится 60% на python и более 80% на C++. Если вы хотите оптимизировать дальнейшую оптимизацию, это определенно то, что вам следует использовать.
Я смотрю на ваш код. Что делает input_mat.ptr<cv::Vec3b>();
? Будет ли он преобразовывать матрицу в указатель и будет ли это <cv::Vec3b>
своего рода кастингом? Приведет ли он матрицу к CV_8UC1
?
Хорошо, после некоторых исследований выяснилось, что cv::Vec3b
- это 3 канала :) Внесли обновление в вопрос с некоторыми изменениями. Изменено cv::Vec3b
на uchar
, потому что вход представляет собой 1 канал в оттенках серого. Но есть проблема с кодом. Если samples[i] = input[idx[i]];
раскомментирован, код возвращает ошибку
Извините за задержку с ответом. input_mat.ptr<cv::Vec3b>()
приводит указатель к CV_8UC3 (не преобразование). Это не должно соответствовать формату входного изображения, а скорее тому, как вы хотите его обрабатывать. В вашем коде Python вы изменяете изображение, оставляя 3 канала, это означает, что 3 элемента будут скопированы за одно назначение. Если вы хотите такое поведение, оставьте его как Vec3b
. Вместо этого вы должны изменить форму ввода так же, как ваш код Python. Смотрите мое обновление.
Следует отметить две вещи: во-первых, это изменение допустимо только в том случае, если количество пикселей во входных данных кратно 3 (как в коде Python). Во-вторых, вы определяете num_samples
как num_pixels * sample_percent
, но это в 5 раз больше num_pixels
, а не 5 процентов. Это вызывает внеиндексацию (так же, как и в коде Python, поэтому вы должны сначала отладить его).
Извините, что пришлось 0.05
(5%).. Спасибо за ваше время :)
а надо ли делать plain_input_mat.reshape(3);
когда делаешь input_mat.ptr<cv::Vec3b>()
??
mat.reshape(3)
изменяет только информацию о форме, чтобы mat.total()
возвращал правильное значение. Таким образом, вы можете удалить mat.reshape(3)
и настроить эти значения вручную, например mat.total() / 3
. Но я думаю, что mat.reshape(3)
было бы намного проще.
Разве Numpy в основном не BLAS?