• 使用Qt5+CMake实现图片的区域选择(附源码)


    近期研发涉及到了图片的区域选择,找来一些资料一直不能很满意,所以自己实现了一个。
    实现步骤如下。源码可以点击ImageAOI获取。
    Gitee clone: ImageAOI.
    如下资料来自源码的README。


    ImageAOI (XLabel): AOI Selection Based on Qt5

    Dependency

    Qt >= 5.0

    Usage

    • Double click to trigger the selector
    • Mouse scrolling t zoom in/out
      Usage

    Reference (Appreciation)

    ImageCropper


    本工具的实现灵感来自ImageCropper。部分源码也做了参考,在此表示非常感。
    实现功能如上面图片所示,方面,快捷。操作起来很有快感。

    实现步骤

    1. 创建一个基于QLabel的类XLabel

    注意需要引用#include <QLabel>。此段代码主要在xlabel.h文件中。

    class XLabel : public QLabel
    {
        Q_OBJECT  
    
    public:
        explicit XLabel(QWidget* parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags());
        ~XLabel();
    
    }
    

    注意 ·Q_OBJECT·必须不能忘记,否则无法顺利生成。

    2. 加入所必须的一些临时变量。

    image是图片本身,需要载入。scale为缩放变量。selection_mode是用来决定是否显示区域的变量,双击图片区域激活或取消显示。

    private:
    	QImage image;
    	float scale;
    
    	// area selection
    	bool selection_mode;
    	QRectF croppingRect;
    
    	bool mouse_pressed;
    	QPoint start_mouse_pos;
    	QRectF last_static_rect;
    	CursorPosition cursor_pos;
    

    3. 加入图片的鼠标事件和绘图事件函数。

    函数主要继承自QWidget。只有捕获到事件,才能顺利操作。

    protected:
    	virtual void mousePressEvent(QMouseEvent* event);
    
    	virtual void mouseMoveEvent(QMouseEvent* event);
    
    	virtual void mouseReleaseEvent(QMouseEvent* event);
    
        virtual void wheelEvent(QWheelEvent* e);
    
    	// double click
    	virtual void mouseDoubleClickEvent(QMouseEvent *event);
    
    	void paintEvent(QPaintEvent* _event) override;
    

    4. 实现事件函数。

    这里分为两部分,鼠标事件用于更新变量。绘图事件中变量的基础上,完成绘制和渲染。

    • 鼠标事件如下
    
    void XLabel::mousePressEvent(QMouseEvent* event)
    {
    	if (Qt::LeftButton == event->button())
    	{
    		mouse_pressed = true;
    		start_mouse_pos = originPoint(event->pos());
    		last_static_rect = croppingRect;
    	}
    
    	updateCursorIcon(originPoint(event->pos()));
    }
    
    void XLabel::mouseMoveEvent(QMouseEvent* event)
    {
    	auto origin_p = originPoint(event->pos());
    
    	if (!mouse_pressed)
    	{
    		cursor_pos = cursorPosition(croppingRect, origin_p);
    
    		updateCursorIcon(origin_p);
    	}
    	else if (cursor_pos != CursorPositionUndefined)
    	{
    		QPointF mouseDelta;
    		mouseDelta.setX(origin_p.x() - start_mouse_pos.x());
    		mouseDelta.setY(origin_p.y() - start_mouse_pos.y());
    
    		int dx = WIDGET_MINIMUM_SIZE.width() / 2;
    		int dy = WIDGET_MINIMUM_SIZE.height() / 2;
    		//
    		if (cursor_pos != CursorPositionMiddle) 
    		{
    			QRectF newGeometry =
    				calculateGeometry(
    					last_static_rect,
    					cursor_pos,
    					mouseDelta);
    		
    			if (!newGeometry.isNull()) 
    			{
    
    				// boudary check
    				if (newGeometry.x() < image.width() - dx && newGeometry.y () < image.height() - dy 
    					&& newGeometry.width() + newGeometry.x() > dx
    					&& newGeometry.height() + newGeometry.y() > dy)
    				{
    					if (newGeometry.width() >= WIDGET_MINIMUM_SIZE.width() && newGeometry.height() >= WIDGET_MINIMUM_SIZE.height())
    					{
    						croppingRect = newGeometry;
    					}
    				}
    
    			}
    		}
    		else
    		{
    			auto new_pt = last_static_rect.topLeft() + mouseDelta;
    			if (new_pt.x() < image.width() - dx && new_pt.y() < image.height() - dy
    				&& croppingRect.width() + new_pt.x() > dx
    				&& croppingRect.height() + new_pt.y() > dy)
    			{
    				croppingRect.moveTo(last_static_rect.topLeft() + mouseDelta);
    			}
    		}
    
    		update();
    		
    	}
    
    
    }
    
    void XLabel::mouseReleaseEvent(QMouseEvent* event) 
    {
    	mouse_pressed = false;
    	updateCursorIcon(originPoint(event->pos()));
    
    	// single-click signal
        //emit clicked(int(0.5 + event->x()/scale), int(0.5+event->y()/scale));
    	printf("[ %d, %d] 
    ", (int)event->x(), (int)event->y());
    }
    
    void XLabel::wheelEvent(QWheelEvent * e)
    {
    	int numDegrees = e->delta() / 8;
    	int numSteps = numDegrees / 15;
    
    	float k = numDegrees > 0 ? 1.09 : 0.90;
    
    	k = k *scale;
    
    	setScale(k );
     
    }
    
    void XLabel::mouseDoubleClickEvent(QMouseEvent * event)
    {
    	
    	selection_mode = !selection_mode;
    	update();
    }
    
    • 绘图事件如下,核心的绘制都在此处。
    void XLabel::paintEvent(QPaintEvent * _event)
    {	
    	QLabel::paintEvent(_event);
    
    	if (image.isNull()) return;
    	int width = image.width();
    	
    
    	//printf("Selection: %d
    ", (int)selection_mode);
    
    
    	QPixmap rawImage = QPixmap::fromImage(image);
    	QPainter widgetPainter;
    	widgetPainter.begin(&rawImage);
    
    	// Image boundary
    	QRectF rect(2, 2, image.width()-4, image.height()-4);
    	QPen pen0(Qt::darkRed);
    	if (selection_mode)
    	{
    		pen0 = QPen(Qt::darkGreen);
    	}
    	pen0.setWidth(4);
    	widgetPainter.setPen(pen0);
    	widgetPainter.drawRect(rect);
    
    
    	if (selection_mode)
    	{
    		if (croppingRect.isNull()) {
    			const int width = image.width() / 2  - 4;
    			const int height = image.height() / 2 - 4;
    			croppingRect.setSize(QSize(width, height));
    
    			float x = (image.width() - croppingRect.width()) / 2-2;
    			float y = (image.height() - croppingRect.height()) / 2-2;
    			croppingRect.moveTo(x, y);
    		}
    
    		//qDebug() << "H: " << croppingRect.height();
    
    
    		// 1. bg color
    		widgetPainter.setBrush(QBrush(QColor(0, 0x6f, 0, 120)));
    		QPen pen;
    		pen.setColor(Qt::yellow);
    		pen.setWidth(2);
    		widgetPainter.setPen(pen);
    		
    		widgetPainter.drawRect(croppingRect);
    		// pos
    		widgetPainter.setPen(Qt::red);
    		QFont font;
    		font.setPointSize(croppingRect.width() >240 ? 16 : 10);
    		widgetPainter.setFont(font);
    		auto tl = croppingRect.topLeft();
    		widgetPainter.drawText(QPoint(tl.x() + 6, tl.y() + 24), QString("(%1, %2, %3, %4)").arg(croppingRect.x()).arg(croppingRect.y())
    			.arg(croppingRect.width()).arg(croppingRect.height()));
    
    		// 2. corner boxes
    		{
    			widgetPainter.setPen(Qt::green);
    			widgetPainter.setBrush(QBrush(Qt::white));
    			const int edgelen = 8;
    
    			// Вспомогательные X координаты
    			int leftXCoord = croppingRect.left() - edgelen/2+1;
    			int centerXCoord = croppingRect.center().x() - edgelen / 2;
    			int rightXCoord = croppingRect.right() - edgelen / 2+1;
    			// Вспомогательные Y координаты
    			int topYCoord = croppingRect.top() - edgelen / 2+1;
    			int middleYCoord = croppingRect.center().y() - edgelen / 2;
    			int bottomYCoord = croppingRect.bottom() - edgelen / 2+1;
    			//
    			const QSize pointSize(edgelen, edgelen);
    			//
    			QVector<QRect> points;
    			points
    				// левая сторона
    				<< QRect(QPoint(leftXCoord, topYCoord), pointSize)
    				<< QRect(QPoint(leftXCoord, middleYCoord), pointSize)
    				<< QRect(QPoint(leftXCoord, bottomYCoord), pointSize)
    				// центр
    				<< QRect(QPoint(centerXCoord, topYCoord), pointSize)
    				<< QRect(QPoint(centerXCoord, middleYCoord), pointSize)
    				<< QRect(QPoint(centerXCoord, bottomYCoord), pointSize)
    				// правая сторона
    				<< QRect(QPoint(rightXCoord, topYCoord), pointSize)
    				<< QRect(QPoint(rightXCoord, middleYCoord), pointSize)
    				<< QRect(QPoint(rightXCoord, bottomYCoord), pointSize);
    			//
    			widgetPainter.drawRects(points);
    		}
    
    
    
    		// 3. center dash lines
    		{
    			QPen dashPen(Qt::white);
    			dashPen.setStyle(Qt::DashLine);
    			widgetPainter.setPen(dashPen); 
    
    			widgetPainter.drawLine(
    				QPoint(croppingRect.center().x(), croppingRect.top()),
    				QPoint(croppingRect.center().x(), croppingRect.bottom()));
    			// ... горизонтальная
    			widgetPainter.drawLine(
    				QPoint(croppingRect.left(), croppingRect.center().y()),
    				QPoint(croppingRect.right(), croppingRect.center().y()));
    		}
    		
    
    	}
    
    
    
    	widgetPainter.end();
    	this->setPixmap(rawImage.scaledToWidth(scale*width)); 
    		
    }
    

    4. 实现主函数,完成一个界面,测试此类是否可行。

    实现一个最简单的QLabel,完成显示,并加入XLabel显示到界面上,完成鼠标拖动测试。

    #include <QApplication>
    #include <QDialog>
    #include <QVBoxLayout>
    #include <QLabel>
    
    #include "src/xlabel.h"
    
    
    int main(int argc, char *argv[])
    {
    	QApplication a(argc, argv);
    
    	QDialog wgt;
    	wgt.setMouseTracking(true);
    	QVBoxLayout* layout = new QVBoxLayout(&wgt);
    	wgt.setLayout(layout);
    
    	auto xlabel = new XLabel(&wgt);
    	layout->addWidget(xlabel);
    	layout->addStretch();
    
    	QImage img("../logo_s.png");
    	xlabel->setImageMap(img);
    
    	wgt.resize(640, 480);
    	wgt.show();
    
    
    	return a.exec();
    }
    

    完整代码可访问ImageAOI
    Gitee clone: ImageAOI.

  • 相关阅读:
    iOS 文件操作--归档和解档
    iOS中UITabBarController的使用
    Objective-C基础知识点总结,字符串操作,数组操作,字典操作
    Objective-C中协议和分类总结
    Objective-C文件操作之NSCoding协议之小练习
    浅谈Objective-C继承和多态
    Objective-C内存管理基础知识
    MySort(选做)的实现
    20175308 2018-2019-2 实验四 《Android开发基础》实验报告
    JAVA 第十一周学习总结
  • 原文地址:https://www.cnblogs.com/zjulion/p/11342075.html
Copyright © 2020-2023  润新知