ImagePy引擎初探_Filter滤波器引擎解析说明

最近在学图形学绘制,想到了ImagePy框架的ROI涂抹交互很方便,于是啃起了绘制代码。啃完了绘制代码,接下来对绘制代码的邻居引擎代码进行了开啃。

这里主要对ImagePy中一个Filter滤波器引擎进行难点讲解。

让我们好好学习Python中高手的代码吧。

Image-Py/imagepy​github.com图标

源码在此

# -*- coding: utf-8 -*-
"""
Created on Fri Dec  2 23:48:33 2016
@author: yxl
"""

import wx
import threading
import numpy as np

from ... import IPy
from ...ui.panelconfig import ParaDialog
from ...core.manager import TextLogManager, ImageManager, \
WindowsManager, TaskManager, WidgetsManager, DocumentManager
from time import time

def process_channels(plg, ips, src, des, para):
    if ips.channels>1 and not 'not_channel' in plg.note:
        for i in range(ips.channels):
            rst = plg.run(ips, src if src is None else src[:,:,i], des[:,:,i], para)
            if not rst is des and not rst is None:
                des[:,:,i] = rst
    else:
        rst = plg.run(ips, src, des, para)
        if not rst is des and not rst is None:
            des[:] = rst
    return des

def process_one(plg, ips, src, img, para, callafter=None):
    TaskManager.add(plg)
    start = time()
    
    transint = '2int' in plg.note and ips.dtype in (np.uint8, np.uint16)
    transfloat = '2float' in plg.note and not ips.dtype in (np.complex128, np.float32, np.float64)
    if transint: 
        buf = img.astype(np.int32)
        src = src.astype(np.int32)
    if transfloat: 
        buf = img.astype(np.float32)
        src = src.astype(np.float32)
    rst = process_channels(plg, ips, src, buf if transint or transfloat else img, para)
    if not img is rst and not rst is None:
        imgrange = {np.uint8:(0,255), np.uint16:(0, 65535)}[img.dtype.type]
        np.clip(rst, imgrange[0], imgrange[1], out=img)
    if 'auto_msk' in plg.note and not ips.get_msk() is None:
        msk = True ^ ips.get_msk()
        img[msk] = src[msk]
    IPy.set_info('%s: cost %.3fs'%(ips.title, time()-start))
    ips.update()
    TaskManager.remove(plg)
    if not callafter is None:callafter()
    
def process_stack(plg, ips, src, imgs, para, callafter=None):
    TaskManager.add(plg)
    start = time()

    transint = '2int' in plg.note and ips.dtype in (np.uint8, np.uint16)
    transfloat = '2float' in plg.note and not ips.dtype in (np.complex128, np.float32, np.float64)
    if transint: 
        buf =  imgs[0].astype(np.int32)
        src = src.astype(np.int32)
    elif transfloat: 
        buf = imgs[0].astype(np.float32)
        src = src.astype(np.float32)
    else: src = src * 1

    for i,n in zip(imgs,list(range(len(imgs)))):
        #sleep(0.5)
        plg.progress(n, len(imgs))
        if 'auto_snap' in plg.note : src[:] = i
        if transint or transfloat: buf[:] = i
        rst = process_channels(plg, ips, src, buf if transint or transfloat else i, para)
        if not i is rst and not rst is None:
            imgrange = {np.uint8:(0,255), np.uint16:(0,65535)}[i.dtype.type]
            np.clip(rst, imgrange[0], imgrange[1], out=i)
        if 'auto_msk' in plg.note and not ips.get_msk() is None:
            msk = True ^ ips.get_msk()
            i[msk] = src[msk]
    IPy.set_info('%s: cost %.3fs'%(ips.title, time()-start))
    ips.update()
    TaskManager.remove(plg)
    if not callafter is None:callafter()
    

class Filter:
    title = 'Filter'
    modal = True
    note = []
    'all, 8-bit, 16-bit, int, rgb, float, not_channel, not_slice, req_roi, auto_snap, auto_msk, preview, 2int, 2float'
    para = None
    view = None
    prgs = (None, 1)

    def __init__(self, ips=None):
        if ips==None:ips = IPy.get_ips()
        self.dialog = None
        self.ips = ips
        
    def progress(self, i, n):
        self.prgs = (i, n)

    def show(self, temp=ParaDialog):
        self.dialog = temp(WindowsManager.get(), self.title)
        self.dialog.init_view(self.view, self.para, 'preview' in self.note, modal=self.modal)

        self.dialog.on_help = lambda : IPy.show_md(self.title, DocumentManager.get(self.title))
        self.dialog.set_handle(lambda x:self.preview(self.ips, x))
        if self.modal: return self.dialog.ShowModal() == wx.ID_OK
        self.dialog.on_ok = lambda : self.ok(self.ips)
        self.dialog.on_cancel = lambda : self.cancel(self.ips)
        self.dialog.Show()
    
    def run(self, ips, snap, img, para = None):
        return 255-img
        
    def check(self, ips):
        note = self.note
        if ips == None:
            IPy.alert('No image opened!')
            return False
        elif 'req_roi' in note and ips.roi == None:
            IPy.alert('No Roi found!')
            return False
        elif not 'all' in note:
            if ips.get_imgtype()=='rgb' and not 'rgb' in note:
                IPy.alert('Do not surport rgb image')
                return False
            elif ips.get_imgtype()=='8-bit' and not '8-bit' in note:
                IPy.alert('Do not surport 8-bit image')
                return False
            elif ips.get_imgtype()=='16-bit' and not '16-bit' in note:
                IPy.alert('Do not surport 16-bit uint image')
                return False
            elif ips.get_imgtype()=='32-int' and not 'int' in note:
                IPy.alert('Do not surport 32-bit int uint image')
                return False
            elif 'float' in ips.get_imgtype() and not 'float' in note:
                IPy.alert('Do not surport float image')
                return False
        return True
        
    def preview(self, ips, para):
        process_one(self, ips, ips.snap, ips.img, para, None)
        
    def load(self, ips):return True
          
    def ok(self, ips, para=None, callafter=None):
        if para == None:
            para = self.para
            if not 'not_slice' in self.note and ips.get_nslices()>1:
                if para == None:para = {}
            if para!=None and 'stack' in para:del para['stack']
        win = WidgetsManager.getref('Macros Recorder')
        if ips.get_nslices()==1 or 'not_slice' in self.note:
            # process_one(self, ips, ips.snap, ips.img, para)
            if IPy.uimode() == 'no':
                process_one(self, ips, ips.snap, ips.img, para, callafter)
            else: threading.Thread(target = process_one, args = 
                (self, ips, ips.snap, ips.img, para, callafter)).start()
            if win!=None: win.write('{}>{}'.format(self.title, para))
        elif ips.get_nslices()>1:
            has, rst = 'stack' in para, None
            if not has:
                rst = IPy.yes_no('Run every slice in current stacks?')
            if 'auto_snap' in self.note and self.modal:ips.reset()
            if has and para['stack'] or rst == 'yes':
                para['stack'] = True
                #process_stack(self, ips, ips.snap, ips.imgs, para)
                if IPy.uimode() == 'no':
                    process_stack(self, ips, ips.snap, ips.imgs, para, callafter)
                else: threading.Thread(target = process_stack, args = 
                    (self, ips, ips.snap, ips.imgs, para, callafter)).start()
                if win!=None: win.write('{}>{}'.format(self.title, para))
            elif has and not para['stack'] or rst == 'no': 
                para['stack'] = False
                #process_one(self, ips, ips.snap, ips.img, para)
                if IPy.uimode() == 'no':
                    process_one(self, ips, ips.snap, ips.img, para, callafter)
                else: threading.Thread(target = process_one, args = 
                    (self, ips, ips.snap, ips.img, para, callafter)).start()
                if win!=None: win.write('{}>{}'.format(self.title, para))
            elif rst == 'cancel': pass
        #ips.update()
        
    def cancel(self, ips):
        if 'auto_snap' in self.note:
            ips.swap()
            ips.update()
            
    def start(self, para=None, callafter=None):
        ips = self.ips
        if not self.check(ips):return
        if not self.load(ips):return
        if 'auto_snap' in self.note:ips.snapshot()
        
        if para!=None:
            self.ok(self.ips, para, callafter)
        elif self.view==None:
            if not self.__class__.show is Filter.show:
                if self.show():
                    self.ok(self.ips, para, callafter)
            else: self.ok(self.ips, para, callafter)
        elif self.modal:
            if self.show():
                self.ok(ips, None, callafter)
            else:self.cancel(ips)
            self.dialog.Destroy()
        else: self.show()

    def __del__(self):
        print('filter del')

 

推荐大神写的Filter引擎解析文章

ImagePy解析:9 -- Filter引擎及其衍生的图像取反插件​qixinbo.info

 

ImagePy_Learn学习系列

土盐:ImagePy_Learn | 图形学绘制代码学习:core\draw\fill.py

土盐:ImagePy_Learn | 图形学绘制代码学习:paint.py

土盐:ImagePy_Learn | 图形学绘制代码学习:core\draw\polygonfill.py

土盐:基于ImagePy编写插件之迷途探索WXPython

 

下面是难点讲解:

from ...ui.panelconfig import ParaDialog

import //模块.函数

from…import // 直接使用函数名使用就可以了

(1) 调用模块属性的区别

import 模块名
模块名.xxx = 引用

from 模块名 import *
xxx = 拷贝  # 能修改属性值  

函数,类... : "import 模块名" 和 "from 模块名 import *" 都是引用。

(2)私有属性两种导入的区别

# . 类中的私有属性
# 本质做了一个名字重整
class test()
    self.__name

__name 名字重整成 _test__name。

_littlethree : 模块的私有属性(数据)。

  • from 模块 import * : 导入模块时,会跳过私有属性;
  • import 模块 : 通过引用可以访问私有属性

from ...

core文件夹内的filter.py从core的上级目录里面找ui.panelconfig等等模块

参考原文链接:from…import * 语句与 import 区别

src if src is None else src[:,:,i]

if not callafter is None:callafter()

src是none,则对src什么也不做

not callafter 是 None,则调用callafter()

Python身份运算符

身份运算符用于比较两个对象的存储单元

运算符描述实例is is 是判断两个标识符是不是引用自一个对象x is y, 类似 id(x) == id(y) , 如果引用的是同一个对象则返回 True,否则返回 Falseis notis not 是判断两个标识符是不是引用自不同对象 x is not y , 类似 id(a) != id(b)。如果引用的不是同一个对象则返回结果 True,否则返回 False。

s 用于判断两个变量是否引用同一个, 会对比其中两个变量的地址。

is not 用于判断两个变量是否引用自不同的对象。也会比较地址

is与== 比较:

==判断变量的值是否一样,不比较地址的。

参考原文链接:Python 运算符 | 菜鸟教程

https://blog.csdn.net/yinjiajia1010/article/details/81260632

 

transint = '2int' in plg.note and ips.dtype in (np.uint8, np.uint16)

transint 是等号后面判断是否的布尔值,0或者1。

Python成员运算符

运算符描述实例in 如果在指定的序列中找到值返回 True,否则返回 False。 x 在 y 序列中 , 如果 x 在 y 序列中返回 True。not in如果在指定的序列中没有找到值返回 True,否则返回 False。x 不在 y 序列中 , 如果 x 不在 y 序列中返回 True。

参考原文链接:Python 运算符 | 菜鸟教程

 

rst = process_channels(plg, ips, src, buf if transint or transfloat else img, para)

rst表示result

列表推导式总共有两种形式:

①[x for x in data if condition]

此处if主要起条件判断作用,data数据中只有满足if条件的才会被留下,最后统一生成为一个数据列表

②[exp1 if condition else exp2 for x in data]

此处if...else主要起赋值作用,当data中的数据满足if条件时将其做exp1处理,否则按照exp2处理,最后统一生成为一个数据列表。

列表推导式的一个示例:

参考原文链接:http://www.mamicode.com/info-detail-1904804.html

人类身份验证 - SegmentFault

imgrange = {np.uint8:(0,255), np.uint16:(0, 65535)}[img.dtype.type]

numpy.dtype.type

dtype.type

用于实例化此数据类型的标量的类型对象。

import numpy as np

image = np.array([[35, 37, 39, 36, 34, 31], 
                  [33, 32, 32, 33, 31, 33], 
                  [30, 34, 36, 37, 36, 32],
                  [33, 30, 28, 30, 28, 28]], dtype=np.uint32)

dtype_range = {np.bool_: (False, True),
               np.bool8: (False, True),
               np.uint8: (0, 255),
               np.uint16: (0, 65535),
               np.int8: (-128, 127),
               np.int16: (-32768, 32767),
               np.int64: (-2**63, 2**63 - 1),
               np.uint64: (0, 2**64 - 1),
               np.int32: (-2**31, 2**31 - 1),
               np.uint32: (0, 2**32 - 1),
               np.float32: (-1, 1),
               np.float64: (-1, 1)}

print(image.dtype.type)
print(dtype_range[image.dtype.type])

<type 'numpy.uint32'>
(0, 4294967295L)

参考原文链接:numpy.dtype.type - NumPy v1.17 Manual

KeyError with np.uint32 and numpy.uint32

np.clip(rst, imgrange[0], imgrange[1], out=img)

将范围外的数强制转化为范围内的数

  • def clip(a, a_min, a_max, out=None): 将数组a中的所有数限定到范围a_min和a_max中,即az中所有比a_min小的数都会强制变为a_min,a中所有比a_max大的数都会强制变为a_max.
  • 其中a_mina_max可以为一个和a一样大小的数组(列表也可以,只要是类似数组的结构就是可行的),则数组中相应位置的元素进行比较。
  • out 是可选项,表示把强制截取后的结果放到这个数组中,但是out中的数组必须和a形状一样
Examples
    --------
    >>> a = np.arange(10)
    >>> np.clip(a, 1, 8)
    array([1, 1, 2, 3, 4, 5, 6, 7, 8, 8])
    >>> a
    array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
    >>> np.clip(a, 3, 6, out=a)
    array([3, 3, 3, 3, 4, 5, 6, 6, 6, 6])
    >>> a = np.arange(10)
    >>> a
    array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
    >>> np.clip(a, [3, 4, 1, 1, 1, 4, 4, 4, 4, 4], 8)
    array([3, 4, 2, 3, 4, 5, 6, 7, 8, 8])

参考原文链接:np.clip截取函数 - cloud&ken - 博客园

msk = True ^ ips.get_msk()

^是按位异或逻辑运算符,比如5^6,其实是101^110,结果是011,所以5^6的答案是3

参考原文链接:https://blog.csdn.net/wxy_csdn_world/article/details/80759915

IPy.set_info('%s: cost %.3fs'%(ips.title, time()-start))

一种字符串格式化的语法, 基本用法是将值插入到%s占位符的字符串中。

%s,表示格式化一个对象为字符

string = "good"  #类型为字符串
print("string=%s" %string)   #输出的打印结果为 string=good  
 
print("string=%3s" %string) # 输出的打印结果为 string=good(数字3的意思是:字符串的长度为3。当字符串的长度大于3时,按照字符串的长度打印出结果)

print("string=%(+)6s" %string)  # 输出的打印结果为 string=  good(当字符串的长度小于6时,在字符串的左侧填补空格,使得字符串的长度为6)

print("string=%-6s" %string)  # 输出的打印结果为 string=good  (当字符串的长度小于6时,在字符串的右侧填补空格,使得字符串的长度为6)
 
#小数点后的数字表示截取的字符串长度

print("string=%.3(6)s" %string)  # 输出的打印结果为 string=goo(good)(%.3s的意思是:截取字符串的前3个字符,当截取字符串的字符长度大于字符串时,输出的结果是整个字符串)

print("string=%a.bs" %string)  # 先是根据小数点后面的数字b截取字符串,当截取的字符串长度小于a时,需要在字符串的左侧填补空格,使得字符串的长度变为a

print("string=%*.*s" %(6, 3, string))  # %*.*s表示精度, 两个*的值分别由%string前面被两个逗号隔开的数值来指定

参考原文链接:python 中的" %s"%用法

for i,n in zip(imgs,list(range(len(imgs)))):

zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同,利用 * 号操作符,可以将元组解压为列表。

zip([iterable, ...]):参数iterabl -- 一个或多个迭代器

x=["a","1"]
y=["b","2"]
z = list(zip(x,y))
print (list(zip(x,y)))
print (list(zip(*z)))
结果:
[('a', 'b'), ('1', '2')]
[('a', '1'), ('b', '2')]

参考原文链接:https://blog.csdn.net/qq_28979491/article/details/91356093

self.dialog = temp(WindowsManager.get(), self.title)

Python wxPython库消息对话框MessageDialog用法。

具体如下:

消息对话框即我们平时说的Messagebox,看看它的原型,下面是wxWidgets中的原型定义,C++风格,与python风格的区别就是wx前缀与后面名称直接相连,例如wxMessageDialog,在wxpython中使用时就是wx.MessageDialog

wxMessageDialog(wxWindow* parent, const wxString& message, const wxString& caption = "Message box", long style = wxOK | wxCANCEL, const wxPoint& pos = wxDefaultPosition)

其各参数不多做介绍,主要看看ShowModal()方法,它使用应用程序在对话框关闭前不能响应其它窗口的用户事件,返回一个整数,取值如下:

wx.ID_YES, wx.ID_NO, wx.ID_CANCEL, wx.ID_OK

另外,style的取值主要有以下几种:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import wx
class MyFrame(wx.Frame):
 def __init__(self, parent, id):
  wx.Frame.__init__(self, parent, id, u'测试面板Panel', size = (600, 300))
  #创建面板
  panel = wx.Panel(self)
  #在Panel上添加Button
  button = wx.Button(panel, label = u'关闭', pos = (150, 60), size = (100, 60))
  #绑定单击事件
  self.Bind(wx.EVT_BUTTON, self.OnCloseMe, button)
 def OnCloseMe(self, event):
  dlg = wx.MessageDialog(None, u"消息对话框测试", u"标题信息", wx.YES_NO | wx.ICON_QUESTION)
  if dlg.ShowModal() == wx.ID_YES:
   self.Close(True)
  dlg.Destroy()
if __name__ == '__main__':
 app = wx.PySimpleApp()
 frame = MyFrame(parent = None, id = -1)
 frame.Show()
 app.MainLoop()

测试:

(1) 提示对话框

QMessageBox.information(self,'标题','提示信息','OK','Cancel','其他')

解释:上面参数中的'OK','Cancel','其他'表示对话框的可选项,一般默认是OK.

(2) 询问对话框

QMessageBox.question(self,'标题','询问信息')

(3) 警告对话框

QMessageBox.warning(self,'标题','提示信息')

(4) 严重警告对话框

QMessageBox.critical(self,'标题','提示信息')

(5) 关于对话框

QMessageBox.information(self,'标题','提示信息')

(6) AboutQt对话框

QMessageBox.information(self,'标题','提示信息')

这个是pyqt内置的,所以参数不能修改,只能像下面这样写:

参考原文链接:Python wxPython库消息对话框MessageDialog用法示例

https://blog.csdn.net/u011146423/article/details/85291002

https://blog.csdn.net/chengqiuming/article/details/78601002

5. 对话框

threading.Thread(target = process_one, args =
(self, ips, ips.snap, ips.img, para, callafter)).start()

# 导入Python标准库中的Thread模块 
from threading import Thread 
# 创建一个线程 
mthread = threading.Thread(target=function_name, args=(function_parameter1, function_parameterN)) 
# 启动刚刚创建的线程 
mthread .start()

function_name: 需要线程去执行的方法名

args: 线程执行方法接收的参数,该属性是一个元组,如果只有一个参数也需要在末尾加逗号。

  • start() 方法是启动一个子线程,线程名就是我们定义的name
  • run() 方法并不启动一个新线程,就是在主线程中调用了一个普通函数而已。

参考原文链接:python语言中threading.Thread类的使用方法 - 429512065 - 博客园

Python 多线程 start()和run()方法的区别(三)

if win!=None: win.write('{}>{}'.format(self.title, para))

format 函数可以接受不限个参数,位置可以不按顺序。

基本语法是通过 {} 和 : 来代替以前的 % 。

参考原文链接:Python format 格式化函数

self.dialog.Destroy()

正常退出对话框

dialog = wx.TextEntryDialog(None,"Input the subitem name","Subitem", style=wx.OK|wx.CANCEL)
if dialog.ShowModal() == wx.ID_OK:
    subitem = dialog.GetValue()
else:
    dialog.Destroy()

 

def OnCloseWindow(self,event):
       dlg = wx.MessageDialog(None,'Exit , Are you sure ?',
                               'Confirmation',wx.YES_NO|wx.ICON_QUESTION)
        retCode = dlg.ShowModal()
        if(retCode == wx.ID_YES):
            self.Destroy()
        else:
            pass

参考原文链接:人类身份验证 - SegmentFault

wxPython如何在退出时弹出提示框-CSDN论坛

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yantuguiguziPGJ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值