0
点赞
收藏
分享

微信扫一扫

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


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

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

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

​​Image-Py/imagepy​​​​github.comImagePy引擎初探_Filter滤波器引擎解析说明_imagepy​​

源码在此

# -*- 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引擎初探_Filter滤波器引擎解析说明_python_02

下面是难点讲解:

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等等模块

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

ImagePy引擎初探_Filter滤波器引擎解析说明_运算符_04

ImagePy引擎初探_Filter滤波器引擎解析说明_运算符_05

参考原文链接:​​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 运算符 | 菜鸟教程​​


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处理,最后统一生成为一个数据列表。

列表推导式的一个示例:

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

ImagePy引擎初探_Filter滤波器引擎解析说明_字符串_07

参考原文链接:​​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_min​​​和​​a_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

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')]

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的取值主要有以下几种:

ImagePy引擎初探_Filter滤波器引擎解析说明_运算符_08

#!/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()

测试:

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

(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用法示例​​

​​

​​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如何在退出时弹出提示框​​


举报

相关推荐

0 条评论