阅读视图

发现新文章,点击刷新页面。
☑️ ☆

opencv-python图像处理常用方法整理(一)

封面图片由PexelsPixabay上发布,本人添加了OpenCV的Logo融合而成

在完成毕业设计后我再也没有碰过图像处理相关的东西,进入公司后也是作为前端开发学习和工作(于是乎才有了Vue的学习笔记),但是当我再开始做图像处理相关的事情时,我发现自己全然忘却了怎么调用函数,于是乎决定整理一份。

本人习惯import cv2 as cv,所以下面的调用均以cv为准,格式中dst为目标图像的mat对象,src为原始图像的mat对象(就是用imread读进去的那个)

获取图片的尺寸

print一下src.shape,可以发现结果为(高度,宽度,通道数),那么获取图片的高度和宽度可以使用以下语句实现:src_height, src_width = src.shape[0:2]

图像缩放函数resize()

常用函数格式:

dst = cv.resize(src, dsize)

其中dsize为类似于(int(source_width / 2), int(source_height / 2))形式的元组,代表了图片的放缩倍率,例子中是保持长宽比放缩了一半,参数可调。

图像颜色转换函数cvtColor()

常用函数格式:

dst = cv.cvtColor(src, colorCode)

其中colorCode部分在库中有对应代码,例如cv.COLOR_RGB2GRAY这样子的。

该函数常用于灰度化等。

图像降噪常用方法高斯模糊GaussianBlur()

常用函数格式:

dst = cv.GaussianBlur(src, ksize, sigmaX)

ksize为卷积核的大小,只能为正奇数元组,例如(3, 3)、(5, 5)这样子,通俗来讲就是邻域大小,对某个像素点周围多少像素在内的区域做处理,ksize越大得到的越模糊。

sigmaX实际上是σX,指的是在图像X向的标准差,为必要参数,如果未指定σY,则根据X的来设定

图像二值化函数threshold()

常用函数格式:

dst = cv.threshold(src, thresh, maxval, type)

其中thresh为阈值,maxval为被设置的最大值,仅当type为cv.THRESH_BINARYcv.THRESH_BINARY_INV时生效

type为二值化方法,在库中有对应值,如下所示:

cv.THRESH_BINARY:当前点大于thresh时设置为maxval的值,否则为0。

cv.THRESH_BINARY_INV:当前点值大于阈值时,设置为0,否则设置为Maxval

THRESH_TRUNC:当前点值大于阈值时,设置为阈值,否则不改变

THRESH_TOZERO:当前点值大于阈值时,不改变,否则设置为0

THRESH_TOZERO_INV:当前点值大于阈值时,设置为0,否则不改变

Canny算子边缘检测Canny()

常用函数格式:

dst = cv.Canny(src, thresh1, thresh2)

其中,像素值低于thresh1的会认为不是边缘,像素值高于thresh2的会认为是边缘,像素值为两个阈值之间的像素点若与被认为是边缘的像素点相邻,则也被认为是边缘。

轮廓检测findContours()与轮廓绘制drawContours()

常用函数格式:

contours, hierarchy = cv.findContours(src, mode, method)
dst = cv.drawContours(src, contours, contoursIdx, color, thickness)

在findContours中,mode为轮廓检索方式,例如cv.RETR_TREE可以完全建立轮廓层级关系,method为轮廓表示方式,例如cv.CHAIN_APPROX_SIMPLE就是以尽量少的像素点表示轮廓。

在drawContours中,contours就是上一步检测出的轮廓,contoursIdx指绘制的轮廓编号,若为-1则是绘制所有的轮廓,color指颜色,可以用类似(255, 0, 0)这样的格式表示一个RGB颜色,thickness指轮廓线的宽度,是一个非必须参数。

通常来讲对边缘检测之后的图进行轮廓检测,得到的轮廓效果一般很好。

霍夫变换HoughLines()

常用函数格式:

lines = cv.HoughLines(src, rho, theta,thresh)

输出为一组检测到的直线,rho为以像素为单位的距离精度,theta为以弧度为单位的角度精度,这里使用了极坐标来表示直线,thresh为阈值。在实际使用时若无特殊情况则一般为1, np.pi / 180, 0

该函数用于直线检测,应用场景为求角度进行旋正,例如以下代码用于旋转直线斜率对应的角度:

lines = cv.HoughLines(img_canny, 1, np.pi / 180, 0)
for rho, theta in lines[0]:
    a = np.cos(theta)
    b = np.sin(theta)
    x0 = a * rho
    y0 = b * rho
    x1 = int(x0 + 1000 * (-b))
    y1 = int(y0 + 1000 * a)
    x2 = int(x0 - 1000 * (-b))
    y2 = int(y0 - 1000 * a)
    if x1 == x2 or y1 == y2:
        continue
    t = float(y2 - y1) / (x2 - x1)
    rotate_angle = math.degrees(math.atan(t))
    img_result = ndimage.rotate(img_resize, rotate_angle)

最后打个小广告:

我的博客即将同步在腾讯云+社区发布,邀请大家一同入驻:
https://cloud.tencent.com/developer/support-plan?invite_code=3hk8c1hl0bqcw

除另有声明外,本博客文章均采用 知识共享(Creative Commons) 署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可,转载请注明文章出处。

☑️ ☆

月落星河的2021小回想

2021年年末时,博客刚刚经历了一个比较大的迁移。由于CODING本身的静态页面托管服务即将下线,我必须把整个博客迁到腾讯云的Webify上,初始化费了一番功夫,而后转移CDN也耗费了很长的时间,加上后来的更新SSL证书,前前后后也是费了一番心神。

想来自己开始写博客已经三年有余了,从刚开始租服务器自己搭WordPress,再到租虚拟主机建站,再到迁回国内使用静态页和图床,最后一步步到目前这种将博客备案并托管在腾讯云,图片与部分静态资源存储在DogeCloud中的方案,从全部自运营到如今专注内容无需纠结于运维中,可以说是各有优劣,而我个人还是更偏向这种本地编辑,上传云端持续部署的方式。

言归正传,这篇文章,我想回过头来,看看走过的2021年。

上一次的回头看还是在前年,所以对于“2020年自己是如何度过的”这样的问题,我的脑海中只留下了从两个人在一起开始发生的事和再往前的一些零星碎片。某种意义上既是不幸也是万幸,我还留下了许许多多的碎碎念(我在自己心情极度压抑的时候或是特别有感触的时候会写一些东西),不得不感慨我面对的问题和自己的脆弱不堪大概都是相似的。自己好像处在一种迷路或者迷失自我的状态,想去做些能拿得出“成绩”的事情来“证明”自己,或是让自己可以“充实”起来以暂时忘记许多胡思乱想中的恐惧。

不过一年过去,写作依旧可以让我冷静,可以暂时缓解目前的焦虑,放下其他的事情得以休整。写的过程也是思绪的整理与“打包”,似乎只要写出来了之后,许多停留在脑海中的漂浮思绪就会消失,不会来侵扰我让我深陷在意识的自我较劲中让时光蹉跎。

今年是对我来说“大变动”的一年,自己从象牙塔中走出,正式进入社会成为一名员工。前半年为毕业不断脑力劳动,后半年在工作之余也为着自己的提高努力着。在新环境中摸爬滚打,逐渐摸清了许多事情,和我的其他很多朋友相比我是幸运的,工作压力不算大而且有自我提升的时间,然而虽然工作很稳定,但如果自己不向前走的话总是会有一种被淘汰的恐惧,唯有自强才能更多地实现自我价值。

在做完毕设之后短暂地放下了Python,开始学习Vue准备承担部门的前端开发工作。小组内有过几次培训,同时自己不想放下serverless这个自己感兴趣的方向,也报名参与了一些技术沙龙形式的活动,虽然因为自己的原因错过了好几次,嘛,希望从今往后不要这样子了。

第二是自己接触了许多新事物,开始进行一些让自己变得更好的尝试,还在一周前接受了一位新的家庭成员:小海参,它现在在家里过得非常舒坦。在后半年靠着工资开始自己养活自己,想攒下为自己提高改变现状让生活变得更好所需要的钱,同时开始对自己做一些改变,希望自己不论是外在还是内在都充实起来。随着时间的推移我愈发相信自己的内在会逐渐影响外在,至少自己的怠惰开始让自己的形象变得不那么有活力,完全不像一个向上的人,我更希望可以通过更加积极健康的生活方式与运动再次唤醒自己。

我常常在自己失意的时候会想到这一年过得乱七八糟的,和之前相比,这一年自己实质上是有一些退步的,我浪费了太多的时间在胡思乱想上,而行动不足更加打破不了现有的焦虑。其实现有的工作并不算是我的压力源,但是自己如果没办法权衡与调节许多方面的事情,到头来浪费了时间还是会一团糟。

如果许久不做总结,每一天的流逝似乎就变得没有了意义,就像如果我不去看以前的日记,我似乎想不到当时的我是怎么想的,即使我现在逐渐“懒于表达”,也是不能让自己糊里糊涂的。即使有越来越少的时间属于自己,每天认真想想自己有没有进步,总比陷在游戏里要好很多。

对于2022年的自己,一个词可以用来概括我最大的期望:“动态清零”。动态清零每天还没做的事情,动态清零每天物品的摆放位置,动态清零每一个小目标,动态清零每一个物品的归宿……以此最大程度拒绝拖延,也是一个不错的选择。

年初时我感叹今年或许会是非常充实的一年,可是一个月快过去了,计划似乎又落了空,本想每天都坚持下来的事情,因为天气寒冷加上自己没有韧劲,还是被搁置了。其实我有做不完的事,旅游、做恋爱日记、做视频、录歌、摄影、写自己的项目、画画……不过为了目标必须有所取舍,即便是想多元发展自己,也要先能解决基本的生存问题。一方面要为能够胜任现在的工作进行积累,一方面要有所提高让自己能够过得更好,我依旧是相信生活会有光亮的。

其实现在并没有那么糟,目标很小,但难在坚持,完成一个个小目标,量变会引起质变。

除另有声明外,本博客文章均采用 知识共享(Creative Commons) 署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可,转载请注明文章出处。

☑️ ☆

Vue开发学习笔记(一)

该封面图片中,背景由Sara TordaPixabay上发布,本人结合Vue图标制作成了封面。
本文为本人结合 Vue3教程 | 菜鸟教程 相应内容学习时的一些想法和感触,文中的实例代码均来源于菜鸟教程。

Vue与“view”的读音接近,它的语法与Flask中的Jinja2模板语言非常相近。Vue通过元素的id来确定位置,通过createApp函数来创建应用,使用mount函数将应用挂载在对应id的元素中。

Hello,Vue!!

例如以下代码,便是使用mount(#app)HelloVueApp挂载到id为app的div元素中,最终的运行结果为在页面上显示“Hello Vue!!”

<div id="app" class="demo">
  {{ message }}
</div>
​
<script>
const HelloVueApp = {
  data() {
    return {
      message: 'Hello Vue!!'
    }
  }
}
​
Vue.createApp(HelloVueApp).mount('#app')
</script>

其中{{ }}用于输出对象属性和函数返回值,{{ message }}就是返回应用中的message值。

Hello

Hello Vue

Vue的数据组织与方法

data选项

data本身是一个函数,是createApp函数中不可或缺的部分,其返回值为一个对象。该对象被Vue通过响应式系统进行包装,以$data的形式存储与实例中,其中的数据可以通过类似于document.write的方法进行编辑,如下:

const app = Vue.createApp({
  data() {
    return { count: 4 }
  }
})

const vm = app.mount('#app')

document.write(vm.$data.count) // => 4
document.write("<br>")
document.write(vm.count)       // => 4
document.write("<br>")
// 修改 vm.count 的值也会更新 $data.count
vm.count = 5
document.write(vm.$data.count) // => 5
document.write("<br>")
// 反之亦然
vm.$data.count = 6
document.write(vm.count) // => 6

结果如下图所示:

data

data数据可改变

方法

Vue中通过methods选项添加方法,如下所示:

const app = Vue.createApp({
  data() {
    return { count: 4 }
  },
  methods: {
    increment() {
      // `this` 指向该组件实例
      this.count++
    }
  }
})

const vm = app.mount('#app')

document.write(vm.count) // => 4
document.write("<br>")
vm.increment()

document.write(vm.count) // => 5

与Java等面向对象语言感觉非常接近,也很容易上手,结果如下:

methods

使用methods

模板

文本插值

前面提到Vue和Jinja2非常像,这一点在模板这里体现得淋漓尽致,例如以下的代码中:

<div id="app">
  <p>{{ message }}</p>
</div>

{{ message }}里,message部分会被转换成具体的这个变量的值,当message这个变量的值发生实时变化,前端界面的信息也会发生实时变化,例如一个在10s后变量值变化并在前端发生变化的script代码如下:

<script>

const HelloVueApp = Vue.createApp({
  data() {
    return {
      message: 'Hello Vue!!'
    }
  }
})
const vm = HelloVueApp.mount('#hello-vue')

setTimeout("vm.message='lalala~'",10000)

</script>

原本页面显示为Hello Vue!!,经过10s后随着message的值的变化,显示内容变成了lalala~,就像这张gif图一样:

change

变量值改变,前端显示改变

这是数据与界面的一种单向绑定,界面元素的显示会随着变量值的变化而变化。这里还有一个需要注意的点,如果不想前端元素的内容发生改变,需要使用v-once标签,例如<span v-once>{{ message }}</span>这样的形式。

表达式

{{ }}中,JavaScript的表达式也是可以被解析的,例如下面的代码:

<div id="app">
    {{5+5}}<br>
    {{ ok ? 'YES' : 'NO' }}<br>
    {{ message.split('').reverse().join('') }}
    <div v-bind:id="'list-' + id">菜鸟教程</div>
</div>
    
<script>
const app = {
  data() {
    return {
      ok: true,
      message: 'RUNOOB!!',
      id: 1
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>

显示结果为如下所示:

expression

表达式的处理

指令

指令通常带有v-前缀,上文中的v-once就是一种指令。它们常常用于当变量或者表达式的值发生变化时,将某些行为反映在前端界面上,指令有很多种,每一种可以实现不同的功能,例如下面的这几种:

  • 显示文本(v-text)

在类似于<p>中使用v-text标签可以显示文本,对应着data中相应变量的变量值,例如以下例子:

<div id='app'>
<p v-text="count"></p>
</div>

<script>
const app = Vue.createApp({
  data() {
    return { count: 4 }
  },
})

const vm = app.mount('#app')
</script>

显示结果为:

v-text

v-text指令

  • html插值(v-html)

利用v-html标签还可以实现将html代码进行解释和表现,下面的例子分别显示了对于同样一行代码使用文本插值与使用html输出,显示结果出现的不同:

<div id="example1" class="demo">
    <p>使用双大括号的文本插值: {{ rawHtml }}</p>
    <p>使用 v-html 指令: <span v-html="rawHtml"></span></p>
</div>
 
<script>
const RenderHtmlApp = {
  data() {
    return {
      rawHtml: '<span style="color: red">这里会显示红色!</span>'
    }
  }
}
 
Vue.createApp(RenderHtmlApp).mount('#example1')
</script>

显示结果为:

v-html

v-html指令

  • 改变html元素内部的属性值(v-bind)

而对于html元素内的属性的值,Vue则提供了v-bind标签用于处理属性值的变化,例如以下的代码是对class或对id的指定,use为true则使用class1,否则不使用。为了不牵扯其他的指令,我依旧使用延时完成。

<style>
.class1{
  background: #66CCFF;
}
</style>
</head>
<body>
<div id="app">
  <div v-bind:class="{'class1': use}">
    v-bind:class 指令
  </div>
</div>

<script>
const app = {
  data() {
    return {
      use: false
    }
  }
}
const vm = Vue.createApp(app).mount('#app')

setTimeout("vm.use=true",10000)

</script>
</body>
</html>

实际效果:

v-bind

v-bind指令

可以看出来10秒后div的背景颜色发生了改变,这是因为use的值变为true,使得class的值变为class1,从而使用了class1对应的样式。

  • 判断是否插入元素(v-if)

例如下面的例子:

<div id="app">
    <p v-if="seen">现在你看到我了</p>
</div>
    
<script>
const app = {
  data() {
    return {
      seen: true  /* 改为false,信息就无法显示 */
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>

例子中v-if指令会根据seen的值来决定该元素是否会被显示。

  • 监听DOM事件(v-on)

与methods搭配使用,处理前端的事件,例如以下代码:

<div id='app'>    
    <button v-on:click="welcome">欢迎</button>
</div>
<script>
const app = Vue.createApp({
  data() {
    return { count: 4 }
  },
  methods: {
    welcome() {
        alert("Hello")
    }
  }
})
const vm = app.mount('#app')
</script>

实际效果:

v-on

v-on指令

不传参数的话可以省略括号。

  • 双向数据绑定(v-model)

这条指令用于收集用户输入或用户选择,用于表单控件(input,textarea,select等),例如如下代码:

<div id="app">
    <p>{{ message }}</p>
    <input v-model="message">
</div>

<script>
const app = {
  data() {
    return {
      message: 'Runoob!'
    }
  }
}

Vue.createApp(app).mount('#app')
</script>

双向绑定的意义在于,用户在页面上的输入会实时在data中修改,且data中的数据也会实时显示在用户界面上。

实际效果:

v-model

表达式的处理

  • 两条指令的缩写

v-bind:

<!-- 完整语法 -->
<a v-bind:href="url"></a>
<!-- 缩写 -->
<a :href="url"></a>

v-on:

<!-- 完整语法 -->
<a v-on:click="doSomething"></a>
<!-- 缩写 -->
<a @click="doSomething"></a>

除另有声明外,本博客文章均采用 知识共享(Creative Commons) 署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可,转载请注明文章出处。

☑️ ☆

技术向文章导航

该图片由Joshua WoronieckiPixabay上发布

该页面是本站到目前为止的一些技术向文章整理,随着文章发布会实时更新。毕竟Gridea还没有智能到引入“分类目录”类似的选项,虽说可以通过标签筛选,但是自己分类整理或许会更直观一些o( ̄▽ ̄)ブ

目录


建站相关


Android开发


Python开发


前端开发


数据库学习


软件使用


瞎折腾


本页面持续更新中ヾ(≧▽≦*)o

除另有声明外,本博客文章均采用 知识共享(Creative Commons) 署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可,转载请注明文章出处。

☑️ ☆

从零开始的BD刻盘过程

封面图片由Tibor Janosi MozesPixabay上发布

突然想把一些游戏和动画刻录成光碟,以及之后或许可以做一些电子相册和纪念视频,将它们刻盘送给朋友,感觉很有纪念意义。于是兴致冲冲买回来了蓝光刻录机和蓝光光碟,开始尝试刻录。这其中踩了许多坑,浪费了总共有12张25G的盘(枯了真的好浪费),目前刻录成了两个游戏,决定记录下来整个的过程。

光驱和光盘

光驱买了外接的惠普蓝光刻录光驱TS-TB23L,算是比较便宜的一款,质量也很好能保证稳定的4X速度刻录。

光盘买了铼德的50个装25G的BD-R蓝光光碟,用来刻录完整的一季动画,或者几部电影应该是够的,但是还是应该多买几种不同容量的空白盘,应对不同的文件大小。以及因为我买的是一次写的光盘,如果刻录失败了一张盘相当于废掉了,所以如果初次尝试刻录,建议买RW类型的光盘,也就是可擦写光盘。虽然价格贵了些(同样价格同样数量的CD-RW光盘单片容量才700M),但即使刻录失败也能擦除后重新刻录,避免浪费。

刻录软件

刻录软件一开始选择了自己之前购买的UltraISO,刻录约战的时候一切顺利,成功封盘和校验,但是刻录另一款游戏的时候,虽然最后封盘成功了,但是结束后尝试读盘还是读不出来。

于是查了查发现了开源刻录软件ImgBurn,经尝试,它会在刻录过程中写数据失败时进行重试,单次连续二十次写入失败(就是写入一组数据,如果这组数据连续二十次尝试写入都没有写进去)才会断开,大大增加了刻录的成功率,但是后来刻录了许多次也遇上了刻录失败的情况。

我一度以为是硬件故障,但是既然之前能刻录成功,就不能这么早草草下了定论,因而决定做一组实验,探究刻录失败的原因。

刻录失败的原因探究

首先,将镜像缓存区设置到E盘,同时保证镜像所占磁盘空间小于C盘的空间,避免因为缓存区空间不足导致刻录失败的客观原因。经过思考,我可以对比不同的刻录软件、不同的外部工作环境,遵循对照原则与单一变量原则来设计实验。

更换镜像存放磁盘

光驱和其他使用USB的外设一同工作时

使用UltraISO进行刻录

刚刚开始刻录,就弹出了刻录失败的字样,报“NO ADDITIONAL SENSE INFORMATION”的错误信息,想接着往里写但是写不进去,盘面上留下了一小圈被烧录过的痕迹,但是再也没办法往里写数据了,浪费了一张盘。

使用ImgBurn进行刻录

超时重写

比UltraISO情况好一些,但是还是会疯狂重新写入,镜像写不完整,刻录的痕迹明显多了很多,但最终还是张废盘。

在只保留光驱的情况下

使用UltraISO进行刻录

校验

一次刻录成功+校验通过,弹出后再读盘也可以正常读写。

使用ImgBurn进行刻录

0次重新写入!整个过程非常顺利,刻录的光盘也可以正常读取。

最终,综合实验结果,在总共浪费了12张25G蓝光光盘后,经过分析我终于总结出来了这些光盘刻录失败的原因:

谁能想到,竟然是供电不足!

没错,虽然光驱在读盘时可以和鼠标一同使用,但是在刻录时,因为光驱和鼠标共同占满了我笔记本电脑的三个USB接口,所以在使用鼠标时光驱就会供电不足,进而就会出现UltraISO的刻录失败和ImgBurn的重写20+次这样的情况。在拔掉鼠标进行刻录后,UltraISO可以正常执行,ImgBurn的重写次数也一直稳定在0次……(😭😭😭呜呜呜我的十二张盘)

刻录成果

刻录的约战(没有设置autorun.inf所以打开时可以看到里面所有的文件):

光盘内容

刻录失败的光盘们:

刻录失败的光盘们

(左侧是用UltraISO刚刚开始刻录就失败的盘,右侧是所有浪费掉的盘)

后续

可以在光盘里设置autorun.inf文件,光驱在读盘的时候就可以显示相应的图标,在用户双击时按照配置直接打开exe文件,具体设置如下:

[AutoRun]
open=exe文件名
icon=图标文件名

因为自己买了盘面空白的光盘,所以后续还打算弄一台盘面彩印机,打印上光盘封面,放在自己的光盘盒里存好。

后面计划刻录一些剧场版和整季的动画,以及一些电影之类的,解放电脑硬盘容量。

参考文章:

windows10刻录exe执行文件到光盘中,插入光盘直接运行exe

除另有声明外,本博客文章均采用 知识共享(Creative Commons) 署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可,转载请注明文章出处。

❌