最近在学图形学绘制,想到了ImagePy框架的ROI涂抹交互很方便,于是啃起了绘制代码。啃完了绘制代码,接下来对绘制代码的邻居引擎代码进行了开啃。
这里主要对ImagePy中一个Filter滤波器引擎进行难点讲解。
让我们好好学习Python中高手的代码吧。
Image-Py/imagepygithub.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_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
参考原文链接: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() 方法是启动一个子线程,线程名就是我们定义的namerun() 方法并不启动一个新线程,就是在主线程中调用了一个普通函数而已。
参考原文链接: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如何在退出时弹出提示框-论坛