マウスの位置に画像を再配置 opencv ・vs2013

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
takezawaakiragm
記事: 26
登録日時: 8年前

マウスの位置に画像を再配置 opencv ・vs2013

#1

投稿記事 by takezawaakiragm » 8年前

現在、画像(背景画像)の上に画像(短形領域)をマウスカーソルの位置に再配置しようとしております。
今のコードだと、最初は中央に配置されるのですが、それをマウスカーソルの位置に移動させたいです。
175行目にコードを追加し、右クリック+SHIFTを押せば移動するようにしたいのですが、
どのようにすればいいでしょうか。

分かる方是非ご教授お願いします。

コード:

/*------------------------------------------------------------*
* @file    transparent2.cpp
* @brief   透過画像を変形して表示する
*------------------------------------------------------------*/
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include<iostream>
#include<opencv\cv.h>

#include <stdlib.h>
#include <stdio.h>

using namespace std;

const char winName[] = "Pers image";
vector<cv::Point2f> m_persRect;
int m_rectIdx = -1, m_mouseUpFlag = -1;
int m_imgW, m_imgH;
bool m_enterFlag = false;

//mouseの座標
int mouse_x , mouse_y;

/**-----------------------------------------------------------*
* @fn  DrawTransPinP
* @brief   透過画像を重ねて描画する
* @param[out] img_dst
* @param[in ] transImg 前景画像。アルファチャンネル付きであること(CV_8UC4) 
* @ pngかgifかtiffのみ
* @param[in ] baseImg  背景画像。アルファチャンネル不要(CV_8UC3)
*------------------------------------------------------------*/
void DrawTransPinP(cv::Mat &img_dst, const cv::Mat transImg, const cv::Mat baseImg, vector<cv::Point2f> tgtPt)
{
	cv::Mat img_rgb, img_aaa, img_1ma;
	vector<cv::Mat>planes_rgba, planes_rgb, planes_aaa, planes_1ma;
	int maxVal = pow(2, 8 * baseImg.elemSize1()) - 1;

	//透過画像はRGBA, 背景画像はRGBのみ許容。ビット深度が同じ画像のみ許容
	if (transImg.data == NULL || baseImg.data == NULL || transImg.channels()<4 || baseImg.channels()<3 || (transImg.elemSize1() != baseImg.elemSize1()))
	{
		img_dst = cv::Mat(100, 100, CV_8UC3);
		img_dst = cv::Scalar::all(maxVal);
		return;
	}

	//書き出し先座標が指定されていない場合は背景画像の中央に配置する
	if (tgtPt.size()<4)
	{
		//座標指定(背景画像の中心に表示する)
		int ltx = (baseImg.cols - transImg.cols) / 2;
		int lty = (baseImg.rows - transImg.rows) / 2;
		int ww = transImg.cols;
		int hh = transImg.rows;

		tgtPt.push_back(cv::Point2f(ltx, lty));
		tgtPt.push_back(cv::Point2f(ltx + ww, lty));
		tgtPt.push_back(cv::Point2f(ltx + ww, lty + hh));
		tgtPt.push_back(cv::Point2f(ltx, lty + hh));
	}

	//変形行列を作成
	vector<cv::Point2f>srcPt;
	srcPt.push_back(cv::Point2f(0, 0));
	srcPt.push_back(cv::Point2f(transImg.cols - 1, 0));
	srcPt.push_back(cv::Point2f(transImg.cols - 1, transImg.rows - 1));
	srcPt.push_back(cv::Point2f(0, transImg.rows - 1));
	cv::Mat mat = cv::getPerspectiveTransform(srcPt, tgtPt);

	//出力画像と同じ幅・高さのアルファ付き画像を作成
	cv::Mat alpha0(baseImg.rows, baseImg.cols, transImg.type());
	alpha0 = cv::Scalar::all(0);
	cv::warpPerspective(transImg, alpha0, mat, alpha0.size(), cv::INTER_CUBIC, cv::BORDER_TRANSPARENT);

	//チャンネルに分解
	cv::split(alpha0, planes_rgba);

	//RGBA画像をRGBに変換   
	planes_rgb.push_back(planes_rgba[0]);
	planes_rgb.push_back(planes_rgba[1]);
	planes_rgb.push_back(planes_rgba[2]);
	merge(planes_rgb, img_rgb);

	//RGBA画像からアルファチャンネル抽出   
	planes_aaa.push_back(planes_rgba[3]);
	planes_aaa.push_back(planes_rgba[3]);
	planes_aaa.push_back(planes_rgba[3]);
	merge(planes_aaa, img_aaa);

	//背景用アルファチャンネル   
	planes_1ma.push_back(maxVal - planes_rgba[3]);
	planes_1ma.push_back(maxVal - planes_rgba[3]);
	planes_1ma.push_back(maxVal - planes_rgba[3]);
	merge(planes_1ma, img_1ma);

	img_dst = img_rgb.mul(img_aaa, 1.0 / (double)maxVal) + baseImg.mul(img_1ma, 1.0 / (double)maxVal);
}

/*------------------------------------------------------------
* @function CalcDistance
*------------------------------------------------------------*/
double CalcDistance(cv::Point srcPt, cv::Point dstPt)
{
	double dd = sqrt(pow(dstPt.x - srcPt.x, 2) + pow(dstPt.y - srcPt.y, 2));
	return dd;
}

/*------------------------------------------------------------*
* 一つの多角形を描画する
*------------------------------------------------------------*/
void DrawPersRect(cv::Mat &img, const vector<cv::Point2f> &rect, const cv::Scalar col, const int thickness)
{
	int ww = img.cols;
	int hh = img.rows;

	for (int j = 0; j<rect.size(); j++)
	{
		cv::Point2f srcPt = rect[j];
		cv::Point2f dstPt = rect[(j + 1) % rect.size()];

		cv::line(img, srcPt, dstPt, col, thickness, CV_AA, 0);
		cv::rectangle(img, srcPt - cv::Point2f(2, 2), srcPt + cv::Point2f(2, 2), col, CV_FILLED, CV_AA);

	}
}

/*------------------------------------------------------------
* @function InitRect
* @brief パース設定四角形の初期化
*------------------------------------------------------------*/
void InitRect(cv::Mat transImg, cv::Mat baseImg)
{
	int ltx = (baseImg.cols - transImg.cols) / 2;
	int lty = (baseImg.rows - transImg.rows) / 2;
	int ww = transImg.cols;
	int hh = transImg.rows;

	//変形前
	m_persRect.clear();
	m_persRect.push_back(cv::Point2f(ltx, lty));
	m_persRect.push_back(cv::Point2f(ltx + ww, lty));
	m_persRect.push_back(cv::Point2f(ltx + ww, lty + hh));
	m_persRect.push_back(cv::Point2f(ltx, lty + hh));
}

/*------------------------------------------------------------
* @function MouseClick
*------------------------------------------------------------*/
void on_mouse(int event, int x, int y, int flags, void *param)
{
	cv::Point2i curPt;
	

	//アンカーポイントでのクリック判定
	if (event == CV_EVENT_LBUTTONDOWN)
	{
		m_rectIdx = -1;
		curPt = cv::Point(x, y);

		for (int i = 0; i<4; i++)
		{
			double dd = CalcDistance(curPt, m_persRect[i]);
			if (dd<10)
			{
				m_rectIdx = i;
			}

		}
	}
	if (event == CV_EVENT_RBUTTONDOWN){
		mouse_x = x;
		mouse_y = y;
			std::cout << x << "," << y << "\n";
			if (flags & cv::EVENT_FLAG_SHIFTKEY){
				//画像をマウスの位置に移動させる
				
			}
	}
	

	//アンカーポイントのドラッグ時
	if (m_rectIdx>-1)
	{
		if (x<2)x = 2; else if (x>(m_imgW - 2))x = m_imgW - 2;
		if (y<2)y = 2; else if (y>(m_imgH - 2))y = m_imgH - 2;
		curPt = cv::Point2f(x, y);
		m_persRect[m_rectIdx] = curPt;
	}

	//アンカーポイントの移動終了時
	if (event == CV_EVENT_LBUTTONUP)
	{
		if (m_rectIdx>-1)m_mouseUpFlag = 1;
	}

	//ポリゴンの内外判定
	double inout = pointPolygonTest(m_persRect, cv::Point2i(x, y), 5);
	if (inout>-5)m_enterFlag = true; else m_enterFlag = false;
}

/*------------------------------------------------------------*
* Main routine
*------------------------------------------------------------*/
int main(int argc, char** argv)
{
	bool drawFlag = false;
	cv::Mat transImg, baseImg, tmpImg;
	bool enterState = false;
	cv::Scalar col = CV_RGB(255, 255, 255);

	//画像読み込み
	transImg = cv::imread("cut123.png", cv::IMREAD_UNCHANGED);
	baseImg = cv::imread("200x150 test2.png");

	if ((transImg.data == NULL) || (baseImg.data == NULL))
	{
		printf("------------------------------\n");
		printf("image not exist\n");
		printf("------------------------------\n");
		return EXIT_FAILURE;
	}
	else
	{
		printf("------------------------------\n");
		printf("Press ESC key to quit\n");
		printf("------------------------------\n");
	}

	m_imgW = baseImg.cols;
	m_imgH = baseImg.rows;

	//パース枠の初期化
	InitRect(transImg, baseImg);

	//ウィンドウ生成
	cv::namedWindow(winName);

	//マウスコールバック設定
	cv::setMouseCallback(winName, on_mouse, 0);

	//初期画面の描画
	DrawTransPinP(tmpImg, transImg, baseImg, m_persRect);
	DrawPersRect(tmpImg, m_persRect, col, 1);
	imshow(winName, tmpImg);

	while (1)
	{
		//アンカーポイントのドラッグ時
		if (m_rectIdx>-1){ drawFlag = true; }

		//アンカーポイントの移動終了時
		if (m_mouseUpFlag>0)
		{
			m_mouseUpFlag = -1;
			m_rectIdx = -1;
			drawFlag = true;
		}

		//キーボード処理
		int a = cv::waitKey(1);
		if (a == 27)break;

		if (enterState != m_enterFlag)
		{
			enterState = m_enterFlag;
			if (enterState)col = CV_RGB(255, 0, 0);
			else col = CV_RGB(255, 255, 255);
			drawFlag = true;
		}

		if (drawFlag)
		{

			DrawTransPinP(tmpImg, transImg, baseImg, m_persRect);
			if (m_enterFlag)DrawPersRect(tmpImg, m_persRect, col, 1);
			imshow(winName, tmpImg);

			drawFlag = false;
			cv::imwrite("outtest1.png", tmpImg);
		}
	}
	
	
	return EXIT_SUCCESS;
}


C6b14

Re: マウスの位置に画像を再配置 opencv ・vs2013

#2

投稿記事 by C6b14 » 8年前

まず bool drawFlag = false; をグローバル変数にする。マウスのx,y代入のとき drawFlag = true; とする。そうすれば DrawTransPinP にmouse_x , mouse_y; が入る。(再描画させるわけだから)。std::cout  で流れをトレースすれば一目瞭然ですよ。

takezawaakiragm
記事: 26
登録日時: 8年前

Re: マウスの位置に画像を再配置 opencv ・vs2013

#3

投稿記事 by takezawaakiragm » 8年前

>>C6b14さん
ありがとうございます。
たびたび申し訳ない。
どのように再描画させればよいでしょうか。DrawTransPinPを呼び出すのでしょうか。

コード:

/*------------------------------------------------------------*
* @file    transparent2.cpp
* @brief   透過画像を変形して表示する
*------------------------------------------------------------*/
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include<iostream>
#include<opencv\cv.h>

#include <stdlib.h>
#include <stdio.h>

using namespace std;

const char winName[] = "Pers image";
vector<cv::Point2f> m_persRect;
int m_rectIdx = -1, m_mouseUpFlag = -1;
int m_imgW, m_imgH;
bool m_enterFlag = false;

//mouseの座標
int mouse_x , mouse_y;

bool drawFlag = false;
/**-----------------------------------------------------------*
* @fn  DrawTransPinP
* @brief   透過画像を重ねて描画する
* @param[out] img_dst
* @param[in ] transImg 前景画像。アルファチャンネル付きであること(CV_8UC4) 
* @ pngかgifかtiffのみ
* @param[in ] baseImg  背景画像。アルファチャンネル不要(CV_8UC3)
*------------------------------------------------------------*/
void DrawTransPinP(cv::Mat &img_dst, const cv::Mat transImg, const cv::Mat baseImg, vector<cv::Point2f> tgtPt)
{
	cv::Mat img_rgb, img_aaa, img_1ma;
	vector<cv::Mat>planes_rgba, planes_rgb, planes_aaa, planes_1ma;
	int maxVal = pow(2, 8 * baseImg.elemSize1()) - 1;

	//透過画像はRGBA, 背景画像はRGBのみ許容。ビット深度が同じ画像のみ許容
	if (transImg.data == NULL || baseImg.data == NULL || transImg.channels()<4 || baseImg.channels()<3 || (transImg.elemSize1() != baseImg.elemSize1()))
	{
		img_dst = cv::Mat(100, 100, CV_8UC3);
		img_dst = cv::Scalar::all(maxVal);
		return;
	}

	//書き出し先座標が指定されていない場合は背景画像の中央に配置する
	if (tgtPt.size()<4)
	{
		//座標指定(背景画像の中心に表示する)
		int ltx = (baseImg.cols - transImg.cols) / 2;
		int lty = (baseImg.rows - transImg.rows) / 2;
		int ww = transImg.cols;
		int hh = transImg.rows;

		tgtPt.push_back(cv::Point2f(ltx, lty));
		tgtPt.push_back(cv::Point2f(ltx + ww, lty));
		tgtPt.push_back(cv::Point2f(ltx + ww, lty + hh));
		tgtPt.push_back(cv::Point2f(ltx, lty + hh));
	}

	//変形行列を作成
	vector<cv::Point2f>srcPt;
	srcPt.push_back(cv::Point2f(0, 0));
	srcPt.push_back(cv::Point2f(transImg.cols - 1, 0));
	srcPt.push_back(cv::Point2f(transImg.cols - 1, transImg.rows - 1));
	srcPt.push_back(cv::Point2f(0, transImg.rows - 1));
	cv::Mat mat = cv::getPerspectiveTransform(srcPt, tgtPt);

	//出力画像と同じ幅・高さのアルファ付き画像を作成
	cv::Mat alpha0(baseImg.rows, baseImg.cols, transImg.type());
	alpha0 = cv::Scalar::all(0);
	cv::warpPerspective(transImg, alpha0, mat, alpha0.size(), cv::INTER_CUBIC, cv::BORDER_TRANSPARENT);

	//チャンネルに分解
	cv::split(alpha0, planes_rgba);

	//RGBA画像をRGBに変換   
	planes_rgb.push_back(planes_rgba[0]);
	planes_rgb.push_back(planes_rgba[1]);
	planes_rgb.push_back(planes_rgba[2]);
	merge(planes_rgb, img_rgb);

	//RGBA画像からアルファチャンネル抽出   
	planes_aaa.push_back(planes_rgba[3]);
	planes_aaa.push_back(planes_rgba[3]);
	planes_aaa.push_back(planes_rgba[3]);
	merge(planes_aaa, img_aaa);

	//背景用アルファチャンネル   
	planes_1ma.push_back(maxVal - planes_rgba[3]);
	planes_1ma.push_back(maxVal - planes_rgba[3]);
	planes_1ma.push_back(maxVal - planes_rgba[3]);
	merge(planes_1ma, img_1ma);

	img_dst = img_rgb.mul(img_aaa, 1.0 / (double)maxVal) + baseImg.mul(img_1ma, 1.0 / (double)maxVal);
}

/*------------------------------------------------------------
* @function CalcDistance
*------------------------------------------------------------*/
double CalcDistance(cv::Point srcPt, cv::Point dstPt)
{
	double dd = sqrt(pow(dstPt.x - srcPt.x, 2) + pow(dstPt.y - srcPt.y, 2));
	return dd;
}

/*------------------------------------------------------------*
* 一つの多角形を描画する
*------------------------------------------------------------*/
void DrawPersRect(cv::Mat &img, const vector<cv::Point2f> &rect, const cv::Scalar col, const int thickness)
{
	int ww = img.cols;
	int hh = img.rows;

	for (int j = 0; j<rect.size(); j++)
	{
		cv::Point2f srcPt = rect[j];
		cv::Point2f dstPt = rect[(j + 1) % rect.size()];

		cv::line(img, srcPt, dstPt, col, thickness, CV_AA, 0);
		cv::rectangle(img, srcPt - cv::Point2f(2, 2), srcPt + cv::Point2f(2, 2), col, CV_FILLED, CV_AA);

	}
}

/*------------------------------------------------------------
* @function InitRect
* @brief パース設定四角形の初期化
*------------------------------------------------------------*/
void InitRect(cv::Mat transImg, cv::Mat baseImg)
{
	int ltx = (baseImg.cols - transImg.cols) / 2;
	int lty = (baseImg.rows - transImg.rows) / 2;
	int ww = transImg.cols;
	int hh = transImg.rows;

	//変形前
	m_persRect.clear();
	m_persRect.push_back(cv::Point2f(ltx, lty));
	m_persRect.push_back(cv::Point2f(ltx + ww, lty));
	m_persRect.push_back(cv::Point2f(ltx + ww, lty + hh));
	m_persRect.push_back(cv::Point2f(ltx, lty + hh));
}

/*------------------------------------------------------------
* @function MouseClick
*------------------------------------------------------------*/
void on_mouse(int event, int x, int y, int flags, void *param)
{
	cv::Point2i curPt;
	

	//アンカーポイントでのクリック判定
	if (event == CV_EVENT_LBUTTONDOWN)
	{
		m_rectIdx = -1;
		curPt = cv::Point(x, y);

		for (int i = 0; i<4; i++)
		{
			double dd = CalcDistance(curPt, m_persRect[i]);
			if (dd<10)
			{
				m_rectIdx = i;
			}

		}
	}
	if (event == CV_EVENT_RBUTTONDOWN){
		drawFlag = true;
		mouse_x = x;
		mouse_y = y;
		
			std::cout << x << "," << y << "\n";
			if (flags & cv::EVENT_FLAG_SHIFTKEY){
				
				//画像をマウスの位置に移動させる
			}
	}
	

	//アンカーポイントのドラッグ時
	if (m_rectIdx>-1)
	{
		if (x<2)x = 2; else if (x>(m_imgW - 2))x = m_imgW - 2;
		if (y<2)y = 2; else if (y>(m_imgH - 2))y = m_imgH - 2;
		curPt = cv::Point2f(x, y);
		m_persRect[m_rectIdx] = curPt;
	}

	//アンカーポイントの移動終了時
	if (event == CV_EVENT_LBUTTONUP)
	{
		if (m_rectIdx>-1)m_mouseUpFlag = 1;
	}

	//ポリゴンの内外判定
	double inout = pointPolygonTest(m_persRect, cv::Point2i(x, y), 5);
	if (inout>-5)m_enterFlag = true; else m_enterFlag = false;
}

/*------------------------------------------------------------*
* Main routine
*------------------------------------------------------------*/
int main(int argc, char** argv)
{
	
	cv::Mat transImg, baseImg, tmpImg;
	bool enterState = false;
	cv::Scalar col = CV_RGB(255, 255, 255);

	//画像読み込み
	transImg = cv::imread("cut123.png", cv::IMREAD_UNCHANGED);
	baseImg = cv::imread("200x150 test2.png");

	if ((transImg.data == NULL) || (baseImg.data == NULL))
	{
		printf("------------------------------\n");
		printf("image not exist\n");
		printf("------------------------------\n");
		return EXIT_FAILURE;
	}
	else
	{
		printf("------------------------------\n");
		printf("Press ESC key to quit\n");
		printf("------------------------------\n");
	}

	m_imgW = baseImg.cols;
	m_imgH = baseImg.rows;

	//パース枠の初期化
	InitRect(transImg, baseImg);

	//ウィンドウ生成
	cv::namedWindow(winName);

	//マウスコールバック設定
	cv::setMouseCallback(winName, on_mouse, 0);

	//初期画面の描画
	DrawTransPinP(tmpImg, transImg, baseImg, m_persRect);
	DrawPersRect(tmpImg, m_persRect, col, 1);
	imshow(winName, tmpImg);

	while (1)
	{
		//アンカーポイントのドラッグ時
		if (m_rectIdx>-1){ drawFlag = true; }

		//アンカーポイントの移動終了時
		if (m_mouseUpFlag>0)
		{
			m_mouseUpFlag = -1;
			m_rectIdx = -1;
			drawFlag = true;
		}

		//キーボード処理
		int a = cv::waitKey(1);
		if (a == 27)break;

		if (enterState != m_enterFlag)
		{
			enterState = m_enterFlag;
			if (enterState)col = CV_RGB(255, 0, 0);
			else col = CV_RGB(255, 255, 255);
			drawFlag = true;
		}

		if (drawFlag)
		{

			DrawTransPinP(tmpImg, transImg, baseImg, m_persRect);
			if (m_enterFlag)DrawPersRect(tmpImg, m_persRect, col, 1);
			imshow(winName, tmpImg);

			drawFlag = false;
			cv::imwrite("outtest1.png", tmpImg);
		}
	}
	
	
	return EXIT_SUCCESS;
}

C6b14

Re: マウスの位置に画像を再配置 opencv ・vs2013

#4

投稿記事 by C6b14 » 8年前

WindowsなどのGUIのプログラミングはイベント・ドリブンなので直線的には流れません。この場合は次にようになります。画像

コード:

/*------------------------------------------------------------*
* @file    transparent2.cpp
* @brief   透過画像を変形して表示する
*------------------------------------------------------------*/
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include<iostream>
#include<opencv\cv.h>

#include <stdlib.h>
#include <stdio.h>

using namespace std;

const char winName[] = "Pers image";
vector<cv::Point2f> m_persRect;
int m_rectIdx = -1, m_mouseUpFlag = -1;
int m_imgW, m_imgH;
bool m_enterFlag = false;

//mouseの座標
int mouse_x = 300;
int mouse_y = 200;

bool drawFlag = false;
/**-----------------------------------------------------------*
* @fn  DrawTransPinP
* @brief   透過画像を重ねて描画する
* @param[out] img_dst
* @param[in ] transImg 前景画像。アルファチャンネル付きであること(CV_8UC4)
* @ pngかgifかtiffのみ
* @param[in ] baseImg  背景画像。アルファチャンネル不要(CV_8UC3)
*------------------------------------------------------------*/
void DrawTransPinP(cv::Mat &img_dst, const cv::Mat transImg, const cv::Mat baseImg, vector<cv::Point2f> tgtPt)
{
	std::cout << "..[今 D 地点にいます]->"; std::cout << mouse_x << "," << mouse_y << "\n";
	cv::Mat img_rgb, img_aaa, img_1ma;
	vector<cv::Mat>planes_rgba, planes_rgb, planes_aaa, planes_1ma;
	int maxVal = pow(2, 8 * baseImg.elemSize1()) - 1;

	//透過画像はRGBA, 背景画像はRGBのみ許容。ビット深度が同じ画像のみ許容
	if (transImg.data == NULL || baseImg.data == NULL || transImg.channels()<4 || baseImg.channels()<3 || (transImg.elemSize1() != baseImg.elemSize1()))
	{
		img_dst = cv::Mat(100, 100, CV_8UC3);
		img_dst = cv::Scalar::all(maxVal);
		return;
	}
	std::cout << "..[今 E 地点にいます]->"; std::cout << mouse_x << "," << mouse_y << "\n";
	//書き出し先座標が指定されていない場合は背景画像の中央に配置する
	if (tgtPt.size()<4)
	{
		//座標指定(背景画像の中心に表示する)
		int ltx = (baseImg.cols - transImg.cols) / 2;
		int lty = (baseImg.rows - transImg.rows) / 2;
		int ww = transImg.cols;
		int hh = transImg.rows;

		tgtPt.push_back(cv::Point2f(ltx, lty));
		tgtPt.push_back(cv::Point2f(ltx + ww, lty));
		tgtPt.push_back(cv::Point2f(ltx + ww, lty + hh));
		tgtPt.push_back(cv::Point2f(ltx, lty + hh));
	}

	//変形行列を作成
	vector<cv::Point2f>srcPt;
	srcPt.push_back(cv::Point2f(0, 0));
	srcPt.push_back(cv::Point2f(transImg.cols - 1, 0));
	srcPt.push_back(cv::Point2f(transImg.cols - 1, transImg.rows - 1));
	srcPt.push_back(cv::Point2f(0, transImg.rows - 1));
	cv::Mat mat = cv::getPerspectiveTransform(srcPt, tgtPt);

	//出力画像と同じ幅・高さのアルファ付き画像を作成
	cv::Mat alpha0(baseImg.rows, baseImg.cols, transImg.type());
	alpha0 = cv::Scalar::all(0);
	cv::warpPerspective(transImg, alpha0, mat, alpha0.size(), cv::INTER_CUBIC, cv::BORDER_TRANSPARENT);

	//チャンネルに分解
	cv::split(alpha0, planes_rgba);

	//RGBA画像をRGBに変換   
	planes_rgb.push_back(planes_rgba[0]);
	planes_rgb.push_back(planes_rgba[1]);
	planes_rgb.push_back(planes_rgba[2]);
	merge(planes_rgb, img_rgb);

	//RGBA画像からアルファチャンネル抽出   
	planes_aaa.push_back(planes_rgba[3]);
	planes_aaa.push_back(planes_rgba[3]);
	planes_aaa.push_back(planes_rgba[3]);
	merge(planes_aaa, img_aaa);

	//背景用アルファチャンネル   
	planes_1ma.push_back(maxVal - planes_rgba[3]);
	planes_1ma.push_back(maxVal - planes_rgba[3]);
	planes_1ma.push_back(maxVal - planes_rgba[3]);
	merge(planes_1ma, img_1ma);

	img_dst = img_rgb.mul(img_aaa, 1.0 / (double)maxVal) + baseImg.mul(img_1ma, 1.0 / (double)maxVal);
}

/*------------------------------------------------------------
* @function CalcDistance
*------------------------------------------------------------*/
double CalcDistance(cv::Point srcPt, cv::Point dstPt)
{
	double dd = sqrt(pow(dstPt.x - srcPt.x, 2) + pow(dstPt.y - srcPt.y, 2));
	return dd;
}

/*------------------------------------------------------------*
* 一つの多角形を描画する
*------------------------------------------------------------*/
void DrawPersRect(cv::Mat &img, const vector<cv::Point2f> &rect, const cv::Scalar col, const int thickness)
{
	int ww = img.cols;
	int hh = img.rows;

	for (int j = 0; j<rect.size(); j++)
	{
		cv::Point2f srcPt = rect[j];
		cv::Point2f dstPt = rect[(j + 1) % rect.size()];

		cv::line(img, srcPt, dstPt, col, thickness, CV_AA, 0);
		cv::rectangle(img, srcPt - cv::Point2f(2, 2), srcPt + cv::Point2f(2, 2), col, CV_FILLED, CV_AA);

	}
}

/*------------------------------------------------------------
* @function InitRect
* @brief パース設定四角形の初期化
*------------------------------------------------------------*/
void InitRect(cv::Mat transImg, cv::Mat baseImg)
{
	int ltx = (baseImg.cols - transImg.cols) / 2;
	int lty = (baseImg.rows - transImg.rows) / 2;
	int ww = transImg.cols;
	int hh = transImg.rows;

	//変形前
	m_persRect.clear();
	m_persRect.push_back(cv::Point2f(ltx, lty));
	m_persRect.push_back(cv::Point2f(ltx + ww, lty));
	m_persRect.push_back(cv::Point2f(ltx + ww, lty + hh));
	m_persRect.push_back(cv::Point2f(ltx, lty + hh));
}

/*------------------------------------------------------------
* @function MouseClick
*------------------------------------------------------------*/
void on_mouse(int event, int x, int y, int flags, void *param)
{
	cv::Point2i curPt;


	//アンカーポイントでのクリック判定
	if (event == CV_EVENT_LBUTTONDOWN)
	{
		m_rectIdx = -1;
		curPt = cv::Point(x, y);

		for (int i = 0; i<4; i++)
		{
			double dd = CalcDistance(curPt, m_persRect[i]);
			if (dd<10)
			{
				m_rectIdx = i;
			}

		}
	}
	if (event == CV_EVENT_RBUTTONDOWN) {
		std::cout << "..[今 A 地点にいます]->";
		// drawFlag = true;
		mouse_x = x;
		mouse_y = y;

		std::cout << x << "," << y << "\n";
		if (flags & cv::EVENT_FLAG_SHIFTKEY) {
			std::cout << "..[今 B 地点にいます]->";
			std::cout << "..[今 drawFlagを立ててDrawTransPinPに飛びます]->";
			//画像をマウスの位置に移動させる
			drawFlag = true;
		}
	}


	//アンカーポイントのドラッグ時
	if (m_rectIdx>-1)
	{
		if (x<2)x = 2; else if (x>(m_imgW - 2))x = m_imgW - 2;
		if (y<2)y = 2; else if (y>(m_imgH - 2))y = m_imgH - 2;
		curPt = cv::Point2f(x, y);
		m_persRect[m_rectIdx] = curPt;
	}

	//アンカーポイントの移動終了時
	if (event == CV_EVENT_LBUTTONUP)
	{
		if (m_rectIdx>-1)m_mouseUpFlag = 1;
	}

	//ポリゴンの内外判定
	double inout = pointPolygonTest(m_persRect, cv::Point2i(x, y), 5);
	if (inout>-5)m_enterFlag = true; else m_enterFlag = false;
}

/*------------------------------------------------------------*
* Main routine
*------------------------------------------------------------*/
int main(int argc, char** argv)
{

	cv::Mat transImg, baseImg, tmpImg;
	bool enterState = false;
	cv::Scalar col = CV_RGB(255, 255, 255);

	//画像読み込み
	transImg = cv::imread("a.png", cv::IMREAD_UNCHANGED);
	baseImg = cv::imread("b.png");

	if ((transImg.data == NULL) || (baseImg.data == NULL))
	{
		printf("------------------------------\n");
		printf("image not exist\n");
		printf("------------------------------\n");
		return EXIT_FAILURE;
	}
	else
	{
		printf("------------------------------\n");
		printf("Press ESC key to quit\n");
		printf("------------------------------\n");
	}
	std::cout << "[今出発地点にいます]->\n";
	m_imgW = baseImg.cols;
	m_imgH = baseImg.rows;

	//パース枠の初期化
	InitRect(transImg, baseImg);
	std::cout << "..[今パース枠の初期化をしました]->";
	//ウィンドウ生成
	cv::namedWindow(winName);
	std::cout << "..[今ウィンドウ生成をしました]->";
	//マウスコールバック設定
	cv::setMouseCallback(winName, on_mouse, 0);
	std::cout << "..[今マウスコールバック設定しWindowsのコールバック関数にon_mouseを登録しました]->";
	//初期画面の描画
	DrawTransPinP(tmpImg, transImg, baseImg, m_persRect);
	std::cout << "..[今DrawTransPinP呼び出しPicture-in-Pictureを作成ました]->";
	DrawPersRect(tmpImg, m_persRect, col, 1);
	std::cout << "..[今パースを作成ました]->";
	imshow(winName, tmpImg);
	std::cout << "..[今出来上がったtmpImgをWindownに表示させました]->";
	std::cout << "..[今無限ループに突入し登録してあるコールバック関数がWindowsから呼ばれるまで待機します]->";
	while (1)
	{
		//アンカーポイントのドラッグ時
		if (m_rectIdx>-1) { drawFlag = true; }

		//アンカーポイントの移動終了時
		if (m_mouseUpFlag>0)
		{
			m_mouseUpFlag = -1;
			m_rectIdx = -1;
			drawFlag = true;
		}

		//キーボード処理
		int a = cv::waitKey(1);
		if (a == 27)break;

		if (enterState != m_enterFlag)
		{
			enterState = m_enterFlag;
			if (enterState)col = CV_RGB(255, 0, 0);
			else col = CV_RGB(255, 255, 255);
			drawFlag = true;
		}

		if (drawFlag)
		{
			std::cout << "..[今 C 地点にいます]->";
			DrawTransPinP(tmpImg, transImg, baseImg, m_persRect);
			if (m_enterFlag)DrawPersRect(tmpImg, m_persRect, col, 1);
			imshow(winName, tmpImg);

			drawFlag = false;
			cv::imwrite("outtest1.png", tmpImg);
		}
	}


	return EXIT_SUCCESS;
}

takezawaakiragm
記事: 26
登録日時: 8年前

Re: マウスの位置に画像を再配置 opencv ・vs2013

#5

投稿記事 by takezawaakiragm » 8年前

>>C6b14さん
たびたびありがとうございます。
これで流れ図がわりました。

ただ、右クリック+SHIFT押しても位置がかわりません。

takezawaakiragm
記事: 26
登録日時: 8年前

Re: マウスの位置に画像を再配置 opencv ・vs2013

#6

投稿記事 by takezawaakiragm » 8年前

どのようにして右クリック+SHIFTしたときtransImgの位置をマウスの位置にいどうできるかが
見えてきません。
どなたかご教授お願いします。

アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: マウスの位置に画像を再配置 opencv ・vs2013

#7

投稿記事 by usao » 8年前

>マウスの位置にいどう

DrawTransPinP()なる関数は,画像を変形させて描画するように見えるが,これに対して
「移動」とはどういう意味なのか?
そこが不明瞭なので,まず何がしたいのかがさっぱりわからない.


>ただ、右クリック+SHIFT押しても位置がかわりません

該当操作に対する処理だと思われる場所では,外部変数3個{ mouse_x, mouse_y, drawFlag }の値を更新しているだけであり,
drawFlagはmain()にて参照されているものの,mouse_x,mouse_yについてはどこからも値を参照されていないように見える.
誰も参照しない変数を更新しても所望の結果は生じないものと見受ける.


>どのようにして右クリック+SHIFTしたときtransImgの位置をマウスの位置にいどうできるか

(1)まず,描画結果を生成している関数と思われるDrawTransPinP()の動作(というか引数の意味か)を把握すること
  (※ご自身のコードなのだから,自明かと思うのですが)
(2)あなたの言う「移動」の意味を明確にすること
(3) (2)が明確になったら,それが実現されるように,(1)で把握した事柄に基づきDrawTransPinP()に渡す引数を適切に変更すればよいかと思います.

C6b14

Re: マウスの位置に画像を再配置 opencv ・vs2013

#8

投稿記事 by C6b14 » 8年前

お邪魔します。そういわれれば 「移動」とはどういう意味なのかはっきりしないし プログラムの企図する ところが見えないから追いにくかったのです。いちど もとに 帰って 問題点だけ 抽出しないと 解決しにくいと思うので 最初に質問があった 「 アフィン変換などをする関数を作成する必要があるのでしょうか。」 には ある と思います。 サンプルが あったので 載せます。結構複雑に見えるので 他人が見ても すぐわかるレベルに ブレークダウン してください。
http://csi.nisinippon.com/cv3.png

コード:

/**------------------------------------------------------------*
* @file    overwriteImage.cpp
* @brief   warpAffineを用いて画像内に別の画像を描画する
*          (ピクチャー・イン・ピクチャー機能)
*------------------------------------------------------------*/
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>

#include <stdlib.h>
#include <stdio.h>

using namespace std;

const char winName[] = "picture in picture";

/**------------------------------------------------------------*
* @fn          OpenCVのピクチャーインピクチャ
* @brief       画像内に画像を貼り付ける(並行移動量指定)
* @param[in ]  backImg  背景画像
* @param[in ]  ninjaImg    前景画像
* @param[in ]  tx  前景画像の左上x座標
* @param[in ]  ty  前景画像の左上y座標
*------------------------------------------------------------*/
void PinP_tr(const cv::Mat &backImg, const cv::Mat &ninjaImg, const int tx, const int ty)
{
	//背景画像の作成
	cv::Mat dstImg = backImg.clone();

	//前景画像の変形行列
	cv::Mat mat = (cv::Mat_<double>(2, 3) << 1.0, 0.0, tx, 0.0, 1.0, ty);

	//アフィン変換の実行
	cv::warpAffine(ninjaImg, dstImg, mat, dstImg.size(), CV_INTER_LINEAR, cv::BORDER_TRANSPARENT);
	imshow(winName, dstImg);
}

/**------------------------------------------------------------*
* @fn          OpenCVのピクチャーインピクチャ
* @brief       画像内に画像を貼り付ける(回転角度指定)
* @param[in ]  backImg  背景画像
* @param[in ]  ninjaImg    前景画像
* @param[in ]  angle   回転角度[degree]
*------------------------------------------------------------*/
void PinP_rot(const cv::Mat &backImg, const cv::Mat &ninjaImg, const double angle)
{
	//背景画像の作成
	cv::Mat dstImg = backImg.clone();

	//前景画像の変形行列
	cv::Point2d ctr(ninjaImg.cols / 2, ninjaImg.rows / 2);//前景画像の回転中心
	cv::Mat mat = cv::getRotationMatrix2D(ctr, angle, 1.0);//回転行列の作成

	cv::warpAffine(ninjaImg, dstImg, mat, dstImg.size(), CV_INTER_LINEAR, cv::BORDER_TRANSPARENT);
	imshow(winName, dstImg);
}

/**------------------------------------------------------------*
* @fn          OpenCVのピクチャーインピクチャ
* @brief       画像内に画像を貼り付ける(回転角度、移動量指定)
* @param[in ]  backImg  背景画像
* @param[in ]  ninjaImg    前景画像
* @param[in ]  angle   回転角度[degree]
* @param[in ]  tx  前景画像の左上x座標
* @param[in ]  ty  前景画像の左上y座標
*------------------------------------------------------------*/
void PinP_rot_tr(const cv::Mat &backImg, const cv::Mat &ninjaImg, const double angle, const double tx, const double ty)
{
	//背景画像の作成
	cv::Mat dstImg = backImg.clone();

	//前景画像の変形行列
	cv::Point2d ctr(ninjaImg.cols / 2, ninjaImg.rows / 2);//前景画像の回転中心
	cv::Mat mat = cv::getRotationMatrix2D(ctr, angle, 1.0);//回転行列の作成
	mat.at<double>(0, 2) += tx;//回転後の平行移動量
	mat.at<double>(1, 2) += ty;//回転後の平行移動量

							   //アフィン変換の実行
	cv::warpAffine(ninjaImg, dstImg, mat, dstImg.size(), CV_INTER_LINEAR, cv::BORDER_TRANSPARENT);
	imshow(winName, dstImg);
}

/**------------------------------------------------------------*
* @fn          OpenCVのピクチャーインピクチャ
* @brief       画像内に画像を貼り付ける(位置を座標で指定)
* @param[in ]  backImg  背景画像
* @param[in ]  ninjaImg    前景画像
* @param[in ]  p0  前景画像の左上座標
* @param[in ]  p1  前景画像の右下座標
*------------------------------------------------------------*/
void PinP_point(const cv::Mat &backImg, const cv::Mat &ninjaImg, const cv::Point2f p0, const cv::Point2f p1)
{
	//背景画像の作成
	cv::Mat dstImg = backImg.clone();

	//3組の対応点を作成
	vector<cv::Point2f> src, dst;
	src.push_back(cv::Point2f(0, 0));
	src.push_back(cv::Point2f(ninjaImg.cols, 0));
	src.push_back(cv::Point2f(ninjaImg.cols, ninjaImg.rows));

	dst.push_back(p0);
	dst.push_back(cv::Point2f(p1.x, p0.y));
	dst.push_back(p1);

	//前景画像の変形行列
	cv::Mat mat = cv::getAffineTransform(src, dst);

	//アフィン変換の実行
	cv::warpAffine(ninjaImg, dstImg, mat, dstImg.size(), CV_INTER_LINEAR, cv::BORDER_TRANSPARENT);
	imshow(winName, dstImg);
}

/**------------------------------------------------------------*
* @fn          main
* @brief       メイン関数
*------------------------------------------------------------*/
int main(int argc, char** argv)
{
	cv::Mat backImg, ninjaImg;

	//画像読み込み
	backImg = cv::imread("b.png");  //
	ninjaImg = cv::imread("a.png");

	if ((backImg.data == NULL) || (ninjaImg.data == NULL))
	{
		printf("------------------------------\n");
		printf("image not exist\n");
		printf("------------------------------\n");
		return EXIT_FAILURE;
	}
	else
	{
		printf("------------------------------\n");
		printf("Press ANY key to progress\n");
		printf("------------------------------\n");
	}

	cv::namedWindow(winName); //ウィンドウ生成

							  //平行移動
	PinP_tr(backImg, ninjaImg, 100, 100);
	cv::waitKey(0); //キーボード処理

					//回転
	PinP_rot(backImg, ninjaImg, 45);
	cv::waitKey(0); //キーボード処理

					//回転+平行移動
	PinP_rot_tr(backImg, ninjaImg, 45, 100, 100);
	cv::waitKey(0); //キーボード処理

					//座標指定
	cv::Point2f p0(100, 100);
	cv::Point2f p1(450, 380);
	PinP_point(backImg, ninjaImg, p0, p1);
	cv::waitKey(0); //キーボード処理

	return EXIT_SUCCESS;
}

C6b14

Re: マウスの位置に画像を再配置 opencv ・vs2013

#9

投稿記事 by C6b14 » 8年前

プログラムの全体をブレークダウンするのが大変ならば 

(1)まず,描画結果を生成している関数と思われるDrawTransPinP()の動作(というか引数の意味か)を把握すること
  (※ご自身のコードなのだから,自明かと思うのですが)

が ネックになっているので 

コード:

void DrawTransPinP(cv::Mat &img_dst, const cv::Mat transImg, const cv::Mat baseImg, vector<cv::Point2f> tgtPt)
の tgtPt の vector の 要素 の 働きの設定を 説明してらえばわかり易くなります。 

コード:


std::cout << "..[今 E 地点にいます]->"; std::cout << mouse_x << "," << mouse_y << "\n";
    //書き出し先座標が指定されていない場合は背景画像の中央に配置する
    if (tgtPt.size()<4)
    {
        //座標指定(背景画像の中心に表示する)
        int ltx = (baseImg.cols - transImg.cols) / 2;
        int lty = (baseImg.rows - transImg.rows) / 2;
        int ww = transImg.cols;
        int hh = transImg.rows;
 
        tgtPt.push_back(cv::Point2f(ltx, lty));
        tgtPt.push_back(cv::Point2f(ltx + ww, lty));
        tgtPt.push_back(cv::Point2f(ltx + ww, lty + hh));
        tgtPt.push_back(cv::Point2f(ltx, lty + hh));
    }
の if (tgtPt.size()<4) が tgtPt.size()の値が 4 で //座標指定(背景画像の中心に表示する)以下は実行されないようですし

コード:

/*------------------------------------------------------------
* @function InitRect
* @brief パース設定四角形の初期化
*------------------------------------------------------------*/
void InitRect(cv::Mat transImg, cv::Mat baseImg)
{
	int ltx = (baseImg.cols - transImg.cols) / 2;
	int lty = (baseImg.rows - transImg.rows) / 2;
	int ww = transImg.cols;
	int hh = transImg.rows;

	//変形前
	m_persRect.clear();
	m_persRect.push_back(cv::Point2f(ltx, lty));
	m_persRect.push_back(cv::Point2f(ltx + ww, lty));
	m_persRect.push_back(cv::Point2f(ltx + ww, lty + hh));
	m_persRect.push_back(cv::Point2f(ltx, lty + hh));
}
で設定したままのように見えます。

プログラムは置いて おいて アルゴリズムと 何の目的の為に 「移動」というものが 必要になったかを説明してもらわないと 他人には理解不能  だと思います。

C6b14

Re: マウスの位置に画像を再配置 opencv ・vs2013

#10

投稿記事 by C6b14 » 8年前

ただの ホームページのサンプルだったの ですね。http://kurino.xii.jp/Gallery/Computer/C ... rent2.html では前回の回答で解決ですね。( 最初から おかしい と 思った... )

閉鎖

“C言語何でも質問掲示板” へ戻る