Jiahong的个人博客

凡事预则立不预则废


  • Home

  • Tags

  • Archives

  • Search

Python——修改图片背景颜色

Posted on 2018-10-23

利用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——缺失值处理

Posted on 2018-10-13

missing值处理


对于数值类型的特征

中位数填充

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

均值填充

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

加权填充

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

对于Category类型

特殊字符填充

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

填充众数

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

什么情况下不用填充

树模型

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

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

不敏感模型

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

敏感模型

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

Chrome——常用插件总结

Posted on 2018-10-11

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


Markdown Viewer

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

油猴插件(Tampermonkey)

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

Postman

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

AdBlock

  • 能屏蔽大部分的广告

SwitchyOmega

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

FS——文件系统总结

Posted on 2018-10-11

FAT32

常用于闪存

优点

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

缺点

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

ExFAT

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

优点

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

缺点

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

NTFS

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

优点

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

缺点

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

MacOS日志式

优点

  • 没啥缺点也是一种优点

缺点


总结

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

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

Posted on 2018-10-11

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


模块导入问题

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

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

Posted on 2018-10-11

参考博客: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中的负数的补码

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

Posted on 2018-10-09

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盘

Numpy——reshape函数

Posted on 2018-10-09

维度确定

规则为从前到后,参数由外到内,由行到列

  • values.reshape(2,3) 表示二行三列(2,3)
  • values.reshape(4,2,3) 表示四个(2,3),四个两行三列
  • values.reshape(5,4,2,3) 表示五个(4,2,3)

reshape本质理解

实际上numpy多维数组变化维度时,内存数据没有变化,只是有一个shape参数指明维度即可

  • 每次变化维度的过程可以理解为先将维度还原到(参考:维度确定)一维数组,然后再转化成制定的目标维度
  • 返回的是新对象,但是新老对象数据共享,只有shape等内部属性不一样,所以reshape操作不浪费内存,也不耗费时间,但是需要注意数据共享可能造成的误操作
    • 测试说明: 使用DataFrame.values.reshape()生成的新对象内存数据不共享

特殊参数 “-1“

用于智能补齐某一维度,只能有一个参数为-1

  • 除了-1参数外,其余的部分一定要能够被整除
  • -1本身占用一个维度
  • 假设一维数组维度为12
    • (-1, 2) <==> (6, 2) <==> (6, -1)
    • (-1) <==> (12)

Numpy——transpose函数

Posted on 2018-10-09

用于矩阵维度的转换


transpose函数的使用

  • 用法说明

    1
    nums.transpose(dims)
    • nums为np.ndarray类型的对象
    • dims为表示维度排列的tuple
      • 对于二维矩阵,只有0, 1两个维度,所以只能是(0,1), (1,0)
      • 三维矩阵,有0,1,2三个维度,所以可以是是(0,1,2)的所有全排列,共6个

简单易懂的解释

  • 只改变维度, 每一维度对应的数据不会变, 比如图片的shape为: (28, 28, 3),那么无论怎么变化维度, 变换后的数据中维度大小为3的那个维度都是表示通道, (28,28)总是表示图片的每个通道的数据
  • 原始numpy.ndarray,shape为(2,3,4)
    • .transpose((0,1,2)): shape为(2,3,4), 不变
    • .transpose((1,0,2)): shape为(3,2,4)
    • .transpose((2,1,0)): shape为(4,3,2)
  • 测试代码
    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
    import numpy as np

    origin = np.random.random_integers(1, 100, (2,3,4))
    print("-"*20 + "origin")
    print(origin)
    print("-"*20 + "(0,1,2)")
    print(origin.transpose((0,1,2)))
    print("-"*20 + "(1,0,2)")
    print(origin.transpose((1,0,2)))
    print("-"*20 + "(2,1,0)")
    print(origin.transpose((2,1,0)))

    # Output:
    --------------------origin
    [[[50 36 80 53]
    [45 45 94 91]
    [49 29 69 53]]

    [[85 83 61 18]
    [16 89 80 60]
    [99 13 36 40]]]
    --------------------(0,1,2)
    [[[50 36 80 53]
    [45 45 94 91]
    [49 29 69 53]]

    [[85 83 61 18]
    [16 89 80 60]
    [99 13 36 40]]]
    --------------------(1,0,2)
    [[[50 36 80 53]
    [85 83 61 18]]

    [[45 45 94 91]
    [16 89 80 60]]

    [[49 29 69 53]
    [99 13 36 40]]]
    --------------------(2,1,0)
    [[[50 85]
    [45 16]
    [49 99]]

    [[36 83]
    [45 89]
    [29 13]]

    [[80 61]
    [94 80]
    [69 36]]

    [[53 18]
    [91 60]
    [53 40]]]

与reshape的区别

reshape的用法:
1
nums.reshape(shape)
* `nums`为`np.ndarray`类型的对象
* `shape`为表示维度排列的`tuple`
    * `shape`必须与原始`nums.shape`兼容,即每个`shape`元素相乘的结果相等
  • 从numpy中矩阵的存储开始讲起,numpy存储数据是C++的方式存储为一行的,然后分为不同shape以不同方式进行索引
  • reshape相当于是把原始数据先还原成一行(实际上没操作),然后再转变成指定shape的数据,本质上数据存储没变化,只是shape变了,以后索引方式也就变了
区别
  • reshape修改的只是shape,每个维度的意义变了
  • transpose修改的只是维度,同时shape跟着变化而已,每个维度的顺序变了,但是意义不会变

Sklearn——CountVectorizer使用介绍

Posted on 2018-10-07

导入

1
from sklearn.feature_extraction.text import CountVectorizer

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 新建一个CountVectorizer对象,以下简称CV对象
vectoerizer = CountVectorizer(min_df=min_df, max_df=max_df, stop_words=stop_words, token_pattern=r"(?u)\b[\w-][\w-]+\b")

# 使用文本集合(一个文本的列表)训练当前CountVectorizer对象
# texts = ["this is a text", "this is another text"]
vectoerizer.fit(texts)

# 训练后可以读取当前CV对象信息
bag_of_words = vectoerizer.get_feature_names()

# 使用CV对象生成任意文本集合的信息,返回对象是文档到字典的数组统计(统计单词数量)
# 这里的texts不必要是之前使用过的,可以是新的文本集合,但是词典已经确定,不能再拓展了,不存在字典中的词直接忽略
X = vectoerizer.transform(texts)

# 使用X对象,toarray()将返回 term/token counts 矩阵,可直接用于TfidfTransformer等对象的训练
X.toarray()

重要参数说明

  • min_df: 用于排除出现次数太少的terms

    • min_df = 0.01 意味着将忽略出现在少于%1的文档中的词
    • min_df = 5 意味着将忽略只出现在5篇以下文档中的词,不包括5篇
  • max_df:

    • max_df = 0.50 意味着将忽略出现在多于%50的文档中的词
    • max_df = 25 意味着将忽略出现在25篇以上文档中的词,不包括25篇
1…567…20
Joe Zhou

Joe Zhou

世界上只有一种真正的英雄主义,那就是在认清生活真相之后依然热爱生活。 ——罗曼·罗兰

195 posts
38 tags
GitHub E-Mail
© 2024 Joe Zhou
Powered by Hexo
|
Theme — NexT.Gemini v5.1.4