Мне одному кажется, что столько преобразований излишни? Ведь OpenCV напрямую позволяет работать со всеми форматами и можно сделать преобразование напрямую!
В моём случае я работаю с форматом видео YUV420P.
if ( m_picture->format == AV_PIX_FMT_YUV420P ) {
int numBytes=avpicture_get_size(AV_PIX_FMT_YUV420P, m_picture->width, m_picture->height);
uint8_t * buffer = reinterpret_cast<uint8_t *>(av_malloc(numBytes));
avpicture_layout( ( AVPicture*)m_picture, AV_PIX_FMT_YUV420P, m_picture->width, m_picture->height, buffer, numBytes); //av_image_copy_to_buffer() в новом API
Mat yuv = Mat(m_picture->height+m_picture->height/2, m_picture->width, CV_8UC1, buffer );
Mat gray = Mat(m_picture->height, m_picture->width, CV_8UC1, buffer ); //-- Если нужно получить сразу в градациях серого (формат YUV содержит в первом канале как раз в градациях серого кадр)
Mat rgb = Mat(m_picture->height, m_picture->width, CV_8UC3);
cvtColor(yuv, rgb, CV_YUV420p2RGB);
imshow("RGB", rgb);
imshow("GRAY", gray);
}
ВАЖНО: Не забудьте очистить память из под buffer, так как после удаления Mat она не очищается самостоятельно!
В вашем случае замените форматы на те, с которыми работаете.
В общем потребовалось восстановить перспективу картинки,
на примере этой:
Как обычно — нашли 4 точки на картинке, в данном случае — углы листа, по часовой стрелке, начиная с верхнего левого:
[20, 340]
[860,110]
[1160, 650]
[200, 950]
Хотим, что бы лист располагался прямо, а для этого мы знаем, что ширина и высота листа 870 на 620 пикселей, аналогично координаты по часовой стрелке, начиная с верхнего левого:
[0, 0]
[870, 0]
[870, 620]
[0, 620]
в данном случае левый верхний угол располагается в нуле (где ещё то), а правый нижний строго по горизонтали и вертикали (мы ведь хотим расположить прямо)
/*
* Пока что всё как обычно
* открываем картинку,
* задаём найденные и целевые точки,
*/
Mat input;
input = imread("/home/pavelk/Projects/OpenCVwrapPerspective/sheet.jpg");
Point2f inputQuad[4];
inputQuad[0] = Point2f( 20, 340 );
inputQuad[1] = Point2f( 860,110 );
inputQuad[2] = Point2f( 1160, 650 );
inputQuad[3] = Point2f( 200, 950 );
Point2f outputQuad[4];
outputQuad[0] = Point2f( 0, 0 );
outputQuad[1] = Point2f( 870, 0 );
outputQuad[2] = Point2f( 870, 620 );
outputQuad[3] = Point2f( 0, 620 );
/*
* Находим матрицу трансформации
*/
Mat M = getPerspectiveTransform( inputQuad, outputQuad );
/*
* Теперь применяем трансформацию на картинке с помощью warpPerspective
*/
Mat output;
warpPerspective(input, output, M, Size(2000, 2000));
Но тут сталкиваемся с первой засадой — нужно задать итоговый размер, а его мы до трансформации не знаем =( Ну ладно, попробуем пока что подгадать.
Посмотрим на вывод:
Как видим, перспективу то мы исправили, но что если нам так же нужна вся плоскость картинки, а не только сам лист (например потому, что точки мы можем найти только с помощью этого объекта, а нам нужен абсолютно другой)?
Да и с определением итогового размера можно не угадать, а заранее задавать слишком большой — тормоза для последующих обработок.
Почему так происходит?
Потому, что когда мы задали целевую точку верхнего левого угла [0, 0], то при трансформации получается (см. формулу warpPerspective), что нужно взять позицию с отрицательными координатами с картинки оригинала, а откуда на ней взяться отрицательному-то значению… Вот и получается обрезка. Можно, конечно, заранее подгадать смещение целевых точек, что бы не было отрицательных позиций, но мы не знаем, как может повернуться исходная картинка, да и проблему с размером результата мы не решим да и вообще гадать — не мой метод.
Что будем делать?
Как следует из написанного выше — нам нужно сместить целевые точки, поэтому будем выяснять насколько их смещать, а для этого нам нужно выяснить, где окажется левый верхний угол после трансформации. Но! Не забываем, что исходная картинка может быть как угодно повёрнута, и левый нижний окажется дальше в минусе, чем верхний левый. Поэтому выясняем положение после трансформации всех четырёх углов исходной картинки, а что бы не геммороиться с определением где какой угол — найдём их ограничительную рамку, благо для этого в OpenCV есть метод boundingRect и сместим целевые точки в обратную сторону от её позиции, а бонусом по её размеру мы знаем размер итоговой картинки=)
Покодим:
/**
* Выясняем положение углов исходной картинке,
* по ширине и высоте
*/
vector<Point2f> inputCorners(4);
inputCorners[0]=Point2f(0, 0);
inputCorners[1]=Point2f(input.cols, 0);
inputCorners[2]=Point2f(0, input.rows);
inputCorners[3]=Point2f(input.cols, input.rows);
/*
* Выясняем, где они будут - применяем трансформацию
*/
vector<Point2f> outputCorners(4);
perspectiveTransform(inputCorners, outputCorners, M);
/*
* Находим ограничительную рамку
*/
Rect br= boundingRect(outputCorners);
/*
* Сдвигаем все целевые точки в противоположное направление,
* от того, куда ушла ограничительная рамка
*/
for(int i=0; i<4; i++) {
outputQuad[i]+=Point2f(-br.x,-br.y);
}
/*
* Ну и заново вычисляем матрицу трансформацию
* с новыми целевыми точками
*/
M = getPerspectiveTransform( inputQuad, outputQuad );
/*
* Применяем трансформацию к картинке
* размер - как ограничительная рамка
*/
warpPerspective(input, output, M, br.size());
/*
* Ну и показываем итоговую картинку
*/
imshow("Output2", output);
#include <QApplication>
#include <QDebug>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui.hpp"
using namespace std;
using namespace cv;
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
qDebug()<<"Hello world";
//-- Выбираем первую попавшуюся камеру
VideoCapture cap(0);
//-- Проверяем, удалось ли подключиться
if (!cap.isOpened()) {
qDebug()<<"Camera not opened!";
return 0;
}
//-- Выставляем параметры камеры ширину и высоту кадра в пикселях
cap.set(CAP_PROP_FRAME_WIDTH, 1280);
cap.set(CAP_PROP_FRAME_HEIGHT, 960);
Mat frame;
while (true) {
cap >> frame; //-- захватываем очередной кадр
imshow("Video", frame); //-- показываем его
char c = waitKey(33); //-- если была нажата клавиша, узнаём её код
if (c==27) { //-- нажата ESC, прерываем цикл
break;
}
}
return app.exec();
}
CMake (Должен быть 32х битным, да же если система 64!)
Для удобства, основной путь у меня такой: E:\Projects\OpenCV и в дальнейшем я отталкиваюсь именно от этой папки (В неё же и ставите OpenCV после скачивания).
Открываем консольку.
Должно скомпилироваться без проблем.
cd E:\Projects\OpenCV\opencv\build
set PATH=D:\Qt\5.10\mingw53_32\bin\;%PATH%
set PATH=D:\Qt\Tools\mingw530_32\bin\;%PATH%
cmake ../sources -G "MinGW Makefiles" -DWITH_QT=ON -DWITH_QT_OPENGL=ON -DBUILD_EXAMLES=ON -DQT_QTCORE_LIBRARY_DEBUG="Qt5Cored.dll" -DQT_QTCORE_LIBRARY_RELEASE="Qt5Core.dll" -DQT_QMAKE_EXECUTABLE="qmake.exe" -DCMAKE_BUILD_TYPE=Release
mingw32-make -j4
mingw32-make install
Если нужны ещё и nonfree модули (например для SURF), то:
«git clone https://github.com/opencv/opencv_contrib ../opencv_contrib»
и к cmake добавить параметр: «-DOPENCV_EXTRA_MODULES_PATH=../opencv_contrib/modules» и «-DOPENCV_ENABLE_NONFREE:BOOL=ON»
Если какой-то из модулей начнёт ебать мозг, то его можно отключить дополнительным параметром к cmake: «-DBUILD_opencv_modulename=OFF»
Мне пришлось отключить:
-DWITH_OPENCL_D3D11_NV=OFF
Так как мне он не нужен вовсе и были какие-то проблемы (заголовочников не было, скорее всего).
j4 — количество ядер в проце, чтобы быстрее компилилось.
После изменения параметров cmake необходимо из директории удалить файл CMakeCache.txt
MinGW необходимо использовать тот, который или идёт с Qt или тем, которым эти исходники уже компилировали!
Соответственно нужно заменить все пути на свои. И в выводе CMake убедитесь, что используется от Qt.
UPDT: OpenCV 3.0
Если во время сборки mingw32-make ругнётся на отсутствие -lRunTmChk то cmake нужно перезапустить с флагом «-DWITH_IPP=OFF»
Если будет ругаться » error: base class ‘s
truct IUnknown’ has accessible non-virtual destructor [-Werror=non-virtual-dtor]»
то идём в папку с opencv d:\projects\opencv\sources\cmake\ открываем OpenCVCompilerOptions.cmake
ищем строку add_extra_compiler_option(-Werror=non-virtual-dtor) и ставим перед ней «#»
перезапускаем cmake.
Если будет ругаться «windres.exe: unknown option —W» то выполните заново cmake с дополнительным параметром «-DENABLE_PRECOMPILED_HEADERS=OFF»
Если будут проблемы на шаге videoio, например «sprintf_instead_use_StringCbPrintfA_or_StringCchPrintfA» и похожие, то в файле «\sources\modules\videoio\src\cap_dshow.hpp» добавьте в самом начале «#define NO_DSHOW_STRSAFE«
#include <iostream>;
#include <opencv2/opencv.hpp>;
#include <opencv2/highgui.hpp>
using namespace std;
using namespace cv;
int main() {
namedWindow("My Image");
Mat image = imread("K:/Projects/OpenCV HelloWorld/img.jpg");
imshow("My Image", image);
waitKey(5000);
return 0;
}
Чуть не забыл, в системную переменную PATH добавьте путь: «E:/Projects/OpenCV/opencv/build/install/x86/mingw/bin»,
E:/Projects/OpenCV/opencv/build/install/x86/mingw/bin;%path%»
иначе проект не соберётся и ребутнуть комп, или в Qt в настройках проекта — переменных окружения добавить.
Если переходите с предыдущей версии OpenCV, то в 4.0 много изменений (ну, к примеру, наименование констант CV_BGR2GRAY теперь COLOR_BGR2GRAY), в общем документация тут: https://docs.opencv.org/
Надеюсь, всё прошло удачно =) На этом, пока что, всё.