系统正在维护中
方案一
nginx.conf1
2
3
4
5
6server {
listen 80
server_name localhost
# rewrite ^(.*)$ /maintainace_page.html break;
}
方案二
拦截503
如何优雅告知用户,网站正在升级维护
不间断升级维护
“蓝绿部署”
方案一
nginx.conf1
2
3
4
5
6server {
listen 80
server_name localhost
# rewrite ^(.*)$ /maintainace_page.html break;
}
方案二
拦截503
如何优雅告知用户,网站正在升级维护
“蓝绿部署”
OpenCV是计算机视觉领域应用最广泛的开源工具包,基于C/C++,支持Linux/Windows/MacOS/Android/iOS,并提供了Python,Matlab和Java等语言的接口
1 | pip install -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple opencv-python |
1 | img = cv2.imread('singlemushroom.jpg', cv2.IMREAD_UNCHANGED) |
cv.imread的枚举参数
1 | result = cv2.resize(src, (200,100)) |
1 | #矩阵运算 |
1 | #通道顺序与R-G-B顺序相反 |
1 | #读取图片 |
自适应阈值 adapativeThreshold
1 | adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C[, dst]) |
1 | thresh1 = cv2.adaptiveThreshold(gray_image, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2) |
设想3*3矩阵 对中心位置像素值做卷积运算 其作用即一种平滑滤波
1 | #高斯滤波 |
1 | #腐蚀 |
开为先腐蚀再膨胀 闭为先膨胀后腐蚀
顶帽:原图像 - 开运算结果 顶帽得到的是开运算中断开的细节
底帽:原图像 - 闭运算结果 底帽得到的是闭运算中补上的细节
1 |
|
一种边缘提取方法
1 | contours,_=cv2.findContours(img,RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) |
cv2.drawContours()
1 | cv2.drawContours(image, contours, contourIdx, color, thickness=None, lineType=None, hierarchy=None, maxLevel=None, offset=None) |
1 | x, y, w, h = cv2.boundingRect(contour) |
1 | mu = cv2.moments(contour, False) |
或者叫蒙版 将感兴趣区域(Region of Interest, ROI)提取出来 掩盖其他区域.
1 | h, w = gaussianBlur.shape |
反映像素的值在图像中的分布,值范围分段,进行统计,值可以是亮度,或任意色彩通道的分量
直方图可以作为阈值分割的参数选择依据
1 | import cv2 |
PIL( Python Imaging Library)是 Python 的第三方图像处理库,由于其功能丰富,API 简洁易用,因此深受好评。由于 PIL 库更新缓慢,目前仅支持 Python 2.7 版本,这明显无法满足 Python3 版本的使用需求。于是一群 Python 社区的志愿者(主要贡献者:Alex Clark 和 Contributors)在 PIL 库的基础上开发了一个支持 Python3 版本的图像处理库,它就是 Pillow。
Pillow 提供了丰富的图像处理功能,可概括为两个方面:
图像归档
图像处理
图像归档,包括创建缩略图、生成预览图像、图像批量处理等;而图像处理,则包括调整图像大小、裁剪图像、像素点处理、添加滤镜、图像颜色处理等。
Pillow 库可以配合 Python GUI(图形用户界面)工具 Tkinter 一起使用。
输入棋盘图片路径 棋盘角点数(横纵方格数减1)1
2
3
4
5
6
7
8def calibratePos(imgPath, w, h):
chessboardImg = cv2.imread(imgPath)
chessboardImg = cv2.cvtColor(chessboardImg, cv2.COLOR_BGR2GRAY)
ret, chessboard = cv2.findChessboardCorners(chessboardImg, (w, h), None)
cv2.drawChessboardCorners(chessboardImg, (w, h), chessboard, ret)
# cv2.imwrite('calibrated_image.jpg', chessboardImg)
cv2.imshow('calibrated image', chessboardImg)
cv2.waitKey(0)
vue的状态管理库
其核心是管理一个store(仓库) store保存变量的状态 组件引用store 查询状态
以commit mutation的方式更新状态
状态驱动视图 UI=f(state)
state
使用单一状态树,即app只用一个对象管理所有变量
vuex通过vue插件系统将store注入到每一个组件,子组件通过this.$store.count访问
1 | comouted: mapState(['count']) |
字符串为state的变量名
1 | store.commit('increment', payload) |
1 | const store = createStore({ |
mutation的处理方法不能是异步的 对于异步的状态是无法追踪的
与mutation类似
分发action1
store.dispatch('increment')
action通常传递一个mutation 其处理方法可以是异步的1
2
3
4
5
6
7actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
store/index.js1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41// 数据
const state = {
count,
user:{name:''}
}
const getters = {
getUser(state){
return state.user
}
}
// 修改数据
const mutations = {
updateUser(state, user){
state.user = user
}
}
// 异步操作
const actions = {
delayUpdate(store, user){
setTimeout(()=>{
store.commit('updateUser', user)
})
}
}
// 分装
const modules = {
moduleA:{
state,
mutations,
actions
},
moduleB:{
state,
mutations,
actions
}
}
子组件中 调用mutation用commit方法 调用action用dispatch方法1
2
3this.$store.commit('updateUser', user)
this.$store.dispatch('delayUpdate', user)
TypeError: Cannot read properties of undefined (reading ‘commit’)
vuex@4 不兼容vue2 改为vuex@3
定义store时 Vue.use(Vuex)
vtk Widget是官方提供的常用小工具,如LineWidget、AngleWidget、PaitWidget等
vtk Widget架构遵循MVC,分为三个组件
下图示意调用widget的不同组件的通信关系
Widget 工厂用于组装Widget及其state、representations
构建vtkWidgetState
调用getWidgetForView 工厂new一个widget对象 将state赋给该对象 创建并设置representations即SetRepresentation
开发者应继承vtkAbstractWidgetFactory开发widget
VTK的交互器样式(vtkInteractorStyle)通常只是控制相机以及提供一些简单的键盘和鼠标事件的交互技术。交互器样式在渲染场景中并没有一种表达形式,也就是说,在交互时我们看不见交互器样式到底是什么样子的,用户在使用这些交互器样式时,必须事先知道哪些键盘和鼠标事件是控制哪些操作的。FromCSDN: Widgets简介
vtkWidget同为vtkInteractorObserver子类,监听并响应交互器事件,又添加可视化的representation
Developing Widgets
vtkWidgetManager 是管理widgets创建、抑制(suppression)及聚焦(focus)的对象,每个render中唯一1
2
3
4
5
6
7
8widget = vtkWidget.newInstance()
handle = widgetManager.addWidget(widget, viewType)
widgetManager.setRenderer(renderer)
widgetManager.grabFocus(widget)
widgetManager.enablePicking()
widgetManager.removeWidget(widget)
widget.delete()
focus至多一个widget 激活并使能其响应事件(其实behaviour也可以在unfocus的状态下响应)
使用widget的newInstance方法创建widget对象 这时widget state被创建 用于在不同组件间同步状态 比如工具栏和canvas的相互交互
viewType用于指示widget manager该使用的representation。
创建子状态1
2
3
4
5
6
7
8
9
10
11
12vtkStateBuilder
.createBuilder()
.addStateFromMixin({
labels: ['{LABEL0}'],
mixins: ['origin', 'color', 'scale1', 'visible'],
name: '{NAME}',
initialValues: {
scale1: 0.1,
origin: [1, 2, 3],
visible: false,
}
})
调用widgetManager.getRepresentationsForViewType(viewType)返回含representation的集合 参数viewType是addWidget时指定的参数
返回各项 {builder, labels} 前者是Representation类 后者是representation对象要用到的子状态1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38switch (viewType) {
case ViewTypes.DEFAULT:
case ViewTypes.GEOMETRY:
case ViewTypes.SLICE:
return [
{
builder: vtkCircleContextRepresentation,
labels: ['handle', 'trail'],
},
{
builder: vtkPolyLineRepresentation,
labels: ['trail'],
},
];
case ViewTypes.VOLUME:
return [
{
builder: vtkSphereHandleRepresentation,
labels: ['handles'],
initialValues: {
scaleInPixels: true,
},
},
{
builder: vtkSphereHandleRepresentation,
labels: ['moveHandle'],
initialValues: {
scaleInPixels: true,
},
},
{
builder: vtkSVGCircleHandleRepresentation,
labels: ['handles', 'moveHandle'],
}
];
default:
return [{ builder: vtkSphereHandleRepresentation, labels: ['handle'] }];
}
Representation托管自身actors和mappers, actor在Representation创建时创建,推入model.actors中进而渲染
Representation应继承vtkHandleRepresentation 或 vtkContextRepresentation
Widget behavior
widgetManager.addWidget返回的handle就是widget behavior对象 它控制这widget的行为:接收并响应鼠标、键盘事件 见其定义的方法形如 PublicAPI.handle{XXX}(callData)
widget behavior也可以访问renderer rendererWindow 和 interactor
1 | // main.js |
h 是 createElement 的别名
$mount(‘#app’) 表示将该组件挂在到id=”app”的dom上,这个#app在public/index.html上
类似于React的 Context Provider, 避免父组件向孙子后代组件传参时逐层引用,而以依赖注入的形式暴露给其他组件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25const app = Vue.createApp({})
app.component('todo-list', {
data() {
return {
todos: ['Feed a cat', 'Buy tickets']
}
},
provide: {
user: 'John Doe'
},
template: `
<div>
{{ todos.length }}
<!-- 模板的其余部分 -->
</div>
`
})
app.component('todo-list-statistics', {
inject: ['user'],
created() {
console.log(`Injected property: ${this.user}` // > 注入的 property: John Doe
}
})
注意 provide 组件实例property,需要用返回函数的形式1
2
3
4
5provide() {
return {
todoLength: this.todos.length
}
}
1 | @startuml |
指的是生命周期的选项 类似React class组件中‘选择’恰当的生命周期钩子嵌入业务逻辑。 created, mounted 等生命周期钩子,直接对应 componentDidMount, componentDidUpdate 等
Composition API则React function组件中的hooks
ref() 和 reactive() 对应 useState。
computed() 对应 useMemo。
onMounted -> useEffect(…, [])
onUpdated -> useEffect(…, […]) (不指定依赖)
onUnmounted -> useEffect(() => { return () => { … } }, [])
defineProperty
defineConponent
vue cli 项目配置包含项目基本配置和对webpack的封装
基本配置:
1 | module.exports = defineConfig({ |
创建新解决方案 使用webapi模板 new -> Project -> ASP.NET Core Web API template
命令行工具参考Microsoft Doc: dotnet new1
2
3
4
5
6mkdir MyServices
cd MyServices
dotnet new sln
mkdir src
dotnet new webapi -lang C# -o src/MyServices.API
dotnet sln add src/MyServices.API/MyServices.API.csproj
添加class library项目1
2
3
4dotnet new classlib -lang C# -o src/MyServices.EntityManagers
dotnet new classlib -lang C# -o src/MyServices.Models
dotnet sln add src/MyServices.EntityManagers/MyServices.EntityManagers.csproj
dotnet sln add src/MyServices.Models/MyServices.Models.csproj
创建详细目录,如下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19─src
├───MyServices.API
│ ├───Controllers
│ ├───Infrastructure
│ │ ├───ActionFilters
│ │ ├───DbContext
│ │ ├───Helpers
│ │ ├───OData
│ │ └───Security
│ └───Properties
├───MyServices.EntityManagers
│ ├───interfaces
│ ├───Managers
│ └───Utilities
└───MyServices.Models
├───Contexts
├───DTO
├───Enums
└───Migrations
安装依赖
API layer
Microsoft DOc: Swagger/OpenAPI Swagger 项目已于 2015 年捐赠给 OpenAPI 计划,自此它被称为 OpenAPI。 是一个与语言无关的规范,用于描述 REST API。 它使计算机和用户无需直接访问源代码即可了解 REST API 的功能。
visual stuadio创建ASP.NET Core Web API项目模板,包含Enable OpenAPI support选项 即swagger页面
初始startup.cs的配置是1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebApplication1", Version = "v1" });
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "WebApplication1 v1"));
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
配置加载名为swagger.json的接口描述,记作WebApplication1 v1的接口文档
swagger.json不会自动生成 从controller获取接口文档由下述swagger的实现包完成
启用xml注释
右键API项目 -> Properties -> Build -> Output
Output path 为空 即项目根目录
勾选XML documentation file 并命名
1
2
3
4
5
6
7
8
9
10
11services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebApplication1", Version = "v1" });
c.DocInclusionPredicate((name, api) => api.HttpMethod != null);
// Locate the XML file being generated by ASP.NET
var xmlFiles = Directory.GetFiles(AppContext.BaseDirectory, "WebApplication1.API.xml", SearchOption.TopDirectoryOnly);
// Tell Swagger to use those XML comments.
xmlFiles.ToList().ForEach(xmlFile => c.IncludeXmlComments(xmlFile));
});
详情待续 参考Microsoft Doc:Swashbuckle入门