Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to implement a simple lasso tool with OpenCV

2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article will explain in detail how OpenCV implements a simple lasso tool. The content of the article is of high quality, so the editor shares it for you as a reference. I hope you will have some understanding of the relevant knowledge after reading this article.

The lasso tool in Photoshop can select an arbitrary polygon area through multiple mouse clicks, and then edit this area separately. Here, use OpenCV to achieve a simple function to simulate the lasso tool in Photoshop.

The lasso tool here creates any number of points through multiple clicks on the picture with the left mouse button, and then connects these points into closed polygons after clicking on the right button to form a region to be edited. The keyboard key controls the movement of the area, thus copying the image in the area to other parts of the original image.

First define the following global variables

Const char* winName = "TaoSuoTool"; / / the window name cv::Mat resultImg;// finally displays on the OpenCV window the image cv::Mat areaMask;// mask before cv::Mat foregroundImg;// editing, the polygonal area is actually drawn on the cv::Point maskLocation;// mask position, after moving through the direction key, the mask position changes accordingly all the points that are being clicked before the std::vector drawingPoints;// area is completed std::vector areaPoints / / the polygonal vertices of the region after it is completed

Main function

Int main (int argc, char * * arv) {foregroundImg = cv::imread ("test.jpg"); foregroundImg.copyTo (resultImg); areaMask = cv::Mat::zeros (foregroundImg.size (), CV_8U); cv::imshow (winName, resultImg); maskLocation.x = maskLocation.y = 0; cv::setMouseCallback (winName, OnMouseEvent); int key = cv::waitKeyEx (0) While (key! = VK_ESCAPE) {key = cv::waitKeyEx (0);} return 0;}

Three messages are processed in the mouse callback function OnMouseEvent: left mouse button down, right mouse button down, and mouse movement

Void OnMouseEvent (int event, int x, int y, int flags, void* userdata) {if (event = = cv::EVENT_LBUTTONDOWN) {OnLeftMouseButtonDown (XMague y);} else if (event = = cv::EVENT_RBUTTONDOWN) {OnRightMouseButtonDown (xMague y);} if (event = = cv::EVENT_MOUSEMOVE) {OnMouseMove (xMague y);}}

Define a function before writing a mouse event

Void OnCompleteArea (bool bDrawOutline)

It means to finish editing the current area, including right-clicking to complete the closed polygon, moving the area, and compositing the final image. The parameter bDrawOutline represents the outer outline of the polygon in the drawing area. Right-click to complete the closed polygon and move the region to display the outline (bDrawOutline=true). After composing the final picture, there is no need to display the outline (bDrawOutline=false).

Left mouse button press event: first judge whether there is a previous area, then complete the previous area and do not display the outline of the region, and then start to draw the points of the new region polygons, the points are connected with blue lines, and the points are placed to draw a red rectangle of 4X4.

Void OnLeftMouseButtonDown (int xjinint y) {if (drawingPoints.empty () & & areaPoints.size () > 0) {OnCompleteArea (false);} drawingPoints.push_back (cv::Point (x, y)); cv::rectangle (resultImg, cv::Rect (x-2, y-2,4,4), CV_RGB (255,0,0),-1) If (drawingPoints.size () > = 2) {cv::line (resultImg, drawingPoints [drawingPoints.size ()-2], cv::Point (x, y), CV_RGB (0,0,255,1);} cv::imshow (winName, resultImg);}

Mouse movement event: determine whether the drawingPoints is empty, draw lines and points if there are already points, and draw a line connected to the current position of the mouse.

Void OnMouseMove (int xjinint y) {if (drawingPoints.size () > 0) {foregroundImg.copyTo (resultImg); for (int I = 0; I

< drawingPoints.size() - 1; i++) { cv::rectangle(resultImg, cv::Rect(drawingPoints[i].x - 2, drawingPoints[i].y - 2, 4, 4), CV_RGB(255, 0, 0), -1); cv::line(resultImg, drawingPoints[i], drawingPoints[i + 1], CV_RGB(0, 0, 255), 1); } cv::rectangle(resultImg, cv::Rect(drawingPoints[drawingPoints.size() - 1].x - 2, drawingPoints[drawingPoints.size() - 1].y - 2, 4, 4), CV_RGB(255, 0, 0), -1); cv::line(resultImg, drawingPoints[drawingPoints.size() - 1], cv::Point(x, y), CV_RGB(0, 0, 255), 1); cv::imshow(winName, resultImg); }} 鼠标右键按下事件:如果点个数少于2,不能形成有效区域则不做处理(不考虑多个点共线),否则就在蒙版Area上绘制一个多边形区域,然后调用OnCompleteArea完成区域编辑,这里需要画多边形轮廓,参数传入true。 void OnRightMouseButtonDown(int x,int y){ if (drawingPoints.size() >

= 3) {areaPoints = drawingPoints; std::vector polys; polys.push_back (areaPoints); cv::fillPoly (areaMask, polys, cv::Scalar::all); OnCompleteArea (true);} else {foregroundImg.copyTo (resultImg);} drawingPoints.clear (); cv::imshow (winName, resultImg);}

The following is the implementation of the OnCompleteArea function, where the MergeImages function synthesizes the final image through the mask and the position of the mask. The pixel value in the area of the mask is greater than 0, and all other pixels are 0. The default image is three channels (destImg.at).

Void MergeImages (cv::Mat& destImg, const cv::Mat& srcImg, const cv::Mat& maskImg, int x, int y) {int top = y > 0? Y: 0; int left = x > 0? X: 0; int right = destImg.cols > x + srcImg.cols? X + srcImg.cols: destImg.cols; int bottom = destImg.rows > y + srcImg.rows? Y + srcImg.rows: destImg.rows; for (int I = top; I

< bottom; i++) { for (int j = left; j < right; j++) { int destIndex = i * destImg.cols + j; int srcIndex = (i - top)*srcImg.cols + j - left; int channel = destImg.channels(); if (maskImg.at(i - y, j - x) >

0) {destImg.at (I, j) [0] = srcImg.at (I-y, j-x) [0]; destImg.at (I, j) [1] = srcImg.at (I-y, j-x) [1]; destImg.at (I, j) [2] = srcImg.at (I-y, j-x) [2] } void OnCompleteArea (bool bDrawOutline) {foregroundImg.copyTo (resultImg); MergeImages (resultImg, foregroundImg, areaMask, maskLocation.x, maskLocation.y); if (bDrawOutline) {if (areaPoints.size () > = 3) {std::vector polys; polys.push_back (areaPoints) Cv::polylines (resultImg, polys, true, cv::Scalar::all);}} else {resultImg.copyTo (foregroundImg); areaPoints.clear (); memset (areaMask.data, 0, areaMask.rows*areaMask.cols*areaMask.elemSize ()); maskLocation.x = maskLocation.y = 0;}}

After drawing the area, you can control the movement of the area image by pressing the direction button (you can also move the area by pressing and dragging the left mouse button), the movement is mainly to update the coordinate values of maskLocation and areaPoints, and then call OnCompleteArea (true) to still display the outline of the area.

Void OnDirectionKeyDown (short keyCode) {int x = keyCode = = VK_LEFT?-2: (keyCode = = VK_RIGHT? 2: 0); int y = keyCode = = VK_UP?-2: (keyCode = = VK_DOWN? 2: 0); maskLocation.x + = x; maskLocation.y + = y; for (int I = 0; I

< areaPoints.size(); i++) { areaPoints[i].x += x; areaPoints[i].y += y; } OnCompleteArea(true); cv::imshow(winName, resultImg);} 将上面函数在主函数的按键循环中调用,方向按键通过key的高16位判断,在Windows下可以使用虚拟键码宏表示。 同时为了能看到最终合成的图片加入Enter按键消息处理,将图像合成并去掉轮廓。 int key = cv::waitKeyEx(0);short lowKey = key;short highKey = key >

> 16 cv::imshow while (key! = VK_ESCAPE) {if (key = = VK_RETURN) / / Enter {OnCompleteArea (false); cv::imshow (winName, resultImg);} else if (lowKey = = 0 & & (highKey = = VK_UP | | highKey = = VK_DOWN | | highKey = = VK_LEFT | | highKey = = VK_RIGHT)) {OnDirectionKeyDown (highKey);} key = cv::waitKeyEx (0); lowKey = key HighKey = key > > 16;}

The function of such a simple lasso tool is done (the above code is simplified, and there are many places that can be optimized to make the editor more smooth).

Complete code

# include#include "opencv2/opencv.hpp" # includeconst char* winName = "TaoSuoTool"; cv::Point maskLocation;cv::Mat resultImg;cv::Mat foregroundImg;cv::Mat areaMask;std::vector drawingPoints;std::vector areaPoints;void MergeImages (cv::Mat& destImg, const cv::Mat& srcImg, const cv::Mat& maskImg, int x, int y) {int top = y > 0? Y: 0; int left = x > 0? X: 0; int right = destImg.cols > x + srcImg.cols? X + srcImg.cols: destImg.cols; int bottom = destImg.rows > y + srcImg.rows? Y + srcImg.rows: destImg.rows; for (int I = top; I

< bottom; i++) { for (int j = left; j < right; j++) { int destIndex = i * destImg.cols + j; int srcIndex = (i - top)*srcImg.cols + j - left; int channel = destImg.channels(); if (maskImg.at(i - y, j - x) >

0) {destImg.at (I, j) [0] = srcImg.at (I-y, j-x) [0]; destImg.at (I, j) [1] = srcImg.at (I-y, j-x) [1]; destImg.at (I, j) [2] = srcImg.at (I-y, j-x) [2] } void OnCompleteArea (bool bDrawOutline) {foregroundImg.copyTo (resultImg); MergeImages (resultImg, foregroundImg, areaMask, maskLocation.x, maskLocation.y); if (bDrawOutline) {if (areaPoints.size () > = 3) {std::vector polys; polys.push_back (areaPoints) Cv::polylines (resultImg, polys, true, cv::Scalar::all);}} else {resultImg.copyTo (foregroundImg); areaPoints.clear (); memset (areaMask.data, 0, areaMask.rows*areaMask.cols*areaMask.elemSize ()); maskLocation.x = maskLocation.y = 0 }} void OnLeftMouseButtonDown (int xpenint y) {if (drawingPoints.empty () & & areaPoints.size () > 0) {OnCompleteArea (false);} drawingPoints.push_back (cv::Point (x, y)); cv::rectangle (resultImg, cv::Rect (x-2, y-2,4,4), CV_RGB (255,0,0),-1) If (drawingPoints.size () > = 2) {cv::line (resultImg, drawingPoints [drawingPoints.size ()-2], cv::Point (x, y), CV_RGB (0,0,255), 1);} cv::imshow (winName, resultImg);} void OnRightMouseButtonDown (int x Mint y) {if (drawingPoints.size () > = 3) {areaPoints = drawingPoints; std::vector polys Polys.push_back (areaPoints); cv::fillPoly (areaMask, polys, cv::Scalar::all); OnCompleteArea (true);} else {foregroundImg.copyTo (resultImg);} drawingPoints.clear (); cv::imshow (winName, resultImg);} void OnMouseMove (int x line int y) {if (drawingPoints.size () > 0) {foregroundImg.copyTo (resultImg) For (int I = 0; I

< drawingPoints.size() - 1; i++) { cv::rectangle(resultImg, cv::Rect(drawingPoints[i].x - 2, drawingPoints[i].y - 2, 4, 4), CV_RGB(255, 0, 0), -1); cv::line(resultImg, drawingPoints[i], drawingPoints[i + 1], CV_RGB(0, 0, 255), 1); } cv::rectangle(resultImg, cv::Rect(drawingPoints[drawingPoints.size() - 1].x - 2, drawingPoints[drawingPoints.size() - 1].y - 2, 4, 4), CV_RGB(255, 0, 0), -1); cv::line(resultImg, drawingPoints[drawingPoints.size() - 1], cv::Point(x, y), CV_RGB(0, 0, 255), 1); cv::imshow(winName, resultImg); }}void OnMouseEvent(int event, int x, int y, int flags, void* userdata){ if (event == cv::EVENT_LBUTTONDOWN) { OnLeftMouseButtonDown(x,y); } else if (event == cv::EVENT_RBUTTONDOWN) { OnRightMouseButtonDown(x,y); } if (event == cv::EVENT_MOUSEMOVE) { OnMouseMove(x,y); }} void OnDirectionKeyDown(short keyCode){ int x = keyCode == VK_LEFT ? -2 : (keyCode == VK_RIGHT ? 2 : 0); int y = keyCode == VK_UP ? -2 : (keyCode == VK_DOWN ? 2 : 0); maskLocation.x += x; maskLocation.y += y; for (int i = 0; i < areaPoints.size(); i++) { areaPoints[i].x += x; areaPoints[i].y += y; } OnCompleteArea(true); cv::imshow(winName, resultImg);}int main(int argc, char **arv){ foregroundImg = cv::imread("test.jpg"); foregroundImg.copyTo(resultImg); areaMask = cv::Mat::zeros(foregroundImg.size(), CV_8U); cv::imshow(winName, resultImg); maskLocation.x = maskLocation.y = 0; cv::setMouseCallback(winName, OnMouseEvent); int key = cv::waitKeyEx(0); short lowKey = key; short highKey = key >

> 16; while (key! = VK_ESCAPE) {if (key = = VK_RETURN) / / Enter {OnCompleteArea (false); cv::imshow (winName, resultImg);} else if (lowKey = = 0 & & (highKey = = VK_UP | | highKey = = VK_DOWN | | highKey = = VK_LEFT | | highKey = = VK_RIGHT)) {OnDirectionKeyDown (highKey) } key = cv::waitKeyEx (0); lowKey = key; highKey = key > > 16;} return 0;} so much about how OpenCV implements the simple lasso tool. I hope the above content can be helpful to you and learn more knowledge. If you think the article is good, you can share it for more people to see.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report