最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解如何在pyqt中通過(guò)OpenCV實(shí)現(xiàn)對(duì)窗口的透視變換

 更新時(shí)間:2020年09月20日 08:41:17   作者:之一Yo  
這篇文章主要介紹了如何在pyqt中通過(guò)OpenCV實(shí)現(xiàn)對(duì)窗口的透視變換,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

窗口的透視變換效果

   當(dāng)我們點(diǎn)擊Win10的UWP應(yīng)用中的小部件時(shí),會(huì)發(fā)現(xiàn)小部件會(huì)朝著鼠標(biāo)點(diǎn)擊位置凹陷下去,而且不同的點(diǎn)擊位置對(duì)應(yīng)著不同的凹陷情況,看起來(lái)就好像小部件在屏幕上不只有x軸和y軸,甚至還有一個(gè)z軸。要做到這一點(diǎn),其實(shí)只要對(duì)窗口進(jìn)行透視變換即可。下面是對(duì)Qt的窗口和按鈕進(jìn)行透視變換的效果:

在這里插入圖片描述

具體代碼

   1.下面先定義一個(gè)類(lèi),它的作用是將傳入的 QPixmap 轉(zhuǎn)換為numpy 數(shù)組,然后用 opencvwarpPerspective 對(duì)數(shù)組進(jìn)行透視變換,最后再將 numpy 數(shù)組轉(zhuǎn)為 QPixmap 并返回;

# coding:utf-8

import cv2 as cv
import numpy
from PyQt5.QtGui import QImage, QPixmap


class PixmapPerspectiveTransform:
 """ 透視變換基類(lèi) """

 def __init__(self, pixmap=None):
  """ 實(shí)例化透視變換對(duì)象\n
  Parameter
  ---------
  src : numpy數(shù)組 """
  self.pixmap = pixmap

 def setPixmap(self, pixmap: QPixmap):
  """ 設(shè)置被變換的QPixmap """
  self.pixmap = QPixmap
  self.src=self.transQPixmapToNdarray(pixmap)
  self.height, self.width = self.src.shape[:2]
  # 變換前后的邊角坐標(biāo)
  self.srcPoints = numpy.float32(
   [[0, 0], [self.width - 1, 0], [0, self.height - 1],
    [self.width - 1, self.height - 1]])

 def setDstPoints(self, leftTop: list, rightTop, leftBottom, rightBottom):
  """ 設(shè)置變換后的邊角坐標(biāo) """
  self.dstPoints = numpy.float32(
   [leftTop, rightTop, leftBottom, rightBottom])

 def getPerspectiveTransform(self, imWidth, imHeight, borderMode=cv.BORDER_CONSTANT, borderValue=[255, 255, 255, 0]) -> QPixmap:
  """ 透視變換圖像,返回QPixmap\n
  Parameters
  ----------
  imWidth : 變換后的圖像寬度\n
  imHeight : 變換后的圖像高度\n
  borderMode : 邊框插值方式\n
  borderValue : 邊框顏色
  """
  # 如果是jpg需要加上一個(gè)透明通道
  if self.src.shape[-1] == 3:
   self.src = cv.cvtColor(self.src, cv.COLOR_BGR2BGRA)
  # 透視變換矩陣
  perspectiveMatrix = cv.getPerspectiveTransform(
   self.srcPoints, self.dstPoints)
  # 執(zhí)行變換
  self.dst = cv.warpPerspective(self.src, perspectiveMatrix, (
   imWidth, imHeight), borderMode=borderMode, borderValue=borderValue)
  # 將ndarray轉(zhuǎn)換為QPixmap
  return self.transNdarrayToQPixmap(self.dst)

 def transQPixmapToNdarray(self, pixmap: QPixmap):
  """ 將QPixmap轉(zhuǎn)換為numpy數(shù)組 """
  width, height = pixmap.width(), pixmap.height()
  channels_count = 4
  image = pixmap.toImage() # type:QImage
  s = image.bits().asstring(height * width * channels_count)
  # 得到BGRA格式數(shù)組
  array = numpy.fromstring(s, numpy.uint8).reshape(
   (height, width, channels_count))
  return array

 def transNdarrayToQPixmap(self, array):
  """ 將numpy數(shù)組轉(zhuǎn)換為QPixmap """
  height, width, bytesPerComponent = array.shape
  bytesPerLine = 4 * width
  # 默認(rèn)數(shù)組維度為 m*n*4
  dst = cv.cvtColor(array, cv.COLOR_BGRA2RGBA)
  pix = QPixmap.fromImage(
   QImage(dst.data, width, height, bytesPerLine, QImage.Format_RGBA8888))
  return pix

  2.接下來(lái)就是這篇博客的主角——PerspectiveWidget,當(dāng)我們的鼠標(biāo)單擊這個(gè)類(lèi)實(shí)例化出來(lái)的窗口時(shí),窗口會(huì)先通過(guò) self.grab() 被渲染為QPixmap,然后調(diào)用 PixmapPerspectiveTransform 中的方法對(duì)QPixmap進(jìn)行透視變換,拿到透視變換的結(jié)果后只需隱藏窗口內(nèi)的小部件并通過(guò) PaintEvent 將結(jié)果繪制到窗口上即可。雖然思路很通順,但是實(shí)際操作起來(lái)會(huì)發(fā)現(xiàn)對(duì)于透明背景的窗口進(jìn)行透視變換時(shí),與透明部分交界的部分會(huì)被插值上半透明的像素。對(duì)于本來(lái)就屬于深色的像素來(lái)說(shuō)這沒(méi)什么,但是如果像素是淺色的就會(huì)帶來(lái)很大的視覺(jué)干擾,你會(huì)發(fā)現(xiàn)這些淺色部分旁邊被描上了一圈黑邊,我們先將這個(gè)圖像記為img_1。img_1差不多長(zhǎng)這個(gè)樣子,可以很明顯看出白色的文字圍繞著一圈黑色的描邊。

透明背景

為了解決這個(gè)煩人的問(wèn)題,我又對(duì)桌面上的窗口進(jìn)行截屏,再次透視變換。注意是桌面上看到的窗口,這時(shí)的窗口肯定是會(huì)有背景的,這時(shí)的透視變換就不會(huì)存在上述問(wèn)題,記這個(gè)透視變換完的圖像為img_2。但實(shí)際上我們本來(lái)是不想要img_2中的背景的,所以只要將img_2中的背景替換完img_1中的透明背景,下面是具體代碼:

# coding:utf-8

import numpy as np

from PyQt5.QtCore import QPoint, Qt
from PyQt5.QtGui import QPainter, QPixmap, QScreen, QImage
from PyQt5.QtWidgets import QApplication, QWidget

from my_functions.get_pressed_pos import getPressedPos
from my_functions.perspective_transform_cv import PixmapPerspectiveTransform


class PerspectiveWidget(QWidget):
 """ 可進(jìn)行透視變換的窗口 """

 def __init__(self, parent=None, isTransScreenshot=False):
  super().__init__(parent)
  self.__visibleChildren = []
  self.__isTransScreenshot = isTransScreenshot
  self.__perspectiveTrans = PixmapPerspectiveTransform() 
  self.__screenshotPix = None
  self.__pressedPix = None
  self.__pressedPos = None

 @property
 def pressedPos(self) -> str:
  """ 返回鼠標(biāo)點(diǎn)擊位置 """
  return self.__pressedPos

 def mousePressEvent(self, e):
  """ 鼠標(biāo)點(diǎn)擊窗口時(shí)進(jìn)行透視變換 """
  super().mousePressEvent(e)
  self.grabMouse()
  pixmap = self.grab()
  self.__perspectiveTrans.setPixmap(pixmap)
  # 根據(jù)鼠標(biāo)點(diǎn)擊位置的不同設(shè)置背景封面的透視變換
  self.__setDstPointsByPressedPos(getPressedPos(self,e))
  # 獲取透視變換后的QPixmap
  self.__pressedPix = self.__getTransformPixmap()
  # 對(duì)桌面上的窗口進(jìn)行截圖
  if self.__isTransScreenshot:
   self.__adjustTransformPix()
  # 隱藏本來(lái)看得見(jiàn)的小部件
  self.__visibleChildren = [
   child for child in self.children() if hasattr(child, 'isVisible') and child.isVisible()]
  for child in self.__visibleChildren:
   if hasattr(child, 'hide'):
    child.hide()
  self.update()

 def mouseReleaseEvent(self, e):
  """ 鼠標(biāo)松開(kāi)時(shí)顯示小部件 """
  super().mouseReleaseEvent(e)
  self.releaseMouse()
  self.__pressedPos = None
  self.update()
  # 顯示小部件
  for child in self.__visibleChildren:
   if hasattr(child, 'show'):
    child.show()

 def paintEvent(self, e):
  """ 繪制背景 """
  super().paintEvent(e)
  painter = QPainter(self)
  painter.setRenderHints(QPainter.Antialiasing | QPainter.HighQualityAntialiasing |
        QPainter.SmoothPixmapTransform)
  painter.setPen(Qt.NoPen)
  # 繪制背景圖片
  if self.__pressedPos:
   painter.drawPixmap(self.rect(), self.__pressedPix)

 def __setDstPointsByPressedPos(self,pressedPos:str):
  """ 通過(guò)鼠標(biāo)點(diǎn)擊位置設(shè)置透視變換的四個(gè)邊角坐標(biāo) """
  self.__pressedPos = pressedPos
  if self.__pressedPos == 'left':
   self.__perspectiveTrans.setDstPoints(
    [5, 4], [self.__perspectiveTrans.width - 2, 1],
    [3, self.__perspectiveTrans.height - 3],
    [self.__perspectiveTrans.width - 2, self.__perspectiveTrans.height - 1])
  elif self.__pressedPos == 'left-top':
   self.__perspectiveTrans.setDstPoints(
    [6, 5], [self.__perspectiveTrans.width - 1, 1],
    [1, self.__perspectiveTrans.height - 2],
    [self.__perspectiveTrans.width - 2, self.__perspectiveTrans.height - 1])
  elif self.__pressedPos == 'left-bottom':
   self.__perspectiveTrans.setDstPoints(
    [2, 3], [self.__perspectiveTrans.width - 3, 0],
    [4, self.__perspectiveTrans.height - 4],
    [self.__perspectiveTrans.width - 2, self.__perspectiveTrans.height - 2])
  elif self.__pressedPos == 'top':
   self.__perspectiveTrans.setDstPoints(
    [3, 5], [self.__perspectiveTrans.width - 4, 5],
    [1, self.__perspectiveTrans.height - 2],
    [self.__perspectiveTrans.width - 2, self.__perspectiveTrans.height - 2])
  elif self.__pressedPos == 'center':
   self.__perspectiveTrans.setDstPoints(
    [3, 4], [self.__perspectiveTrans.width - 4, 4],
    [3, self.__perspectiveTrans.height - 3],
    [self.__perspectiveTrans.width - 4, self.__perspectiveTrans.height - 3])
  elif self.__pressedPos == 'bottom':
   self.__perspectiveTrans.setDstPoints(
    [2, 2], [self.__perspectiveTrans.width - 3, 3],
    [3, self.__perspectiveTrans.height - 3],
    [self.__perspectiveTrans.width - 4, self.__perspectiveTrans.height - 3])
  elif self.__pressedPos == 'right-bottom':
   self.__perspectiveTrans.setDstPoints(
    [1, 0], [self.__perspectiveTrans.width - 3, 2],
    [1, self.__perspectiveTrans.height - 2],
    [self.__perspectiveTrans.width - 5, self.__perspectiveTrans.height - 4])
  elif self.__pressedPos == 'right-top':
   self.__perspectiveTrans.setDstPoints(
    [0, 1], [self.__perspectiveTrans.width - 7, 5],
    [2, self.__perspectiveTrans.height - 1],
    [self.__perspectiveTrans.width - 2, self.__perspectiveTrans.height - 2])
  elif self.__pressedPos == 'right':
   self.__perspectiveTrans.setDstPoints(
    [1, 1], [self.__perspectiveTrans.width - 6, 4],
    [2, self.__perspectiveTrans.height - 1],
    [self.__perspectiveTrans.width - 4, self.__perspectiveTrans.height - 3])

 def __getTransformPixmap(self) -> QPixmap:
  """ 獲取透視變換后的QPixmap """
  pix = self.__perspectiveTrans.getPerspectiveTransform(
   self.__perspectiveTrans.width, self.__perspectiveTrans.height).scaled(
    self.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)
  return pix

 def __getScreenShot(self) -> QPixmap:
  """ 對(duì)窗口口所在的桌面區(qū)域進(jìn)行截圖 """
  screen = QApplication.primaryScreen() # type:QScreen
  pos = self.mapToGlobal(QPoint(0, 0)) # type:QPoint
  pix = screen.grabWindow(
   0, pos.x(), pos.y(), self.width(), self.height())
  return pix

 def __adjustTransformPix(self):
  """ 對(duì)窗口截圖再次進(jìn)行透視變換并將兩張圖融合,消除可能存在的黑邊 """
  self.__screenshotPix = self.__getScreenShot()
  self.__perspectiveTrans.setPixmap(self.__screenshotPix)
  self.__screenshotPressedPix = self.__getTransformPixmap()
  # 融合兩張透視圖
  img_1 = self.__perspectiveTrans.transQPixmapToNdarray(self.__pressedPix)
  img_2 = self.__perspectiveTrans.transQPixmapToNdarray(self.__screenshotPressedPix)
  # 去除非透明背景部分  
  mask = img_1[:, :, -1] == 0
  img_2[mask] = img_1[mask]
  self.__pressedPix = self.__perspectiveTrans.transNdarrayToQPixmap(img_2)

mousePressEvent中調(diào)用了一個(gè)全局函數(shù) getPressedPos(widget,e) ,如果將窗口分為九宮格,它就是用來(lái)獲取判斷鼠標(biāo)的點(diǎn)擊位置落在九宮格的哪個(gè)格子的,因?yàn)槲以谄渌胤接杏玫剿?,所以沒(méi)將其設(shè)置為PerspectiveWidget的方法成員。下面是這個(gè)函數(shù)的代碼:

# coding:utf-8

from PyQt5.QtGui import QMouseEvent


def getPressedPos(widget, e: QMouseEvent) -> str:
 """ 檢測(cè)鼠標(biāo)并返回按下的方位 """
 pressedPos = None
 width = widget.width()
 height = widget.height()
 leftX = 0 <= e.x() <= int(width / 3)
 midX = int(width / 3) < e.x() <= int(width * 2 / 3)
 rightX = int(width * 2 / 3) < e.x() <= width
 topY = 0 <= e.y() <= int(height / 3)
 midY = int(height / 3) < e.y() <= int(height * 2 / 3)
 bottomY = int(height * 2 / 3) < e.y() <= height
 # 獲取點(diǎn)擊位置
 if leftX and topY:
  pressedPos = 'left-top'
 elif midX and topY:
  pressedPos = 'top'
 elif rightX and topY:
  pressedPos = 'right-top'
 elif leftX and midY:
  pressedPos = 'left'
 elif midX and midY:
  pressedPos = 'center'
 elif rightX and midY:
  pressedPos = 'right'
 elif leftX and bottomY:
  pressedPos = 'left-bottom'
 elif midX and bottomY:
  pressedPos = 'bottom'
 elif rightX and bottomY:
  pressedPos = 'right-bottom'
 return pressedPos

使用方法

   很簡(jiǎn)單,只要將代碼中的QWidget替換為PerspectiveWidget就可以享受透視變換帶來(lái)的無(wú)盡樂(lè)趣。要想向gif中那樣對(duì)按鈕也進(jìn)行透視變換,只要按代碼中所做的那樣重寫(xiě)mousePressEvent、mouseReleaseEventpaintEvent 即可,如果有對(duì)按鈕使用qss,記得在paintEvent中加上super().paintEvent(e),這樣樣式表才會(huì)起作用??傊蚣芤呀?jīng)給出,具體操作取決于你。如果你喜歡這篇博客的話,記得點(diǎn)個(gè)贊哦(o゚▽゚)o 。順便做個(gè)下期預(yù)告:在gif中可以看到界面切換時(shí)帶了彈入彈出的動(dòng)畫(huà),在下一篇博客中我會(huì)對(duì)如何實(shí)現(xiàn)QStackedWidget的界面切換動(dòng)畫(huà)進(jìn)行介紹,敬請(qǐng)期待~~

到此這篇關(guān)于詳解如何在pyqt中通過(guò)OpenCV實(shí)現(xiàn)對(duì)窗口的透視變換的文章就介紹到這了,更多相關(guān)pyqt OpenCV窗口透視變換內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Python基于pyopencv人臉識(shí)別并繪制GUI界面

    Python基于pyopencv人臉識(shí)別并繪制GUI界面

    本文詳細(xì)講解了Python基于pyopencv人臉識(shí)別并繪制GUI界面,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-12-12
  • Tensorflow:轉(zhuǎn)置函數(shù) transpose的使用詳解

    Tensorflow:轉(zhuǎn)置函數(shù) transpose的使用詳解

    今天小編就為大家分享一篇Tensorflow:轉(zhuǎn)置函數(shù) transpose的使用詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-02-02
  • matplotlib之pyplot模塊坐標(biāo)軸范圍設(shè)置(autoscale(),xlim(),ylim())

    matplotlib之pyplot模塊坐標(biāo)軸范圍設(shè)置(autoscale(),xlim(),ylim())

    這篇文章主要介紹了matplotlib之pyplot模塊坐標(biāo)軸范圍設(shè)置(autoscale(),xlim(),ylim()),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • AI與Python人工智能遺傳算法

    AI與Python人工智能遺傳算法

    這篇文章主要為大家介紹了AI與Python人工智能遺傳算法的詳解教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-05-05
  • Python批量操作Excel文件詳解

    Python批量操作Excel文件詳解

    因?yàn)椴┲魉诘牡胤?,需要每周整理全校的青年大學(xué)習(xí)數(shù)據(jù),Excel操作本身不難,但是這種毫無(wú)意義的體力勞動(dòng)做久了就會(huì)很無(wú)趣,剛好我想起來(lái)上學(xué)期接觸過(guò)Python,想著能不能試一下,取代這種無(wú)意義的勞動(dòng)
    2021-11-11
  • Python 字典dict使用介紹

    Python 字典dict使用介紹

    這篇文章主要介紹了Python 字典dict使用介紹,需要的朋友可以參考下
    2014-11-11
  • Python使用matplotlib和pandas實(shí)現(xiàn)的畫(huà)圖操作【經(jīng)典示例】

    Python使用matplotlib和pandas實(shí)現(xiàn)的畫(huà)圖操作【經(jīng)典示例】

    這篇文章主要介紹了Python使用matplotlib和pandas實(shí)現(xiàn)的畫(huà)圖操作,結(jié)合實(shí)例形式分析了Python基于matplotlib和pandas的數(shù)值運(yùn)算與圖形顯示操作相關(guān)實(shí)現(xiàn)技巧,并對(duì)部分代碼的圖形顯示進(jìn)行了顯示效果測(cè)試,需要的朋友可以參考下
    2018-06-06
  • Python安裝Bs4及使用方法

    Python安裝Bs4及使用方法

    這篇文章主要介紹了Python安裝Bs4及使用方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • Python文件讀取的3種方法及路徑轉(zhuǎn)義

    Python文件讀取的3種方法及路徑轉(zhuǎn)義

    這篇文章主要介紹了Python文件讀取的3種方法及路徑轉(zhuǎn)義,本文分別給出讀取文件的代碼實(shí)例,最后講解了路徑轉(zhuǎn)義的相關(guān)知識(shí)、小技巧,需要的朋友可以參考下
    2015-06-06
  • pyqt5實(shí)現(xiàn)俄羅斯方塊游戲

    pyqt5實(shí)現(xiàn)俄羅斯方塊游戲

    這篇文章主要介紹了pyqt5實(shí)現(xiàn)俄羅斯方塊游戲,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-01-01

最新評(píng)論

神农架林区| 厦门市| 汉源县| 兴业县| 逊克县| 莲花县| 张家川| 榆林市| 灵宝市| 阿拉善左旗| 阿巴嘎旗| 河东区| 中山市| 南平市| 宿州市| 永州市| 定边县| 平谷区| 揭东县| 四川省| 丹棱县| 连平县| 静宁县| 谷城县| 沾化县| 石阡县| 上思县| 临沭县| 长春市| 宾川县| 凤台县| 清苑县| 西城区| 辽宁省| 涿鹿县| 沙坪坝区| 大冶市| 游戏| 大田县| 西平县| 瓦房店市|