Перейти к содержимому
Compvision.ru
sanchez

Выравнивание яркости двух изображений

Recommended Posts

Помогите, пожалуйста, с кодом. При склейке двух изображений

1.png.f82c6500433f8da1e120893ae1a800d0.png2.png.16d267458bd197f00d9746ed2f56b106.png

получается вот это:

result.thumb.png.ae804e856fd20b0bf063d4ca5fce22f1.png

Как можно отредактировать конечное изображение или сначала сравнять два изображения к одной яркости?

#include <stdio.h>
#include <iostream>
#include "opencv2/core/core.hpp"
#include "opencv2/features2d/features2d.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/nonfree/nonfree.hpp"
#include "opencv2/calib3d/calib3d.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;

int main()
{
	// Load the images
	Mat image1 = imread("2.png");
	Mat image2 = imread("1.png");
	Mat image3;
	Mat gray_image1;
	Mat gray_image2;
	Mat gray_image3;
	// Convert to Grayscale
	cvtColor(image1, gray_image1, CV_RGB2GRAY);
	cvtColor(image2, gray_image2, CV_RGB2GRAY);
	imshow("first image", image1);
	imshow("second image", image2);

	if (!gray_image1.data || !gray_image2.data)
	{
		std::cout << " Error reading images " << std::endl; return -1;
	}

	//-- Step 1: Detect the keypoints using SURF Detector
	int minHessian = 400;

	SurfFeatureDetector detector(minHessian);

	std::vector< KeyPoint > keypoints_object, keypoints_scene;

	detector.detect(gray_image1, keypoints_object);
	detector.detect(gray_image2, keypoints_scene);

	//-- Step 2: Calculate descriptors (feature vectors)
	SurfDescriptorExtractor extractor;

	Mat descriptors_object, descriptors_scene;

	extractor.compute(gray_image1, keypoints_object, descriptors_object);
	extractor.compute(gray_image2, keypoints_scene, descriptors_scene);

	//-- Step 3: Matching descriptor vectors using FLANN matcher
	FlannBasedMatcher matcher;
	std::vector< DMatch > matches;
	matcher.match(descriptors_object, descriptors_scene, matches);

	double min_dist = 100;

	//-- Quick calculation of min distances between keypoints
	for (int i = 0; i < descriptors_object.rows; i++)
	{
		double dist = matches[i].distance;
		if (dist < min_dist) min_dist = dist;
	}


	//-- Use only "good" matches (i.e. whose distance is less than 3*min_dist )
	std::vector< DMatch > good_matches;

	for (int i = 0; i < descriptors_object.rows; i++)
	{
		if (matches[i].distance < 3 * min_dist)
		{
			good_matches.push_back(matches[i]);
		}
	}

	std::vector< Point2f > obj;
	std::vector< Point2f > scene;

	for (int i = 0; i < good_matches.size(); i++)
	{
		//-- Get the keypoints from the good matches
		obj.push_back(keypoints_object[good_matches[i].queryIdx].pt);
		scene.push_back(keypoints_scene[good_matches[i].trainIdx].pt);
	}
	

	// Find the Homography Matrix
	Mat H = findHomography(obj, scene, CV_RANSAC);
	// Use the Homography Matrix to warp the images
	Mat result;
	warpPerspective(image1, result, H, Size(image1.cols + image2.cols, image1.rows));
	Mat half(result, Rect(0, 0, image2.cols, image2.rows));
	image2.copyTo(half);	

	imshow("Result", result);
	waitKey(0);
	return 0;
}

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Вроде как можно перевести обе картинки в HSV, найти среднее по каналу V: v1 и v2 для каждого изображения. Затем найти отношения k1 = (v1 + v2) / 2*v1 и k2 = (v1 + v2) / 2*v2.

Далее умножить каналы V обоих изображений на, соответственно, k1 и k2. И преобразовать обратно в RGB.

  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
26 минут назад, Nuzhny сказал:

Вроде как можно перевести обе картинки в HSV, найти среднее по каналу V: v1 и v2 для каждого изображения. Затем найти отношения k1 = (v1 + v2) / 2*v1 и k2 = (v1 + v2) / 2*v2.

Далее умножить каналы V обоих изображений на, соответственно, k1 и k2. И преобразовать обратно в RGB.

спасибо, попробую) возможно чуть позже появятся вопросы

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
4 часа назад, Nuzhny сказал:

Вроде как можно перевести обе картинки в HSV, найти среднее по каналу V: v1 и v2 для каждого изображения. Затем найти отношения k1 = (v1 + v2) / 2*v1 и k2 = (v1 + v2) / 2*v2.

Далее умножить каналы V обоих изображений на, соответственно, k1 и k2. И преобразовать обратно в RGB.

попробовал сделать по вашему совету:

	Mat image3;
	Mat image4;
	Mat hsv1, hsv2;
	vector<Mat> channel1;
	vector<Mat> channel2;

	//  конвертируем в HSV 
	cvtColor(image1, hsv1, CV_RGB2HSV);
	split(hsv1, channel1);

	cvtColor(image2, hsv2, CV_RGB2HSV);
	split(hsv2, channel2);

	double v1 = 0, v2 = 0;

	for (int i = 0; i<hsv1.rows; i++)     
	{
		for (int j = 0; j<hsv1.cols; j++)
		{
			v1 += channel1[2].at<uchar>(i, j);
		}
	}
	for (int i = 0; i<hsv2.rows; i++)     
	{
		for (int j = 0; j<hsv2.cols; j++)
		{
			v2 += channel2[2].at<uchar>(i, j);
		}
	}

	double v_average1 = 0, v_average2 = 0;
	v_average1 = v1 / (hsv1.rows * hsv1.cols);
	v_average2 = v2 / (hsv2.rows * hsv2.cols);

	double k1 = 0, k2 = 0;
	k1 = (v_average1 + v_average2) / (2 * v_average1);
	k2 = (v_average1 + v_average2) / (2 * v_average2);
	cout <<"v1 and v2" << v1 << "     " << v2 << endl;
	cout <<"v_average1 and v_average2" << v_average1 << "     " << v_average2 << endl;
	cout <<"k1 and k2" << k1 << "     " << k2 << endl;

	for (int i = 0; i<hsv1.rows; i++)     
	{
		for (int j = 0; j<hsv1.cols; j++)
		{
			channel1[2].at<uchar>(i, j) *= k1;
		}
	}

	for (int i = 0; i<hsv2.rows; i++)    
	{
		for (int j = 0; j<hsv2.cols; j++)
		{
			channel2[2].at<uchar>(i, j) *= k2;
		}
	}

	bitwise_and(channel1[0], channel1[1], image3);
	bitwise_and(image3, channel1[2], image3);

	bitwise_and(channel2[0], channel2[1], image4);
	bitwise_and(image4, channel2[2], image4);

	cvtColor(image3, image1, CV_HSV2RGB);
	cvtColor(image4, image2, CV_HSV2RGB);

и вот результатres.thumb.png.5b4f09f33c61626fdf69e469e5393b87.png

выдаёт вот такие ошибки:error2.png.abfde0c675ebc0ff785fb6f92d026f8a.pngerror1.png.12d2d40a4d21242a507afe918039935d.pngconsole.png.9b3f728321c0d1570f3e5c35914c9369.png

я нашёл, что ошибка возникает из-за того, что я преобразую обратно из HSV. но как это исправить?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

1. Вместо кода типа:

channel1[2].at<uchar>(i, j) *= k1;

 Лучше писать:

channel1[2].at<uchar>(i, j) = cv::saturate_cast<uchar>(k1 * channel1[2].at<uchar>(i, j));

2. Использование bitwise_and тут в корне неправильное, надо cv::merge вызывать.

3. При конвертации, скорее всего, надо использовать не RGB, а BGR.

4. Если потом захочется ускорить всё это дело, то split/merge можно вообще убрать, а сумму считать через cv::sum.

  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
3 часа назад, Nuzhny сказал:

1. Вместо кода типа:


channel1[2].at<uchar>(i, j) *= k1;

 Лучше писать:


channel1[2].at<uchar>(i, j) = cv::saturate_cast<uchar>(k1 * channel1[2].at<uchar>(i, j));

2. Использование bitwise_and тут в корне неправильное, надо cv::merge вызывать.

3. При конвертации, скорее всего, надо использовать не RGB, а BGR.

4. Если потом захочется ускорить всё это дело, то split/merge можно вообще убрать, а сумму считать через cv::sum.

учёл ваши замечания, теперь компилируется без ошибок, но данный способ не помог выравнять яркость

res3.thumb.png.35a01f748b656d78d60d96d871d47164.png

#include <stdio.h>
#include <iostream>
#include "opencv2/core/core.hpp"
#include "opencv2/features2d/features2d.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/nonfree/nonfree.hpp"
#include "opencv2/calib3d/calib3d.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;


int main()
{
	// Load the images
	Mat image1 = imread("2.png");
	Mat image2 = imread("1.png");
	Mat image3;
	Mat image4;
	Mat gray_image1;
	Mat gray_image2;
	Mat gray_image3;

	Mat hsv1, hsv2;
	vector<Mat> channel1;
	vector<Mat> channel2;

	//  конвертируем в HSV 
	cvtColor(image1, hsv1, CV_BGR2HSV);
	split(hsv1, channel1);
	imshow("h channel", channel1[2]);

	cvtColor(image2, hsv2, CV_BGR2HSV);
	split(hsv2, channel2);
	imshow("h channel1", channel2[2]);

	double v1 = 0, v2 = 0;

	for (int i = 0; i<hsv1.rows; i++)    
	{
		for (int j = 0; j<hsv1.cols; j++)
		{
			v1 += channel1[2].at<uchar>(i, j);
		}
	}
	for (int i = 0; i<hsv2.rows; i++)    
	{
		for (int j = 0; j<hsv2.cols; j++)
		{
			v2 += channel2[2].at<uchar>(i, j);
		}
	}

	double v_average1 = 0, v_average2 = 0;
	v_average1 = v1 / (hsv1.rows * hsv1.cols);
	v_average2 = v2 / (hsv2.rows * hsv2.cols);

	double k1 = 0, k2 = 0;
	k1 = (v_average1 + v_average2) / (2 * v_average1);
	k2 = (v_average1 + v_average2) / (2 * v_average2);
	cout <<"v1 and v2:                        " << v1 << "     " << v2 << endl;
	cout << "v_average1 and v_average2:        " << v_average1 << "     " << v_average2 << endl;
	cout << "k1 and k2:                        " << k1 << "     " << k2 << endl;

	for (int i = 0; i<hsv1.rows; i++)   
	{
		for (int j = 0; j<hsv1.cols; j++)
		{
			channel1[2].at<uchar>(i, j) = cv::saturate_cast<uchar>(k1 * channel1[2].at<uchar>(i, j));
		}
	}

	for (int i = 0; i<hsv2.rows; i++)   
	{
		for (int j = 0; j<hsv2.cols; j++)
		{
			channel2[2].at<uchar>(i, j) = cv::saturate_cast<uchar>(k2 * channel2[2].at<uchar>(i, j));
		}
	}

	merge(channel1, image3);
	merge(channel2, image4);

	imshow("first image11", image3);
	imshow("first image12", image4);

	cvtColor(image3, image1, CV_HSV2BGR);
	cvtColor(image4, image2, CV_HSV2BGR);

	// Convert to Grayscale
	cvtColor(image1, gray_image1, CV_RGB2GRAY);
	cvtColor(image2, gray_image2, CV_RGB2GRAY);
	imshow("first image", image1);
	imshow("second image", image2);

	if (!gray_image1.data || !gray_image2.data)
	{
		std::cout << " Error reading images " << std::endl; return -1;
	}

	//-- Step 1: Detect the keypoints using SURF Detector
	int minHessian = 400;

	SurfFeatureDetector detector(minHessian);

	std::vector< KeyPoint > keypoints_object, keypoints_scene;

	detector.detect(gray_image1, keypoints_object);
	detector.detect(gray_image2, keypoints_scene);

	//-- Step 2: Calculate descriptors (feature vectors)
	SurfDescriptorExtractor extractor;

	Mat descriptors_object, descriptors_scene;

	extractor.compute(gray_image1, keypoints_object, descriptors_object);
	extractor.compute(gray_image2, keypoints_scene, descriptors_scene);

	//-- Step 3: Matching descriptor vectors using FLANN matcher
	FlannBasedMatcher matcher;
	std::vector< DMatch > matches;
	matcher.match(descriptors_object, descriptors_scene, matches);

	double min_dist = 100;

	//-- Quick calculation of min distances between keypoints
	for (int i = 0; i < descriptors_object.rows; i++)
	{
		double dist = matches[i].distance;
		if (dist < min_dist) min_dist = dist;
	}


	//-- Use only "good" matches (i.e. whose distance is less than 3*min_dist )
	std::vector< DMatch > good_matches;

	for (int i = 0; i < descriptors_object.rows; i++)
	{
		if (matches[i].distance < 3 * min_dist)
		{
			good_matches.push_back(matches[i]);
		}
	}

	std::vector< Point2f > obj;
	std::vector< Point2f > scene;

	for (int i = 0; i < good_matches.size(); i++)
	{
		//-- Get the keypoints from the good matches
		obj.push_back(keypoints_object[good_matches[i].queryIdx].pt);
		scene.push_back(keypoints_scene[good_matches[i].trainIdx].pt);
	}

	// Find the Homography Matrix
	Mat H = findHomography(obj, scene, CV_RANSAC);
	// Use the Homography Matrix to warp the images
	Mat result;
	warpPerspective(image1, result, H, Size(image1.cols + image2.cols, image1.rows));
	Mat half(result, Rect(0, 0, image2.cols, image2.rows));
	image2.copyTo(half);
	
	imshow("Result", result);
	waitKey(0);
	return 0;
}

это я что-то неправильно сделал, или же метод просто не подходит?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

1. Ты делаешь вывод HSV изображений:
imshow("first image11", image3);
Imshow("first image12", image4);
OpenCV это не умеет и выводит их, считая, что они RGB. Вывод надо делать после конвертации.

2. Я бы сделал как-то так:


// Load the images
cv::Mat image1 = cv::imread("2.png");
cv::Mat image2 = cv::imread("1.png");

// конвертируем в HSV
cv::Mat hsv1;
cv::cvtColor(image1, hsv1, CV_BGR2HSV);

cv::Mat hsv2;
cv::cvtColor(image2, hsv2, CV_BGR2HSV);

cv::Scalar v1 = cv::sum(hsv1) / (hsv1.rows * hsv1.cols);
cv::Scalar v2 = cv::sum(hsv2) / (hsv1.rows * hsv1.cols);

double k1 = (v1[2] + v2[2]) / (2 * v1[2]);
double k2 = (v1[2] + v2[2]) / (2 * v2[2]);

std::cout << "v_average1 and v_average2: " << v1[2] << " " << v2[2] << std::endl;
std::cout << "k1 and k2: " << k1 << " " << k2 << std::endl;

for (int y = 0; y < hsv1.rows; ++y)
{
uchar* p1 = hsv1.ptr(y);
uchar* p2 = hsv2.ptr(y);
for (int x = 0; x < hsv1.cols; ++x)
{
p1[2] = cv::saturate_cast(k1 * p1[2]);
p2[2] = cv::saturate_cast(k2 * p2[2]);

p1 += 3;
p2 += 3;
}
}

cv::imshow("first image original", image1);
cv::imshow("second image original", image2);

cv::Mat image12;
cv::Mat image22;
cv::cvtColor(hsv1, image12, CV_HSV2BGR);
cv::cvtColor(hsv2, image22, CV_HSV2BGR);

cv::imshow("first image new", image12);
cv::imshow("second image new", image22);

// Узнаем, как сильно стали различаться каналы в RGB
cv::Mat diff1;
cv::absdiff(image1, image12, diff1);
cv::Mat diff2;
cv::absdiff(image2, image22, diff2);

cv::Scalar d1 = cv::sum(diff1);
cv::Scalar d2 = cv::sum(diff2);
std::cout << "diff1 and diff2: " << d1 << " " << d2 << std::endl;


  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
25 minutes ago, mrgloom said:

Если что в opencv есть

Ну, если можно использовать опенсивишный stitching, то вообще всё это не нужно - просто его использовать и всё.

  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
1 час назад, Nuzhny сказал:

Ну, если можно использовать опенсивишный stitching, то вообще всё это не нужно - просто его использовать и всё.

если вам не сложно, не могли бы помочь с кодом, или поподробней рассказать что с этим stitching надо делать

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Так тебе mrgloom выше ссылку дал на стандартный пример. Это и есть код.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
5 часов назад, Nuzhny сказал:

Так тебе mrgloom выше ссылку дал на стандартный пример. Это и есть код.

Простите за мою откровенную тупость(первый раз сталкиваюсь с opencv), просто я никак не могу разобраться с этим примером, и уж тем более как его применить к моей работе. если поможете, буду очень благодарен

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Разобраться в исходниках примера или в алгоритмах?

Если у тебя скомпилированы примеры, то просто запускай его с различными опциями (они описаны). Если примеры не скомпилированы, то скомпилируй.

Так, запуская на своих данных с разными опциями, ты поймёшь что и как влияет на результат. Это самый лучший способ разобраться, только потом есть смысл углубляться в теорию.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

а есть какой-то способ (без stiching), чтобы обработать изображения. Может как то обработать уже результирующее изображении? просто нужно, чтобы алгоритм(код) склейки оставался прежним(как я скидывал в самом начале)

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Можно и не выравнивать, а просто швы блендингом замазать. Делаете маску с элементами 0 для первого изображения, 1 для второго, размываете, и получаете результат как  res=mask.mul(I_1)+(1-mask).mul(I_2). Нужно делать поканально, все матрицы преобразовать к CV_32FC1. 

Получим плавный переход в месте стыка.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
1 час назад, Smorodov сказал:

Можно и не выравнивать, а просто швы блендингом замазать. Делаете маску с элементами 0 для первого изображения, 1 для второго, размываете, и получаете результат как  res=mask.mul(I_1)+(1-mask).mul(I_2). Нужно делать поканально, все матрицы преобразовать к CV_32FC1. 

Получим плавный переход в месте стыка.

если не сложно, можно чуть поподробней как это сделать, а то с реализацией у меня большие проблемы. я понял только создание масок(и то не уверен об этом ли вы говорили)

Mat mask1 = Mat::zeros(image1.rows, image1.cols, CV_32FC1);
Mat mask2 = Mat::ones(image2.rows, image2.cols, CV_32FC1);

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Создайте учётную запись или войдите для комментирования

Вы должны быть пользователем, чтобы оставить комментарий

Создать учётную запись

Зарегистрируйтесь для создания учётной записи. Это просто!

Зарегистрировать учётную запись

Войти

Уже зарегистрированы? Войдите здесь.

Войти сейчас


  • Сейчас на странице   0 пользователей

    Нет пользователей, просматривающих эту страницу

×