欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

python opencv畫局部放大圖實(shí)例教程

 更新時(shí)間:2021年10月11日 09:41:11   作者:拜陽  
這篇文章主要給大家介紹了關(guān)于python opencv畫局部放大圖的相關(guān)資料,獲取鼠標(biāo)的單擊相應(yīng)以及鼠標(biāo)的移動(dòng)信息,進(jìn)行放大功能的實(shí)現(xiàn),需要的朋友可以參考下

這項(xiàng)功能的目的是為了方便使用opencv做圖像標(biāo)注工具。

為什么要畫局部放大圖?

在做圖像數(shù)據(jù)標(biāo)注時(shí),很難一次就做到精準(zhǔn)標(biāo)注,經(jīng)常需要微調(diào)才能達(dá)到比較好的標(biāo)注效果。如果目標(biāo)比較小,即使微調(diào)也難以做到精準(zhǔn),所以就需要另外一個(gè)窗口對標(biāo)注區(qū)域進(jìn)行局部放大以方便微調(diào)。

程序邏輯

本文中標(biāo)注信息以矩形框作為示例,矩形框是圖像標(biāo)注中最常用到的一種標(biāo)注信息形態(tài)。其他標(biāo)注信息的設(shè)計(jì)邏輯雷同。

程序主要邏輯是:鼠標(biāo)在任意窗口中做的操作都要同步映射到另外一個(gè)窗口。主要通過兩個(gè)維度進(jìn)行控制:1. 鼠標(biāo)在主窗口還是在放大窗口;2. 鼠標(biāo)的操作。其中主窗口指的是用來顯示完整大圖的窗口,放大窗口是用來顯示局部放大區(qū)域的窗口。

  • 鼠標(biāo)在主窗口
    • 鼠標(biāo)移動(dòng):以當(dāng)前點(diǎn)(current_pt)為中心顯示一塊放大區(qū)域,顯示的范圍由一個(gè)參數(shù)指定default_zoom_image_size。為了確保以current_pt為中心向四個(gè)方向均能取到default_zoom_image_size一半的值,current_pt的活動(dòng)范圍是大圖減去一定的邊緣區(qū)域,這個(gè)邊緣區(qū)域就是default_zoom_image_size的一半。在活動(dòng)范圍內(nèi),current_pt等同于鼠標(biāo)當(dāng)前點(diǎn),在邊緣區(qū)域,current_pt等于距離鼠標(biāo)當(dāng)前點(diǎn)最近的邊緣點(diǎn)。
    • 鼠標(biāo)畫矩形框:左鍵按下并拖動(dòng)可以畫矩形框,矩形框不會(huì)超出大圖邊界
    • 刪除矩形框:有兩種情況會(huì)刪除矩形框 —— 1. 左鍵畫出來的矩形框面積為零;2. 右鍵點(diǎn)擊
  • 鼠標(biāo)在放大窗口
    • 鼠標(biāo)移動(dòng):什么也不做。
    • 鼠標(biāo)畫矩形框:左鍵按下并拖動(dòng)可以畫矩形框,矩形框可以超出放大窗口的邊界,但是不會(huì)超出大圖邊界
    • 刪除矩形框:同主窗口的兩種情況
    • 鼠標(biāo)左鍵或右鍵點(diǎn)擊:可以起到選擇current_pt的作用

總體來說,我們在主窗口做任何操作都可以比較隨意,因?yàn)橹鞔翱谥谐司匦慰蛲?,圖像本身不發(fā)生變化。而在放大窗口中做操作就需要相對保守一些,不然會(huì)非常亂,以至于無法操作。所以我們在主窗口中畫矩形框時(shí),放大窗口會(huì)隨著矩形框不停改變;而我們在放大窗口中畫矩形框時(shí),放大窗口則保持不變(不然眼花繚亂)。

程序?qū)嵗?br />

把程序邏輯搞明白后下面代碼就比較容易懂了。細(xì)節(jié)就不多說了,只說一個(gè)需要注意的大方向:我們需要為兩個(gè)窗口各寫一個(gè)鼠標(biāo)回調(diào)函數(shù)。

先看一下單純畫矩形框的代碼可能會(huì)有助于理解下面程序中的一些細(xì)節(jié):opencv-python鼠標(biāo)畫矩形框:cv2.rectangle()

# -*- coding: utf-8 -*-
import cv2


class Rect(object):
    def __init__(self, pt1=(0, 0), pt2=(0, 0)):
        self.tl = pt1
        self.br = pt2
        self.regularize()

    def regularize(self):
        """
        make sure tl = TopLeft point, br = BottomRight point
        """
        tl = (min(self.tl[0], self.br[0]), min(self.tl[1], self.br[1]))
        br = (max(self.tl[0], self.br[0]), max(self.tl[1], self.br[1]))
        self.tl = tl
        self.br = br

    def get_center(self):
        """
        get center point of Rect
        """
        center_x = (self.tl[0] + self.br[0]) // 2
        center_y = (self.tl[1] + self.br[1]) // 2
        return center_x, center_y

    def get_width(self):
        """
        get width of Rect
        """
        return abs(self.br[0] - self.tl[0])

    def get_height(self):
        """
        get height of Rect
        """
        return abs(self.br[1] - self.tl[1])

    def height_over_width(self):
        """
        ratio of height over width
        """
        return self.get_height() / self.get_width()

    def get_area(self):
        """
        get area of Rect
        """
        return self.get_width() * self.get_height()


class DrawZoom(object):
    def __init__(self, image, color,
                 current_pt=(0, 0),
                 default_zoom_image_size=(256, 256)):
        self.original_image = image
        self.color = color
        self.thickness = 2
        self.current_pt = current_pt
        self.default_zoom_image_size = default_zoom_image_size

        self.rect_in_big_image = Rect()
        self.rect_in_zoom_image = Rect()
        self.zoom_offset = (0, 0)

        self.is_drawing_big = False
        self.is_drawing_zoom = False
        self.exist_rect = False

        self.big_image = image.copy()
        self.zoom_image = None
        self.zoom_image_backup = None
        self.get_zoom_image()

    def get_image_height(self):
        """
        get height of big image
        """
        return self.original_image.shape[0]

    def get_image_width(self):
        """
        get width of big image
        """
        return self.original_image.shape[1]

    def get_margin_height(self):
        """
        get height of margin. in the margin area of big image, coordinate of
        current_pt does NOT change
        """
        return self.default_zoom_image_size[0] // 2

    def get_margin_width(self):
        """
        get width of margin
        """
        return self.default_zoom_image_size[1] // 2

    def get_zoom_image(self, height_ratio_expand=0.2, width_ratio_expand=0.2):
        """
        get zoom image for two cases: the rect exists or not.
        height_ratio_expand and width_ratio_expand are used for expanding some
        area of rect
        """
        if not self.exist_rect:
            self.get_zoom_image_for_current_pt()
        elif self.rect_in_big_image.get_area() > 0:
            self.get_zoom_image_for_rect(height_ratio_expand,
                                         width_ratio_expand)

    def get_zoom_image_for_current_pt(self):
        """
        get zoom image for current mouse point (when rect does not exist)
        """
        # (x, y) is center coordinate
        x = max(self.current_pt[0], self.get_margin_width())
        x = min(x, self.get_image_width() - self.get_margin_width())
        y = max(self.current_pt[1], self.get_margin_height())
        y = min(y, self.get_image_height() - self.get_margin_height())

        tl_x = x - self.get_margin_width()
        tl_y = y - self.get_margin_height()
        br_x = x + self.get_margin_width()
        br_y = y + self.get_margin_height()
        tl_x, tl_y, br_x, br_y = self.shrink_rect(tl_x, tl_y, br_x, br_y)
        self.zoom_image = self.big_image[tl_y:br_y, tl_x:br_x]
        self.zoom_image_backup = self.original_image[tl_y:br_y, tl_x:br_x]
        self.zoom_offset = (tl_x, tl_y)

    def get_zoom_image_for_rect(self, height_ratio_expand, width_ratio_expand):
        """
        get zoom image when rect exists
        """
        if self.rect_in_big_image.get_area() == 0:
            return None
        height_over_width_for_win_zoom = \
            self.default_zoom_image_size[1] / self.default_zoom_image_size[0]
        center = self.rect_in_big_image.get_center()
        if self.rect_in_big_image.height_over_width() > \
                height_over_width_for_win_zoom:
            half_height = int(0.5 * (1 + height_ratio_expand) *
                              self.rect_in_big_image.get_height())
            half_width = int(half_height / height_over_width_for_win_zoom)
        else:
            half_width = int(0.5 * (1 + width_ratio_expand) *
                             self.rect_in_big_image.get_width())
            half_height = int(half_width * height_over_width_for_win_zoom)
        tl_x = center[0] - half_width
        tl_y = center[1] - half_height
        br_x = center[0] + half_width
        br_y = center[1] + half_height
        tl_x, tl_y, br_x, br_y = self.shrink_rect(tl_x, tl_y, br_x, br_y)
        self.zoom_image = self.big_image[tl_y:br_y, tl_x:br_x]
        self.zoom_image_backup = self.original_image[tl_y:br_y, tl_x:br_x]
        self.zoom_offset = (tl_x, tl_y)

    @staticmethod
    def clip(value, low, high):
        """
        clip value between low and high
        """
        output = max(value, low)
        output = min(output, high)
        return output

    def shrink_point(self, x, y):
        """
        shrink point (x, y) to inside big image
        """
        x_shrink = self.clip(x, 0, self.get_image_width())
        y_shrink = self.clip(y, 0, self.get_image_height())
        return x_shrink, y_shrink

    def shrink_rect(self, pt1_x, pt1_y, pt2_x, pt2_y):
        """
        shrink rect to inside big image
        """
        pt1_x = self.clip(pt1_x, 0, self.get_image_width())
        pt1_y = self.clip(pt1_y, 0, self.get_image_height())
        pt2_x = self.clip(pt2_x, 0, self.get_image_width())
        pt2_y = self.clip(pt2_y, 0, self.get_image_height())
        rect = Rect((pt1_x, pt1_y), (pt2_x, pt2_y))
        rect.regularize()
        tl_x, tl_y = rect.tl
        br_x, br_y = rect.br
        return tl_x, tl_y, br_x, br_y

    def reset_big_image(self):
        """
        reset big_image (for show) using original image
        """
        self.big_image = self.original_image.copy()

    def reset_zoom_image(self):
        """
        reset zoom_image (for show) using the zoom image backup
        """
        self.zoom_image = self.zoom_image_backup.copy()

    def draw_rect_in_big_image(self):
        """
        draw rect in big image
        """
        cv2.rectangle(self.big_image,
                      self.rect_in_big_image.tl, self.rect_in_big_image.br,
                      color=self.color, thickness=self.thickness)

    def draw_rect_in_zoom_image(self):
        """
        draw rect in zoom image
        """
        cv2.rectangle(self.zoom_image,
                      self.rect_in_zoom_image.tl, self.rect_in_zoom_image.br,
                      color=self.color, thickness=self.thickness)

    def update_drawing_big(self):
        """
        update drawing big image, and map the corresponding area to zoom image
        """
        if self.exist_rect:
            self.draw_rect_in_big_image()
        self.get_zoom_image()

    def update_drawing_zoom(self):
        """
        update drawing big and zoom image when drawing rect in zoom image
        """
        if self.exist_rect:
            self.draw_rect_in_big_image()
            self.draw_rect_in_zoom_image()


def onmouse_big_image(event, x, y, flags, draw_zoom):
    if event == cv2.EVENT_LBUTTONDOWN:
        # pick first point of rect
        draw_zoom.is_drawing_big = True
        draw_zoom.rect_in_big_image.tl = (x, y)
        draw_zoom.exist_rect = True
    elif draw_zoom.is_drawing_big and event == cv2.EVENT_MOUSEMOVE:
        # pick second point of rect and draw current rect
        draw_zoom.rect_in_big_image.br = draw_zoom.shrink_point(x, y)
        draw_zoom.reset_big_image()
        draw_zoom.update_drawing_big()
    elif event == cv2.EVENT_LBUTTONUP:
        # finish drawing current rect
        draw_zoom.is_drawing_big = False
        draw_zoom.rect_in_big_image.br = draw_zoom.shrink_point(x, y)
        draw_zoom.rect_in_big_image.regularize()
        if draw_zoom.rect_in_big_image.get_area() == 0:
            draw_zoom.reset_big_image()
            draw_zoom.rect_in_big_image = Rect()
            draw_zoom.exist_rect = False
        draw_zoom.update_drawing_big()
    elif (not draw_zoom.is_drawing_big) and event == cv2.EVENT_RBUTTONDOWN:
        # right button down to erase current rect
        draw_zoom.rect_in_big_image = Rect()
        draw_zoom.exist_rect = False
        draw_zoom.reset_big_image()
        draw_zoom.update_drawing_big()
    else:
        # default case: mouse move without rect
        draw_zoom.current_pt = (x, y)
        draw_zoom.update_drawing_big()


def onmouse_zoom_image(event, x, y, flags, draw_zoom):
    if event == cv2.EVENT_LBUTTONDOWN:
        # pick first point of rect
        draw_zoom.is_drawing_zoom = True
        draw_zoom.rect_in_zoom_image.tl = (x, y)
        draw_zoom.rect_in_big_image.tl = (x + draw_zoom.zoom_offset[0],
                                          y + draw_zoom.zoom_offset[1])
        draw_zoom.exist_rect = True
    elif draw_zoom.is_drawing_zoom and event == cv2.EVENT_MOUSEMOVE:
        # pick second point of rect and draw current rect
        draw_zoom.rect_in_zoom_image.br = (x, y)
        draw_zoom.rect_in_big_image.br = draw_zoom.shrink_point(
            x + draw_zoom.zoom_offset[0], y + draw_zoom.zoom_offset[1])
        draw_zoom.reset_zoom_image()
        draw_zoom.reset_big_image()
        draw_zoom.update_drawing_zoom()
    elif event == cv2.EVENT_LBUTTONUP:
        # finish drawing current rect
        draw_zoom.is_drawing_zoom = False
        draw_zoom.rect_in_big_image.br = draw_zoom.shrink_point(
            x + draw_zoom.zoom_offset[0], y + draw_zoom.zoom_offset[1])
        draw_zoom.rect_in_big_image.regularize()
        if draw_zoom.rect_in_big_image.get_area() == 0:
            draw_zoom.reset_big_image()
            draw_zoom.rect_in_big_image = Rect()
            draw_zoom.rect_in_zoom_image = Rect()
            draw_zoom.exist_rect = False
            draw_zoom.current_pt = draw_zoom.shrink_point(
                x + draw_zoom.zoom_offset[0], y + draw_zoom.zoom_offset[1])
        draw_zoom.update_drawing_big()
    elif (not draw_zoom.is_drawing_big) and event == cv2.EVENT_RBUTTONDOWN:
        # right button down to erase current rect
        draw_zoom.rect_in_big_image = Rect()
        draw_zoom.rect_in_zoom_image = Rect()
        draw_zoom.exist_rect = False
        draw_zoom.reset_big_image()
        draw_zoom.current_pt = draw_zoom.shrink_point(
            x + draw_zoom.zoom_offset[0], y + draw_zoom.zoom_offset[1])
        draw_zoom.update_drawing_big()
    else:
        # mousemove in zoom image will not change the content of image
        pass


if __name__ == '__main__':
    WIN_NAME_BIG = 'big_image'
    WIN_NAME_ZOOM = 'zoom_image'
    image = cv2.imread('1.jpg')
    draw_zoom = DrawZoom(image, (0, 255, 0))
    cv2.namedWindow(WIN_NAME_BIG, 0)
    cv2.namedWindow(WIN_NAME_ZOOM, 0)
    cv2.setMouseCallback(WIN_NAME_BIG, onmouse_big_image, draw_zoom)
    cv2.setMouseCallback(WIN_NAME_ZOOM, onmouse_zoom_image, draw_zoom)
    while True:
        cv2.imshow(WIN_NAME_BIG, draw_zoom.big_image)
        cv2.imshow(WIN_NAME_ZOOM, draw_zoom.zoom_image)
        key = cv2.waitKey(30)
        if key == 27:  # ESC
            break
    cv2.destroyAllWindows()

結(jié)果:

總結(jié)

到此這篇關(guān)于python opencv畫局部放大圖的文章就介紹到這了,更多相關(guān)python opencv局部放大圖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論