Jiahong 的个人博客

凡事预则立,不预则废


  • Home

  • Tags

  • Archives

  • Navigation

  • Search

Python——修改图片背景颜色

利用Python的skimage包实现修改图片背景色等


修改签名背景,按照灰度图像处理

代码

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
from skimage import io, color
import matplotlib.pyplot as plt


def change_grey_to_white(origin, new):
"""
Change background color for image, such as signature
:param origin: the path of original image
:param new: the path of new image
:return: None
"""
# img = io.imread('./origin.jpeg')
img = io.imread(origin)
io.imshow(img)
plt.show()
img_gray = color.rgb2gray(img)
rows, cols = img_gray.shape
for i in range(rows):
for j in range(cols):
if img_gray[i, j] <= 0.5:
img_gray[i, j] = 0
else:
img_gray[i, j] = 1
io.imshow(img_gray)
plt.show()
io.imsave(new, img_gray)

# example
change_grey_to_white('./origin.jpeg', 'new.jpeg')

说明

  • 这里可接受彩色图片,比如拍照片得到的原始图片
  • 函数会将其转换为灰度图片然后处理,最终输出也是灰度图片
  • 如果需要处理指定灰度,可通过修改判断语句中0.5这个值从而实现(比如修改为区间等)

修改证件照背景,按彩色图像处理

代码

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
38
39
40
41
42
43
44
45
46
47
48
49
50
from skimage import io
import numpy as np


def change_pixel_color(pixel, old_pixel, new_pixel=None, error=60):
"""
change color for pixel if pixel in range [old_pixel-error, old_pixel+error]
:param pixel:
:param old_pixel:
:param new_pixel:
:param error:
:return: the new pixel
"""
if new_pixel is None:
new_pixel = [255, 255, 255]
similar = True
for i in abs(pixel - old_pixel):
if i > error:
similar = False
break
if similar:
return new_pixel
else:
return pixel


def change_background(origin_path, new_path, new_color, error=60):
"""
change background, auto detect background
:param origin_path:
:param new_path:
:param new_color: target background color
:param error: the error for change color
:return: None
"""
img = io.imread(origin_path)
bg = img[10:30, 10:30, :]
bg_pixel = [1.0 * np.sum(bg[:, :, channel])/bg[:, :, channel].size for channel in range(3)]
print("background color: %s" % bg_pixel)
new_img = np.array([[change_pixel_color(pixel, [0, 160, 234], new_color, error) for pixel in row] for row in img])
io.imshow(new_img)
# import matplotlib.pyplot as plt
# plt.show()
io.imsave(new_path, new_img)


# example
change_background(origin_path="origin.jpeg",
new_path='new.jpeg',
new_color=[0, 255, 255])

说明

  • 自动读取图片的左上角部分像素的平均值作为背景颜色
  • 允许差范围在合适的范围内,可通过error参数调节,该参数不宜过大也不宜过小,可测试多次选择比较合适的

ML——缺失值处理

missing值处理


对于数值类型的特征

中位数填充

  • 用当前特征所有未缺失的值的中位数(median)填充当前特征的缺失值

均值填充

  • 用当前特征所有未缺失的值的均值填充当前特征的缺失值

加权填充

  • 引入相似性矩阵,评估缺失样本与其他未缺失样本的相似性,按照相似性分配权重
  • 效果更好,但是需要更多时间

对于Category类型

特殊字符填充

  • 使用某种未出现过的特殊字符填充
  • 等价于将缺失值看成是个特殊类别

填充众数

  • 寻找所有未缺失数据中样本最多的类别,然后将缺失值填充为该众数类别

什么情况下不用填充

树模型

普通树模型
  • ID3不支持缺失值处理(也可能可以,但是我没看到具体介绍)
  • C4.5和CART都使用下面的方法进行缺失值处理
  • 结点分裂时:
    • 参考自周志华机器学习书籍中
    • 先按照无缺失的数据正常划分(缺失数据不参与计算)
    • 对于无缺失值的样本,正常分配到对应叶子节点
    • 对于缺失值的样本,每个样本以不同的概率分配到各个叶子节点, 概率值为: 子节点中无缺失样本的数量 / 无缺失样本的总数
  • 问题: 如果是训练时没有缺失,预测时有缺失怎么办?
    • 一种可能的方式是直接放到某个结点中
    • [待更新]
GBDT
  • 如果使用的是树模型(CART)作为基分类器是否可以直接借用树模型对缺失值的处理方法?
  • 如果使用的是逻辑回归模型作为基分类器,需要自己对缺失值进行处理
XGBoost
  • 寻找分裂点时(split point):
    • 不遍历缺失值对应的样本,只使用无缺失的样本确定分裂点(节省开销)
    • 尝试将缺失值分配到左叶子结点或者右叶子结点,分别计算增益(保证完备性)
    • 选择增益大的点即可
  • 如果训练时没有缺失值,预测时有缺失值:
    • 将缺失值自动放到右子树中即可

不同模型对缺失值的敏感度总结

不敏感模型

  • 树模型
  • 神经网络的鲁棒性强, 数据量够的话不敏感?
    • 神经网络的输入必须是没有缺失值的,需要我们使用特征工程的方法填充缺失值
    • 其实我觉得自己需要填充数据的其实都有点敏感吧, 所以神经网络在使用时有时候感觉并不敏感,因为缺失值被我们填充后总能得到不错的效果

敏感模型

  • 距离度量模型: KNN, SVM等
    • 在尽量保证缺失值是随机的前提下使用基于统计分布的填充方法可能降低缺失值造成的负面影响
    • 但是SVM这样的模型对缺失值的抗性非常差,不恰当的非随机缺失值可能导致模型出现意外
  • 线性模型的损失函数往往也涉及到距离的计算?

Chrome——常用插件总结

Chrome是最好用的浏览器没有之一,本文将总结Chrome中功能强大的插件


Markdown Viewer

  • 用于浏览markdown文件内容
  • 自动为markdown文件生成HTML源码
  • 安装后记得设置允许该插件访问文件URL

油猴插件(Tampermonkey)

  • 脚本引擎,用于管理其他脚本
  • 功能强大,里面有形形色色黑科技油猴脚本

Postman

  • 程序员必备,测试API首选
  • 各种http接口(如GET, POST)和数据格式(如表格)

AdBlock

  • 能屏蔽大部分的广告

SwitchyOmega

  • shadowsocks的伴侣
  • 特别是Ubuntu必备

Conference——各种会议知识总结

NIPS和Advances in Neural Information Processing Systems的关系

  • NIPS 会议全称为:Annual Conference on Neural Information Processing Systems,年度神经信息处理系统大会
  • NIPS 收录的会议论文会以“Advances in Neural Information Processing Systems”为名出版成书,一般由 mit 和 Morgan Kaufmann 出版社出版
  • 引用时一般会以“Advances in Neural Information Processing Systems”命名

ICLR不在CCF列表中

  • ICLR(International Conference on Learning Representations) 是近年来的会议,也是很好的会议,甚至可以比拟 NIPS,但是因为国内发的不多,所以没有列到 CCF列表 中
  • ICLR 在清华的列表中是 A 类

SIGKDD vs KDD

  • 这两者指的是同一个会议,一般正式叫做 SIGKDD
  • 会议全称:ACM Knowledge Discovery and Data Mining

AI 顶会 & 顶刊排名

  • AI顶会&顶刊
  • 人工智能方面顶级会议(转)

FS——文件系统总结


FAT32

常用于闪存

优点

  • 通用格式,任何USB都会预装FAT32,任何操作系统平台上都能读写
  • 节约空间

缺点

  • 单个文件的大小限制为4GB
  • 无读写日志,不能记录磁盘上文件的修改记录?

ExFAT

微软自家创建的用来取代FAT32的新型文件格式类型

优点

  • 跨平台
  • 能支持4GB以上的单个文件

缺点

  • 无读写日志,不能记录磁盘上文件的修改记录

NTFS

New Technology File System
微软为硬盘和固态硬盘创建的默认新型文件系统
几乎集成了所有文件系统的优点

优点

  • 日志功能
  • 无文件大小限制
  • 支持文件压缩和长文件名
  • 服务器文件管理权限

缺点

  • MacOS不能写,只能读(严格来说并不能算是NTFS的问题,是苹果自己不适配吧)

MacOS日志式

优点

  • 没啥缺点也是一种优点

缺点


总结

  • 如果要在MacOS上访问使用就用ExFAT,否则一律使用NTFS
  • 对于移动硬盘可以考虑部分分区格式化为ExFAT,部分分区格式化为NTFS

IDEA——一些常见的问题解决方案

本文总结一些IDEA使用过程中遇到的问题和解决方案


模块导入问题

  • 同一目录下Python模块导入有红色波浪线,但是可以运行
    • 解决方案:
      • 将当前文件夹标记成Source Root
      • 如果是使用的from,可使用当前文档标记符来解决问题
        1
        from .test_module import test_function

机器码——源码-补码-反码

参考博客:https://www.cnblogs.com/zhangziqiu/archive/2011/03/30/ComputerCode.html


基础

机器数

  • 一个数在计算机中的二进制表示形式, 叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放符号, 正数为0, 负数为1.
    • 比如,十进制中的数 +3 ,计算机字长为8位,转换成二进制就是00000011。如果是 -3 ,就是 10000011
  • 那么,这里的 00000011 和 10000011 就是机器数

真值

  • 因为第一位是符号位,所以机器数的形式值就不等于真正的数
  • 例如上面的有符号数 10000011,其最高位1代表负,其真正数值是 -3 而不是形式值131(10000011转换成十进制等于131)
  • 所以,为区别起见,将带符号位的机器数对应的真正数值称为机器数的真值
    • 例:0000 0001的真值 = +000 0001 = +1,1000 0001的真值 = –000 0001 = –1

原码, 反码, 补码

  • 计算机要使用一定的编码方式进行存储. 原码, 反码, 补码是机器存储一个具体数字的编码方式.

原码

  • 原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值. 比如如果是8位二进制:

    [+1]原 = 0000 0001
    [-1]原 = 1000 0001

  • 第一位是符号位. 因为第一位是符号位, 所以8位二进制数的取值范围就是:

    [1111 1111 , 0111 1111]
    [-127 , 127]

  • 原码是人脑最容易理解和计算的表示方式

反码

  • 反码的表示方法是:
    • 正数的反码是其本身
    • 负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.

      [+1] = [00000001]原 = [00000001]反
      [-1] = [10000001]原 = [11111110]反

  • 可见如果一个反码表示的是负数, 人脑无法直观的看出来它的数值. 通常要将其转换成原码再计算.

补码

  • 补码的表示方法是:
    • 正数的补码就是其本身
    • 负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)

      [+1] = [00000001]原 = [00000001]反 = [00000001]补
      [-1] = [10000001]原 = [11111110]反 = [11111111]补

  • 对于负数, 补码表示方式也是人脑无法直观看出其数值的. 通常也需要转换成原码在计算其数值.

Python中特殊加法运算负数非负的问题

原始题目

  • 剑指 Offer:不用加减乘除对两个整数做加法
    1
    2
    3
    4
    5
    6
    def Add(num1, num2):
    mask = 0xFFFFFFFF
    max_value = 0x7FFFFFFF
    while num2:
    num1, num2 = (num1^num2)&mask, (num1&num2)<<1
    return num1 if num1 <= max_value else ~(num1^mask)
~(num1^mask)的理解
  • 此处实际上是假设num1是一个32位的整型数(在C++和Java中的定义为int,也就是默认32位整型数),Python中由于无法定义整型位数,也就无法识别正常的负数
    • num1为0x8FFFFFFF在C++和Java中为32位整型数时,实际上是个负数
    • Python无法识别到这个负数,因为Python的数值存储长度很长,强制转换也不行
    • 如果我们可以将num1的后32位不变,前面的所有位置取反(由0变成1) ,那么就可以得到Python中的负数值
    • 此时如果先用num1^mask即可得到一个后面32位取反,前面其他位置全部保留正常(实际上都还是0)
      • 原因是mask后32位是1,所以num1每一位为0时异或结果为1,为1时异或结果为0,整体上就等价于对后32位取反
    • 在后面的位置取反后再对所有位置全部取反,那么就可以实现对num1的后32位不变,前面所有位置取反的操作得到Python中的负数的补码

数据结构——堆排序

本文介绍堆排序的思想和代码

  • 参考链接:堆排序【超详细+代码】

相关基本概念

  • 完全二叉树 :除最后一层外,每层节点都填满;最后一层节点从左到右排列
    • 完全二叉树的核心性质 :任意节点索引为 i 的子节点为:
      • 左子节点索引 2*i+1
      • 右子节点索引 2*i+2
  • 大顶堆(最大堆) :每个父节点的值 ≥ 其子节点的值(堆排序常用)
    • 注:小顶堆则与大顶堆相反,父节点的值小于其他子节点
  • 堆排序是一种基于完全二叉树结构(堆)的高效排序算法
    • 时间复杂度稳定为 \(O(n \log n)\),且是原地排序(空间复杂度 \(O(1)\))

基本思想

  • 堆排序实现步骤:
    • 第一步:初始化生成最大堆(heapify)
      • 功能:将无序数组转化为大顶堆(根节点是整个数组的最大值)
      • 做法:从倒数第一个非叶节点开始,按照下虑操作找到该节点在最大堆中的位置,逐步遍历每一个非叶结点执行上述操作,生成最大堆
    • 第二步:交换最大值到末尾
      • 功能:将堆顶的最大值放到数组末尾(确定一个元素的最终位置)
      • 做法:交换根节点与最后一个元素的值,树节点减少1(刚才根节点是当前树的最大值,更换以后就是有序的元素了,不再参与最大堆调整)
    • 第三步:重新生成最大堆(调整根节点)
      • 功能:对剩余未排序的元素重新调整为大顶堆
      • 做法:调整新换上来的根节点(下虑操作),找到其应该在的地方,重新生成最大堆
    • 循环执行:循环 第二步 和 第三步,直到最大堆只剩下一个元素

总结

  1. 堆排序的核心是构建大顶堆 + 逐步提取堆顶最大值,利用堆的特性实现高效排序
  2. 堆化(heapify)是关键操作,作用是修复不符合大顶堆规则的子树,时间复杂度为 $O(\log n)$
  3. 堆排序的优势是时间复杂度稳定(不受数据分布影响),缺点是不稳定(相同值的元素可能改变相对位置)

堆排序关键子步骤:堆化(Heapify)

  • 堆化是堆排序的核心操作,作用是修复不符合大顶堆规则的子树
    • 输入:数组、堆的大小、需要调整的父节点索引 i
    • 逻辑:
      • 1)先假设当前父节点 i 是最大值节点
      • 2)核心 :找到 i 的左子节点(索引 2*i+1)和右子节点(索引 2*i+2)
      • 3)比较父节点与左右子节点,找到最大值的索引 largest
      • 4)如果 largest != i(父节点不是最大值),交换两者的值,使得父节点变成最大值;然后并递归调整被交换的子节点(因为交换后子树可能不符合堆规则)

堆排序 Python 代码实现(带详细注释)

  • 代码实现:

    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
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    def heap_sort(arr):
    """
    堆排序(升序)实现
    :param arr: 待排序的数组(列表)
    """
    n = len(arr)

    # 第一步:构建大顶堆(从最后一个非叶子节点开始向前遍历)
    # 最后一个非叶子节点的索引:n//2 - 1(完全二叉树特性)
    for i in range(n // 2 - 1, -1, -1):
    heapify(arr, n, i)

    # 第二步:逐个取出堆顶元素(最大值),放到数组末尾
    for i in range(n - 1, 0, -1):
    # 交换堆顶(arr[0])和当前未排序部分的末尾(arr[i])
    arr[0], arr[i] = arr[i], arr[0]
    # 对剩余未排序的元素(0到i-1)重新堆化(堆大小变为i)
    heapify(arr, i, 0)

    def heapify(arr, heap_size, parent_idx):
    """
    堆化函数:调整以 parent_idx 为根的子树为大顶堆
    :param arr: 待调整的数组
    :param heap_size: 当前堆的有效大小(未排序部分的长度)
    :param parent_idx: 需要调整的父节点索引
    """
    largest = parent_idx # 初始化最大值为父节点
    left_child = 2 * parent_idx + 1 # 左子节点索引
    right_child = 2 * parent_idx + 2 # 右子节点索引

    # 1. 比较父节点与左子节点,更新最大值索引
    if left_child < heap_size and arr[left_child] > arr[largest]:
    largest = left_child

    # 2. 比较当前最大值与右子节点,更新最大值索引
    if right_child < heap_size and arr[right_child] > arr[largest]:
    largest = right_child

    # 3. 如果最大值不是父节点,交换并递归调整子树
    if largest != parent_idx:
    arr[parent_idx], arr[largest] = arr[largest], arr[parent_idx]
    # 递归调整被交换的子节点(因为交换后子树可能不符合堆规则)
    heapify(arr, heap_size, largest) # largest 是刚换下来的父节点,此时作为父节点继续判断是否满足最大堆

    # 测试示例
    if __name__ == "__main__":
    # 待排序数组
    test_arr = [12, 11, 13, 5, 6, 7]
    print("排序前数组:", test_arr)

    heap_sort(test_arr)

    print("排序后数组:", test_arr)
  • 代码中关键逻辑解释:

    • 1)构建大顶堆 :
      • 从最后一个非叶子节点(n//2 -1)开始向前遍历,因为叶子节点本身就是堆(单个节点满足堆的性质),无需调整
      • 每个节点通过 heapify 函数调整为大顶堆,最终整个数组变成大顶堆(根节点是最大值)
    • 2)交换堆顶与末尾元素 :
      • 堆顶(arr[0])是当前未排序部分的最大值,交换到数组末尾后,该元素的位置就固定了
      • 每次交换后,堆的有效大小减 1(因为末尾元素已排序),重新对剩余元素堆化
    • 3)堆化函数的递归调整 :
      • 交换父节点和子节点后,子节点所在的子树可能不再是大顶堆,因此需要递归调整该子树

附录:数据流中的中位数实现

  • 题目实现
    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
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    # -*- coding:utf-8 -*-
    class Solution:
    def __init__(self):
    self.left_max = []
    self.right_min = []

    def Insert(self, num):
    # write code here
    if not self.left_max or num < self.left_max[0]:
    self.left_max.append(num)
    Solution.heapify_up_max(self.left_max, len(self.left_max)-1)
    if len(self.left_max) > len(self.right_min) + 1:
    mid = Solution.pop_max(self.left_max)
    self.right_min.append(mid)
    Solution.heapify_up_min(self.right_min, len(self.right_min)-1)
    else:
    self.right_min.append(num)
    Solution.heapify_up_min(self.right_min, len(self.right_min)-1)
    if len(self.right_min) > len(self.left_max)+1:
    mid = Solution.pop_min(self.right_min)
    self.left_max.append(mid)
    Solution.heapify_up_max(self.left_max, len(self.left_max)-1)
    pass

    def GetMedian(self):
    # write code here
    if len(self.right_min) > len(self.left_max):
    return self.right_min[0]
    elif len(self.right_min) < len(self.left_max):
    return self.left_max[0]
    else:
    return (self.right_min[0] + self.left_max[0])/2
    pass

    @staticmethod
    def heapify_up_max(a, i):
    curr = i
    while (curr-1)//2 >= 0:
    if a[curr] > a[(curr-1)//2]:
    a[(curr-1)//2], a[curr] = a[curr], a[(curr-1)//2]
    else:
    break
    curr = (curr-1)//2

    @staticmethod
    def heapify_up_min(a, i):
    curr = i
    while (curr-1)//2 >= 0:
    if a[curr] < a[(curr-1)//2]:
    a[(curr-1)//2], a[curr] = a[curr], a[(curr-1)//2]
    else:
    break
    curr = (curr-1)//2

    @staticmethod
    def heapify_down_max(a, i):
    maxi = i
    if i*2+1 < len(a) and a[i*2+1] > a[maxi]:
    maxi = i*2+1
    if i*2+2 < len(a) and a[i*2+2] > a[maxi]:
    maxi = i*2+2
    if maxi != i:
    a[maxi], a[i] = a[i], a[maxi]
    Solution.heapify_down_max(a, maxi)

    @staticmethod
    def heapify_down_min(a, i):
    mini = i
    if i*2+1 < len(a) and a[i*2+1] < a[mini]:
    mini = i*2+1
    if i*2+2 < len(a) and a[i*2+2] < a[mini]:
    mini = i*2+2
    if mini != i:
    a[mini], a[i] = a[i], a[mini]
    Solution.heapify_down_min(a, mini)

    @staticmethod
    def pop_min(a):
    result = a[0]
    a[0] = a.pop()
    Solution.heapify_down_min(a, 0)
    return result

    @staticmethod
    def pop_max(a):
    result = a[0]
    a[0] = a.pop()
    Solution.heapify_down_max(a, 0)
    return result

通用——日常杂记

本文记录一些日常通用的笔记,定期整理入其他的路径下


日常笔记

  • Linux 中,realpath 命令可显示文件绝对路径
  • jsonl 文件中每一行是一个 json 字符串
  • Linux 中,汇总显示当前文件夹下占用大小:du -bsh
    • -s 参数表示总结性展示一个数字即可
  • Python 中,函数内部若不使用 return 而是 ...,则表示返回 None
  • Python 中,x = a or b 等价于 x = a if a else b

U盘——做系统盘后恢复空间

U盘做系统盘后空间会减少,本文给出恢复U盘空间的方法
U盘做系统时会自动隐藏部分空间,用于放置启动文件,这些被隐藏的空间用普通的格式化方法无法恢复


Windows

  • 第一步:清除 U 盘: cmd 输入diskpart

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # show all disks
    list disk
    # select target disk(our usb flash disk)
    select disk=n
    # show all disks again
    # make sure we have chosen the right disk
    # there will be a star in choosed disk
    list disk
    # clean the disk
    clean
  • 第二步:用任意方式格式化 U 盘

1…484950…66
Joe Zhou

Joe Zhou

Stay Hungry. Stay Foolish.

659 posts
53 tags
GitHub E-Mail
© 2026 Joe Zhou
Powered by Hexo
|
Theme — NexT.Gemini v5.1.4