草庐IT

一定要用Photoshop?no!动手用Python做一个颜色提取器! ⛵

ShowMeAI 2023-03-28 原文

? 作者:韩信子@ShowMeAI
? Python3◉技能提升系列https://www.showmeai.tech/tutorials/56
? 计算机视觉实战系列https://www.showmeai.tech/tutorials/46
? 本文地址https://www.showmeai.tech/article-detail/404
? 声明:版权所有,转载请联系平台与作者并注明出处
? 收藏ShowMeAI查看更多精彩内容

? 引言

我们本次用到的数据集是 ?Kaggle 100 种鸟数据集,大家可以通过 ShowMeAI 的百度网盘地址下载。

? 实战数据集下载(百度网盘):公众号『ShowMeAI研究中心』回复『实战』,或者点击 这里 获取本文 [39]使用Python构建图片颜色提取器Bird 450 Species数据集

ShowMeAI官方GitHubhttps://github.com/ShowMeAI-Hub

大家在做图像处理或者制作PPT时,一个非常常用的功能是颜色提取,我们可以通过提取器从已有图像的某个位置提取颜色,而不用自己肉眼比对和选择。今天ShowMeAI就带大家用python来实现这个功能。

我们会构建如下2个颜色提取器:

  • 简单提取器——从单个图像中选择颜色
  • 复杂提取器——从多个图像中选择颜色列表并显示颜色

本文的实现涉及python编程知识与部分数据可视化知识,大家可以通过ShowMeAI的以下教程和文章进行系统学习:

?图解Python编程:从入门到精通系列教程

? 数据科学工具库速查表 | Matplotlib 速查表

? 颜色提取器实现

? 导入工具库

首先我们需要导入本次所需的工具库,matplotlib.image用于显示图像,pyperclip用于将字符串保存到剪贴板,glob用于处理文件路径。

#Imports
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg

import pyperclip

import random
import glob

我们将从不同的鸟类图像中提取颜色(即大家在第1节看到的图像)。我们读取路径下所有的jpg格式图像,代码如下:

#Dataset
read_path = "../../data/birds/"
img_path = glob.glob(read_path + "*.jpg")

? 简单颜色提取器实现

我们先实现一个简单颜色提取器。它的功能是,每次我们单击图像中的某个位置,该像素的 RGB 通道会保存到我们的剪贴板中,然后我们可以将该值粘贴到笔记本中。

我们先构建一个onclick函数,每次单击图像时都会运行此程序。我们获取点击的 x 和 y 坐标,然后得到该坐标处像素的 RGB 通道值,并将其作为字符串保存到剪贴板。完整的代码如下:

def onclick(event):
    global img
    
    # get x,y of click
    x = round(event.xdata)
    y = round(event.ydata)
    
    # get RGB values
    rgb = img[y][x]
    
    # save to clip board
    pyperclip.copy(str(rgb))

我们要使用上面这个函数,我们首先使用 matplotlib 创建一个图形,然后设置该图的交互功能,将onclick函数作为参数传入,这样我们每次点击就会调用上述函数进行颜色提取。

%matplotlib notebook
global img

fig = plt.figure(figsize=(5,5))

#Add an interactive widget to figure 
cid = fig.canvas.mpl_connect('button_press_event', onclick)

#Load image and add count
path = img_path[0]
img = mpimg.imread(path)

plt.imshow(img)
plt.show()

注意:上述代码的第2行使用了全局变量,这样就可以在onclick函数中更新这些变量。

? 复杂颜色提取器实现

下面我们来构建一个复杂颜色提取器,它实现的功能如下图所示:我们多次点击不同图像的多个位置,我们会按照顺序编号和记录颜色(注意颜色框左上角的红色数字),并把颜色保存到列表中。

我们还是需要构建onclick函数,和之前的简单颜色提取器有点类似,这里的主要区别在于我们不直接保存 RGB 通道值,而是调用change_choice来调整右侧显示的提取颜色。

def onclick(event):
    global img
    global rgb
    
    # get x,y of click
    x = round(event.xdata)
    y = round(event.ydata)
    
    # get RGB values
    rgb = img[y][x]
    
    #Update second plot with colour
    change_choice()

我们再定义一个函数onpress,它会在按下键盘时运行。这个函数在按下了不同键的情况下做不同的处理:(change_imagechange_choice会分别用于更新图片和更新显示颜色框,在后续会定义)

  • n:运行change_image函数。
  • c:我们将 RGB 通道值保存到剪贴板和颜色列表中,接着运行change_choice函数。

具体代码实现如下:

def onpress(event):
    global rgb
    global colours
    
    #Get key 
    key = event.key

    if key == 'n':
        change_image()
        
    elif key == 'c':
         # save to clip board
        pyperclip.copy(str(rgb))
        
        # add to list of colours
        colours.append(rgb)
        
        change_choice()

change_choice函数用于更新右侧颜色框。右侧的颜色框有与图像框相同的尺寸,并且根据当前全局 rgb 值进行颜色显示。

def change_choice():
    global img
    global ax
    global colours
    global rgb
    
    # remove previous count
    for txt in ax[1].texts:
        txt.set_visible(False)
    
    # create array of colour choice
    dims = np.shape(img)
    col = np.array([[rgb]*dims[0]]*dims[1])
    ax[1].imshow(col)
    
    # update colour count
    ax[1].text(0, 15, len(colours),color='r',size=20)
    
    plt.show()

change_choice函数在2处调用和执行:

  • 点击图片时调用的onclick函数中,它完成 全局 rgb 更新并调整框中的颜色。
  • 调用onpress函数并按下“c”时,这里颜色列表的长度+1,颜色计数也会改变。

接下来我们定义change_image函数。我们在按下“n”时会调用它更新图像框。代码如下:

def change_image():
    global img_path
    global img
    global ax
    global rgb
    
    # close all open plots
    plt.close('all')
    
    fig,ax = plt.subplots(1,2,figsize=(10,5))
    
    # add an interactive widget to figure 
    cid = fig.canvas.mpl_connect('button_press_event', onclick)
    cid2 = fig.canvas.mpl_connect('key_press_event', onpress)

    # load random image
    path = random.choice(img_path)
    img = mpimg.imread(path)
    
    ax[0].imshow(img)
    
    # reset the colour window
    rgb = [255,255,255]
    change_choice()

我们可以通过运行change_image函数来启动颜色选择器,如下:

%matplotlib tk
global img_path
global colours
colours = []

# load image paths
read_path = "../../data/birds/"
img_path = glob.glob(read_path + "*.jpg")

# start widget
change_image()

接下来当你就可以使用这个复杂颜色提取器啦,在您遍历图像并保存颜色时,颜色列表随之更更新,我们在下图的 colours 里可以看到提取的颜色构建的rgb值序列。

参考资料

推荐阅读

有关一定要用Photoshop?no!动手用Python做一个颜色提取器! ⛵的更多相关文章

  1. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  2. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  3. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  4. ruby-on-rails - 渲染另一个 Controller 的 View - 2

    我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>

  5. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  6. ruby - 为什么 SecureRandom.uuid 创建一个唯一的字符串? - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?

  7. ruby-on-rails - Rails - 从另一个模型中创建一个模型的实例 - 2

    我有一个正在构建的应用程序,我需要一个模型来创建另一个模型的实例。我希望每辆车都有4个轮胎。汽车模型classCar轮胎模型classTire但是,在make_tires内部有一个错误,如果我为Tire尝试它,则没有用于创建或新建的activerecord方法。当我检查轮胎时,它没有这些方法。我该如何补救?错误是这样的:未定义的方法'create'forActiveRecord::AttributeMethods::Serialization::Tire::Module我测试了两个环境:测试和开发,它们都因相同的错误而失败。 最佳答案

  8. ruby - 匹配大写字母并用后续字母填充,直到一定的字符串长度 - 2

    我有一个驼峰式字符串,例如:JustAString。我想按照以下规则形成长度为4的字符串:抓取所有大写字母;如果超过4个大写字母,只保留前4个;如果少于4个大写字母,则将最后大写字母后的字母大写并添加字母,直到长度变为4。以下是可能发生的3种情况:ThisIsMyString将产生TIMS(大写字母);ThisIsOneVeryLongString将产生TIOV(前4个大写字母);MyString将生成MSTR(大写字母+tr大写)。我设法用这个片段解决了前两种情况:str.scan(/[A-Z]/).first(4).join但是,我不太确定如何最好地修改上面的代码片段以处理最后一种

  9. ruby - 用 Ruby 编写一个简单的网络服务器 - 2

    我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b

  10. ruby - 一个 YAML 对象可以引用另一个吗? - 2

    我想让一个yaml对象引用另一个,如下所示:intro:"Hello,dearuser."registration:$introThanksforregistering!new_message:$introYouhaveanewmessage!上面的语法只是它如何工作的一个例子(这也是它在thiscpanmodule中的工作方式。)我正在使用标准的ruby​​yaml解析器。这可能吗? 最佳答案 一些yaml对象确实引用了其他对象:irb>require'yaml'#=>trueirb>str="hello"#=>"hello"ir

随机推荐