抠图党福音:教你一键分割图像

摘要:输入一个图像,通过Segment Anything模型即可获得图像所有目标的分割点位置,再通过位置将图像进行分割保存。

本文分享自华为云社区《一键分割图像》,作者:雨落无痕 。

Segment Anything

Segment Anything Model(SAM)通过点或框等输入提示生成高质量的对象分割区域,并且可以用于为图像中的所有对象生成分割区域。它已经在1100万张图像和11亿个分割区域的数据集上进行了训练,并且在各种分割任务上具有强大的零样本性能。

SAM 的工作原理:可提示分割

在自然语言处理和最近的计算机视觉领域,最令人兴奋的发展之一是基础模型的发展,这些基础模型可以使用提示技术(prompting)对新数据集和任务执行零样本和小样本学习。我们从这类工作中汲取了灵感。

我们训练 SAM 为任何提示返回有效的分割掩码,其中提示可以是前景/背景点、粗框或掩码、自由格式文本。或者一般来说,提示图像中要分割的内容的任何信息。有效掩码的要求仅仅意味着即使提示不明确并且可能指代多个对象(例如,衬衫上的一个点可能表示衬衫或穿着它的人),输出也应该是一个合理的掩码对象之一。此任务用于预训练模型并通过提示解决一般的下游分割任务。

我们观察到预训练任务和交互式数据收集对模型设计施加了特定的限制。特别是,该模型需要在Web浏览器的CPU上实时运行,以允许我们的标注者实时交互地使用 SAM 以高效地进行标注。虽然运行时限制意味着质量和运行时之间的权衡,但我们发现简单的设计在实践中会产生良好的结果。具体地,图像编码器为图像生成一次性嵌入向量,而轻量级编码器将任何提示实时转换为嵌入向量。然后将这两个信息源组合在一个预测分割掩码的轻量级解码器中。在计算图像嵌入后,SAM 可以在 50 毫秒内根据网络浏览器中的任何提示生成一个分割。

SAM模型总体上分为3部分:

绿色的图像编码器,基于可扩展和强大的预训练方法,我们使用MAE预训练的ViT,最小限度地适用于处理高分辨率输入。图像编码器对每张图像运行一次,在提示模型之前进行应用。

紫色的提示编码器,考虑两组prompt:稀疏(点、框、文本)和密集(掩码)。我们通过位置编码来表示点和框,并将对每个提示类型的学习嵌入和自由形式的文本与CLIP中的现成文本编码相加。密集的提示(即掩码)使用卷积进行嵌入,并通过图像嵌入进行元素求和。

橙色的提示编码器,掩码解码器有效地将图像嵌入、提示嵌入和输出token映射到掩码。该设计的灵感来自于DETR,采用了对(带有动态掩模预测头的)Transformer decoder模块的修改。

Segment Anything适配ModelArts

使用方法:

输入一个图像,通过Segment Anything模型即可获得图像所有目标的分割点位置,再通过位置将图像进行分割保存。

本案例需使用 Pytorch-1.8 GPU-P100 及以上规格运行

点击Run in ModelArts,将会进入到ModelArts CodeLab中,这时需要你登录华为云账号,如果没有账号,则需要注册一个,且要进行实名认证,参考《ModelArts准备工作_简易版》 即可完成账号注册和实名认证。登录之后,等待片刻,即可进入到CodeLab的运行环境

出现 Out Of Memory ,请检查是否为您的参数配置过高导致,修改参数配置,重启kernel或更换更高规格资源进行规避❗❗❗

1.环境准备

为了方便用户下载使用及快速体验,本案例已将代码及segment-anything预训练模型转存至华为云OBS中。模型下载与加载需要几分钟时间。

import os
import torch
import os.path as osp
import moxing as mox
path = osp.join(os.getcwd(),'segment-anything')
if not os.path.exists(path):
 mox.file.copy_parallel('obs://modelarts-labs-bj4-v2/case_zoo/segment-anything', path)
 if os.path.exists(path):
 print('Download success')
 else:
        raise Exception('Download Failed')
else:
 print("Model Package already exists!") 

check GPU & 安装依赖

大约耗时1min

%cd segment-anything
!pip install --upgrade pip
!pip install torch==1.12.1 torchvision==0.13.1 torchaudio==0.12.1
!pip install opencv-python matplotlib
!python setup.py install
import numpy as np
import matplotlib.pyplot as plt
import cv2
import copy
import torch
import torchvision
print("PyTorch version:", torch.__version__)
print("Torchvision version:", torchvision.__version__)
print("CUDA is available:", torch.cuda.is_available())

2.加载模型

from segment_anything import sam_model_registry, SamAutomaticMaskGenerator, SamPredictor
sam_checkpoint = "sam_vit_h_4b8939.pth"
model_type = "vit_h"
device = "cuda"
sam = sam_model_registry[model_type](checkpoint=sam_checkpoint)
sam.to(device=device)
mask_generator = SamAutomaticMaskGenerator(
    model=sam,
    #points_per_side=32,
    #pred_iou_thresh=0.86,
    #stability_score_thresh=0.92,
    #crop_n_layers=1,
    #crop_n_points_downscale_factor=2,
    #min_mask_region_area=100,  # Requires open-cv to run post-processing
)

3.一键分割所有目标

def show_anns(anns,image):
 segment_image = copy.copy(image)
 segment_image.astype("uint8")
 if len(anns) == 0:
 return
 sorted_anns = sorted(anns, key=(lambda x: x['area']), reverse=True)
 for ann in sorted_anns:
        mask_2d = ann['segmentation']
 h,w = mask_2d.shape
        mask_3d_color = np.zeros((h,w,3), dtype=np.uint8)
        mask = (mask_2d!=0).astype(bool)
 rgb = np.random.randint(0, 255, (1, 3), dtype=np.uint8)
        mask_3d_color[mask_2d[:, :] == 1] = rgb
 segment_image[mask] = segment_image[mask] * 0.5 + mask_3d_color[mask] * 0.5
 return segment_image
image = cv2.imread('images/dog.jpg')
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
masks = mask_generator.generate(image)
segment_image = show_anns(masks,image)
fig = plt.figure(figsize=(25, 10))
ax1 = fig.add_subplot(1, 2, 1)
plt.title('Original image', fontsize=16)
ax1.axis('off')
ax1.imshow(image)
ax2 = fig.add_subplot(1, 2, 2)
plt.title('Segment image', fontsize=16)
ax2.axis('off')
ax2.imshow(segment_image)
plt.show()

4.保存所有分割的图片

将所有识别出来的分割位置进行分割,并保存成图片。

def apply_mask(image, mask, alpha_channel=True):#应用并且响应mask
 if alpha_channel:
        alpha = np.zeros_like(image[..., 0])#制作掩体
        alpha[mask == 1] = 255#兴趣地方标记为1,且为白色
        image = cv2.merge((image[..., 0], image[..., 1], image[..., 2], alpha))#融合图像
 else:
        image = np.where(mask[..., None] == 1, image, 0)
 return image
def mask_image(image, mask, crop_mode_=True):#保存掩盖部分的图像(感兴趣的图像)
 if crop_mode_:
        y, x = np.where(mask)
 y_min, y_max, x_min, x_max = y.min(), y.max(), x.min(), x.max()
 cropped_mask = mask[y_min:y_max+1, x_min:x_max+1]
 cropped_image = image[y_min:y_max+1, x_min:x_max+1]
 masked_image = apply_mask(cropped_image, cropped_mask)
 else:
 masked_image = apply_mask(image, mask)
 return masked_image
def save_masked_image(image, filepath):
 if image.shape[-1] == 4:
        cv2.imwrite(filepath, image, [cv2.IMWRITE_PNG_COMPRESSION, 9])
 else:
        cv2.imwrite(filepath, image)
 print(f"Saved as {filepath}")
def save_anns(anns,image,path):
 if len(anns) == 0:
 return
 sorted_anns = sorted(anns, key=(lambda x: x['area']), reverse=True)
    index = 1
 for ann in sorted_anns:
        mask_2d = ann['segmentation']
 segment_image = copy.copy(image)
 masked_image = mask_image(segment_image, mask_2d)
        filename = str(index) + '.png'
 filepath = os.path.join(path, filename)
 save_masked_image(masked_image, filepath)
        index = index + 1
save_path = 'result/'
if not os.path.exists(save_path):
 os.mkdir(save_path)
image = cv2.imread('images/dog.jpg')
masks = mask_generator.generate(image)
save_anns(masks,image,save_path)

5. Gradio可视化部署

为了方便大家使用一键分割案例,当前增加了Gradio可视化部署案例演示。

运行如下代码,Gradio应用启动后可在下方页面进行一键分割图像,您也可以分享public url在手机端,PC端进行访问生成图像。

示例效果如下:

!pip install gradio==3.24.1
def segment_image(image):
    masks = mask_generator.generate(image)
 return show_anns(masks,image)
def show_image(image):
    masks = mask_generator.generate(image)
 if len(masks) == 0:
 return
 sorted_anns = sorted(masks, key=(lambda x: x['area']), reverse=True)
    index = 1
 image_list = []
 for ann in sorted_anns:
        mask_2d = ann['segmentation']
 segment_image = copy.copy(image)
 masked_image = mask_image(segment_image, mask_2d)
 image_list.append(masked_image)
 return image_list
import gradio as gr
with gr.Blocks() as demo:
 with gr.Row():
 with gr.Column():
 img_in = gr.Image(source='upload')
 with gr.Row():
 segment_button = gr.Button("segment",variant="primary")
 save_button = gr.Button("segment_images",variant="primary")
 with gr.Row():
 with gr.Column():
 img_out = gr.Image()
 with gr.Row():
 result_gallery = gr.Gallery(label='Output', show_label=False, elem_id="gallery").style(grid=6, height='auto')
 segment_button.click(segment_image,
                 inputs= [img_in], 
                 outputs=[img_out])
 save_button.click(show_image,
                 inputs= [img_in], 
                 outputs=[result_gallery])
demo.launch(share=True)

相关链接:

Notebook案例地址:一键分割图像

AI Gallery:http://developer.huaweicloud.com/develop/aigallery/home.html

速来体验!

 

点击关注,第一时间了解华为云新鲜技术~

本文转载于网络 如有侵权请联系删除

相关文章

  • mysql语句大全及例子_SQL语句大全实例教程.pdf[通俗易懂]

    大家好,又见面了,我是你们的朋友全栈君。SQL语句大全实例教程无论您是一位SQL的新手,或是一位只是需要对SQL复习一下的资料仓储业界老将,您就来对地方了。这个SQL教材网站列出常用的SQL指令,包含以下几个部分:♦SQL指令:SQL如何被用来储存、读取、以及处理数据库之中的资料。♦表格处理:SQL如何被用来处理数据库中的表格。♦进阶SQL:介绍SQL进阶概念,以及如何用SQL来执行一些较复杂的运算。♦SQL语法:这一页列出所有在这个教材中被提到的SQL语法。对于每一个指令,我们将会先列出及解释这个指令的语法,然后用一个例子来让读者了解这个指令是如何被运用的。当您读完了这个网站的所有教材后,您将对SQL的语法会有一个大致上的了解。另外,您将能够正确地运用SQL来由数据库中获取信息。笔者本身的经验是,虽然要对SQL有很透彻的了解并不是一朝一夕可以完成的,可是要对SQL有个基本的了解并不难。希望在看完这个网站后,您也会有同样的想法。SQL指令SELECT是用来做什么的呢?一个最常用的方式是将资料从数据库中的表格内选出。从这一句回答中,我们马上可以看到两个关键字:从(FROM)数据库中的表格

  • 如何实现项目管理自动化?为什么项目需要需要自动化?

    如何实现项目管理自动化?为什么项目需要需要自动化? 为什么项目管理中需要自动化技术?自动化可以理解为通过技术、AI等能力让任务或者流程在尽量少人为干预的情况下自动进行处理。在项目管理的流程中,能实现项目管理自动化,可以很好的帮助项目管理人员,智能的去做判断和执行消息通知。比如,你可以通过自动化去自动在项目逾期时,发消息提醒通知;如果没有自动化技术,通过就需要通过人工的在Excel里进行函数计算和比对,来实现项目逾期的警告。 项目管理的自动化是希望把工作中大量的重复的、耗时的、需要人工处理的繁复工作自动智能的按照一定规则去处理掉,从而可以让团队中负责项目管理(一般都是比较核心的成员)可以把自己的时间放在更重要的工作上,而且减少人工复制粘贴中出错的几率。项目管理讨论会项目管理的过程中什么时候需要使用到自动化? 日常的项目管理中,在这些场景需要使用到自动化的能力:关键事件变更自动化通知:比如一个关键重点决策变更,希望这个变更可以及时的和项目成员进行同步项目逾期自动化通知:特别是重点小马,工期较赶的情况下,时间管控非常严格,项目逾期,需要实现自动化的通知。比如某几个关键的节点,如果交付逾期了

  • MySQL(一)

    MySQL(一)發佈於 2019-03-14从本篇开始,我们来说说最流行的数据库——关于MySQL的那些事儿。数据库概念数据库(Database)是按照数据结构来组织、存储和管理数据的建立在计算机存储设备上的仓库。人们通常用数据库这个术语来代表他们使用的数据库软件。这是不正确的,它是引起混淆的根源。确切地说,数据库软件应称为DBMS(数据库管理系统)。数据库是通过DBMS创建和操纵的容器。数据库可以是保存在硬设备上的文件,但也可以不是。在很大程度上说,数据库究竟是文件还是别的什么东西并不重要,因为你并不直接访问数据库;你使用的是DBMS,它替你访问数据库。 数据库分类关系数据库(RelationalDatabase)非关系数据库(NoSQL)关系型数据库基本概念关系型数据库是创建在关系模型基础上的数据库,借助于集合代数等数学概念和方法来处理数据库中的数据。现实世界中的各种实体以及实体之间的各种联系均用关系模型来表示。现如今虽然对此模型有一些批评意见,但它还是数据存储的传统标准。关系模型由关系数据结构、关系操作集合、关系完整性约束三部分组成。标准数据查询语言SQL就是一种基于关系数据库的

  • PHP大文件切割上传并带进度条功能示例

    本文实例讲述了PHP大文件切割上传并带进度条功能。分享给大家供大家参考,具体如下:前面一篇介绍了PHP大文件切割上传功能,这里再来进一步讲解PHP大文件切割上传并带进度条功能。项目结构图:14-slice-upload-fix.html文件:<!DOCTYPEhtml <html <head <metacharset="utf-8" <metahttp-equiv="X-UA-Compatible"content="IE=edge" <title大文件切割上传带进度条</title <linkrel="stylesheet"href="" <script varxhr=newXMLHttpRequest();//xhr对象 varclock=null; functionselfile(){ clock=window.setInterval(sendfile,1000); } varsendfile=(function(){ const

  • Java Review (五、数组)

    数组是编程语言中最常见的一种数据结构,可用于存储多个数据,每个数组元素存放一个数据,通常可通过数组元素的索引来访问数组元素,包括为数组元素赋值和取出数组元素的值。数组数据类型Java的数组要求所有的数组元素具有相同的数据类型。因此,在一个数组中,数组元素的类型是唯一的,即一个数组里只能存储一种数据类型的数据,而不能存储多种数据类型的数据。 一旦数组的初始化完成,数组在内存中所占的空间将被固定下来,因此数组的长度将不可改变。即使把某个数组元素的数据清空,但它所占的空间依然被保留,依然属于该数组,数组的长度依然不变。 Java的数组既可以存储基本类型的数据,也可以存储引用类型的数据,只要所有的数组元素具有相同的类型即可。 数组也是一种数据类型,它本身是一种引用类型。例如int是一个基本类型,但int[]就是一种引用类型了。声明数组变量dataType[]arrayRefVar;//首选的方法 或 dataTypearrayRefVar[];//效果相同,但不是首选方法复制数组是一种引用类型的变量,因此使用它定义一个变量时,仅仅表示定义了一个引用变量(也就是定义了一个指针),这个引用变量

  • 运维记录 - 业务日志清理功能

    线上某些系统业务跑一段时间后,日志就会越来越多,考虑到业务机器磁盘有限,需要添加业务日志清理功能。根据日志所在分区磁盘使用情况来判断是否清理日志,比如当日志分区磁盘空间使用超过90%时,将一周前的日志打包转移到别处(别的分区下或远程存储设备上)。脚本(/opt/script/log_clear.sh)如下:#!/bin/bash #定义日志所在分区当前空间所占比例数(去掉%)。grep-w表示精准匹配,只匹配"/"这个分区 LOG_PARTITION=$(`whichdf`-h|awk'{print$5,$6}'|grep-w"/"|cut-d""-f1|awk-F"%"'{print$1}') #定义一周前的日期,用于日志分区空间超过设定的阈值后保留的份数(这里保留一周的日志) KEEP_DATE=`date-d'-7days'+%Y%m%d` #定义日志路径 LOG_DIR=/opt/log/kevin #定义日志备份路径(即当日志分区空间超

  • node.js的async和await

    一、async和await是什么ES2017标准引入了async函数,使得异步操作变得更加方便,async其实本质是Generator函数的语法糖async表示函数里有异步操作await表示在后面的表达式需要等待结果async函数返回的是一个Promise对象,可以使用then方法添加回调函数,一旦遇到await就会先返回。二、node异步编程演进的四个阶段我们来回顾一下异步编程的写法的演进过程第一阶段通过回调函数fs.readFile('/etc/passwd','utf-8',function(err,data){ if(err)throwerr; console.log(data); })复制这样的方式会造成嵌套过多,在调用过多的时候,就变成了下面这样的写法,传说中的callbackhellfs.readFile(fileA,'utf-8',function(err,data){ fs.readFile(fileB,'utf-8',function(err,data){ fs.readFile(fil

  • 【中国AI合伙人】助理来也胡一川、罗超专访(视频)

    新智元报道InfoQ二叉树视频联合新智元共同出品【新智元导读】如果把人工智能所有技术比作一个皇冠,自然语言理解就是皇冠上的明珠,是AI技术范畴内最具挑战性的一项技术。如何让机器更好地理解人类的自然语言,是全球AI研发者的共同挑战。助理来也的两位合伙人胡一川和罗超,就在试图摘取这颗明珠。稍有些常识的人都知道这样的生物学知识:惯用右手的人,左脑更加发达;而左撇子,则是右脑更加发达。左脑负责处理理性的信息,譬如数学;右脑负责处理感性的信号,譬如艺术。本集二叉树的两位主人公就像是一颗“人工智能大脑”里的左脑与右脑,是一家人工智能企业的左膀与右臂。他们一个是宾夕法尼亚大学的博士,一个是麻省理工的MBA;他们一个弃码从文,一个坚守研发阵地;一左一右,一文一武,一个负责技术,一个负责市场;两人都把人工智能未来发展的赌注下在了中国,对2018年人工智能发展的预测,他们的答案一致指向了:落地。视频内容《两界之间》技术专题纪录片InfoQ二叉树视频联合新智元共同出品左脑·胡一川:和落地场景的结合是AI价值最好的体现 胡一川,宾夕法尼亚大学博士,「来也」联合创始人兼CTO。在清华读研究生的时候,胡一川就已经

  • 编程学习中的瓶颈

    刚学习编程的时候,几乎每天都会有收获,昨天会用if了,今天会调用函数了,明天又会get到新的技能。这时候也许你会觉得自己正在快速成长势不可挡。然而过了一段时间,入门的知识都掌握得差不多了,突然就会陷入了一个停滞不前的阶段。这个时间因人而异,或早或晚,或长或短,但多半难以避免。通常我们称之为“瓶颈期”。如果你已经看完了我的几十篇Python系列教程,搞懂了里面说的各种知识点,却仍然无法自己写出一个完整的程序。那么恭喜你,你已来到编程学习的瓶颈。同样是瓶,颈的差距也不尽相同自“Crossin的编程教室”创建以来,在被经常问到的问题中,除了如何入门以及一些技术性问题外,有相当多的是诸如“我已经把基本语法都看会了,但还是不知道如何下手写程序”、“学完了教程,接下来该如何提高”之类的问题。因此,这几乎是继编程入门之后,将面临的最大难题。对于这个复杂的问题,我的回答却是很简单:多写代码。不要觉得我这是在敷衍你的问题。虽然你可能会想听到我告诉你,去看下某本书的第几章,然后去某某网站上做一下哪几个练习,再到某某论坛上找到某某大牛,给他发私信聊几句……然后就可以醍醐灌顶、茅塞顿开,打通任督二脉,掌握编程

  • [C#1] 10-事件

    事件概述CLR的事件模型建立在委托的机制之上。定义事件成员的类型允许类型(或者类型的实例)在某些特定事件发生时通知其他对象,事件为类型提供了一下三种能力:1允许对象登记该事件2允许对象注销该事件3允许定义事件的对象维持一个登记对象的集合,并在某些特定的事件反生时通知这些对象下面是根据一个上课的场景解释事件的原理【上课铃响,老师讲课,学生进教室听课】。定义一个RingManager类管理上课铃声,定义一个SchoolBell【上课铃响】的事件,Teacher和Student类型登记该事件。当Teacher和Student对象构造时登记SchoolBell事件,上课铃声响起时则通知这两个对象。发布事件1classRingManager 2{ 3/* 4*定义一个类型保存发送给事件登记者的附加信息, 5*按照.NET框架的约定,所有这样保存事件信息的类型 6*都继承自System.EventArgs,且以EventArgs作 7*为名字的结尾。 8*/ 9publicclassSchoolBellEventArgs:EventArgs 10{ 11//教室位置 12publicreadonl

  • 对象转JSON首字母大写[通俗易懂]

    大家好,又见面了,我是你们的朋友全栈君。最近在做一个第三方接口,接口给的数据类型如下请求报文如下{“A0144″:”12141256″,”AB6AM”:”中国银行支行”,”STATUS”:1}一般按照对象转JSON会使首字母小写,与接口文档不相符,因此需要转为大写,在字段名称前加@JSONField(name=“”)注释即可如下:packagecn.com.cis.acic.util.personHr.vo.request; importcom.alibaba.fastjson.annotation.JSONField; publicclassBankInfo{ privateStringA0144; privateStringAB6AM; privateintSTATUS; @JSONField(name="A0144") publicStringgetA0144(){ returnA0144; } @JSONField(name="AB6AM") publicStringgetAB6AM(){ returnAB6A

  • [LeetCode] 100. Same Tree

    Giventherootsoftwobinarytreespandq,writeafunctiontocheckiftheyarethesameornot. Twobinarytreesareconsideredthesameiftheyarestructurallyidentical,andthenodeshavethesamevalue. Example1: Input:p=[1,2,3],q=[1,2,3] Output:true 复制 Example2: Input:p=[1,2],q=[1,null,2] Output:false 复制 Example3: Input:p=[1,2,1],q=[1,1,2] Output:false 复制 Constraints: Thenumberofnodesinbothtreesisintherange[0,100]. -104<=Node.val<=1049 这道题其实就是判断每一个树的root的值,以及它的左右子树是否相同,考虑用递归来做。所以最后返回的是判断值是否相同,左子树是否相同(递归调用),右子树是否相同(递

  • 禁用IPv6临时地址方法

    1、管理员权限运行CMD 输入以下命令: netshinterfaceipv6setprivacystate=disable复制 2、重启网卡    

  • C#SQL服务导致错误

    本地连接 要添加本地IP 发布APP 要添加IP

  • (办公)记事本_Linux目录

           转载自菜鸟教程:https://www.runoob.com/linux/linux-system-contents.html         /bin: bin是Binary的缩写,这个目录存放着最经常使用的命令。 /boot: 这里存放的是启动Linux时使用的一些核心文件,包括一些连接文件以及镜像文件。 /dev: dev是Device(设备)的缩写,该目录下存放的是Linux的外部设备,在Linux中访问设备的方式和访问文件的方式是相同的。 /etc: 这个目录用来存放所有的系统管理所需要的配置文件和子目录。 /home: 用户的主目录,在Linux中,每个用户都有一个自己的目录,一般该目录名是以用户的账号命名的。 /lib: 这个目录里存放着系统最基本的动态连接共享库,其作用类似于Windows里的DLL文件。几乎所有的应用程序都需要用到这些共享库。 /lost+found: 这个目录一般情况下是空的,当系统非

  • centos7安装 swoole详细教程

    swoole的优点就不说了,安装对于没接触过的还是优点麻烦,写个教程看看能不能帮上需要的同学们! centos7的安装就不说了网上很多,一般都是虚拟机里面安装的,我是在mac虚拟机里面装的(我装的时候分区就没有按照教程分区,系统自动分区的(20G)因为按照教程分区有错误安装不了)温馨提示里面有桌面的程序看自己需不需要,自己选择!   安装好centos7后咱们就开始装swoole,二话不说先下载php,毕竟swoole是运行在php上面的插件,php是必须的,PHP当然是7以上,php.net官网下载又慢,所以现下载它! 可以命令可以桌面访问下: $cd/usr/local/src/ $wgethttp://cn2.php.net/distributions/php-7.4.1.tar.bz2 $tarjxvfphp-7.4.1.tar.bz2复制 cd是打开目录 这个就不用说了(tab键都不知道是怎么用的绝对小白) 上面的命令如果没有权限记得在前面加上  『sudo命令』空格别忘记了,这个是已管理员权限运行后面命令肯定回输入你的密

  • “浅入浅出”函数防抖(debounce)与节流(throttle)

    函数防抖与节流是日常开发中经常用到的技巧,也是前端面试中的常客,但是发现自己工作一年多了,要么直接复用已有的代码或工具,要么抄袭《JS高级程序设计》书中所述“函数节流”,(实际上红宝书上的实现类似是函数防抖而不是函数节流),还没有认真的总结和亲自实现这两个方法,实在是一件蛮丢脸的事。网上关于这方面的资料简直就像是中国知网上的“水论文”,又多又杂,难觅精品,当然,本文也是一篇很水的文章,只当是个人理解顺便备忘,毕竟年纪大了,记忆力下降严重。CSS-Tricks上这篇文章DebouncingandThrottlingExplainedThroughExamples算是非常通识的博文,值得一读。 函数防抖与节流的区别及应用场合 关于函数常规、防抖、节流三种执行方式的区别可以通过下面的例子直观的看出来 函数防抖和节流都能控制一段时间内函数执行的次数,简单的说,它们之间的区别及应用: 函数防抖:将本来短时间内爆发的一组事件组合成单个事件来触发。等电梯就是一个非常形象的比喻,电梯不会立即上行,而是等待一段时间内没有人再上电梯了才上行,换句话说此时函数执行时一阵一阵的,如果一直有人上电梯

  • 架构即未来阅读笔记1

    架构即未来阅读笔记1 斗众如斗寡,形名是也 出自孙子兵法里的,凡治众如治寡,分数是也。斗众如斗寡,形名是也,孙权如是说。 管很多人跟管很少的人一样,是因为有“分数”,就是编制。“分”,就是分成班、连、团、师、军之类,看你怎么分。“数”,就是每个编制单位多少人。编制搞好了,组织架构搞好了,管很多人就跟管很少的人一样,和运用自己的手臂一样方便。 整个现代管理学,就是从军队管理发展起来的。这“分数”两个字,怎么分,多少数,那学问大了去了。你看好多公司,成天都在研究组织架构,老也研究不明白。而且业务发展变化越快,对组织架构的变革越多。 参考:长篇《孙子兵法》解读——凡治众如治寡,分数是也。斗众如斗寡,形名是也 文中反复强调人是最为重要的因素,没有人永远没有扩展性问题,没有人永远没有机会去研发一款需要扩展性的产品,好像非常有道理,又似乎废话一样,可能动物也是这么认为自己的。人重要! 所有扩展性最成功的产品,最核心的部分是有一群人,他们最初了很多正确的决策,当然偶尔也出现昏招。 要想成功,必须有合适的人,合适的行为,合适的工作,合适的时间。 合适的人是此人具有合适的知识,技术和能力。俗话说一将顶三

  • DLL调试方法

    1、已经做好的dll不能设置;你可以用AfxMessageBox把信息打印出来。2、哪个地方调用的函数把DLL重新编译一次在把DLL放到工程里从新添加一下然后在你工程调用DLL内容的地方设置断点然后按F11就进去了。3、在你要调试的dll函数哪里加上硬断点:__asm{int3}然后把dll编译成debug版本,直接用exe运行就行程序走到这个地方,会出现一个对话框,提示错误,让你选调试、终止什么的,点调试,就会进入硬断点那里了。4,在dll工程中利用exe调试dll用debug编译,工程属性->配置属性->调试->命令,加入exe文件的位置如果exe还需要一些其他的配置文件,则工程属性->配置属性->调试->工作目录,加入所需文件位置复制 复制

  • 约瑟夫循环

    数据结构的第一个实验拖了好久才完成,总算是能够理解其中指针是咋用的了,但估计要我再单独自己写还是够呛。 但是至少学会了如何构建循环链表,理解了结构体中可以包含指向本结构体类型的指针成员,以及动态存储空间的分配。 1.实验题目:线性表应用 约瑟夫(Joeph)问题的一种描述是:编号为1,2,…,n的n个人按顺时针方向围坐一圈,  每人持有一个密码(正整数)。一开始任选一个正整数作为报数上限值m,从第一个人开始 按顺时针方向自1开始顺序报数,报到m时停止报数。报m的人出列,将他的密码作为新的m值,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直至所有人全部出列为止。试设计一个程序求出出列顺序。 2.需求分析 本程序用C语言编写,以循环链表完成功能,要完成此程序要熟练掌握创建循环链表、在结构体中定义一个指针类型的成员变量、在链表中插入节点、删除节点、及对约瑟夫循环如何进行有充分的理解,通过一系列工作输出出列顺序。 ①    输入的形式和输入值的范围:插入元素是需要输入进行游戏的人数及每个人的密码,还要输入初始密码值。所有输入元素都要

  • 做bad apple第二步: python如何将视频变成一帧帧的图片,如何将一帧帧的图片转为视频

    直接上代码 """视频转图片"""复制 portcv2defgetphoto(video_in,video_save):cap=cv2.VideoCapture(video_in)#打开视频文件n_frames=int(cap.get(cv2.CAP_PROP_FRAME_COUNT))#视频的帧数fps=cap.get(cv2.CAP_PROP_FPS)#视频的帧率dur=n_frames/fps#视频的时间num_frame=0judge=cap.isOpened()whilejudge:flag,frame=cap.read()#flag是读取状态,frame下一帧cv2.imshow('video',frame)#captureifcv2.waitKey(0)==27:breakifflag:num_frame+=1#print("正在保存第%d张照片"%num_frame)#cv2.imwrite('a\\'+str(num_frame)+'.jpg',frame)#cv2.imwrite(‘路径’+‘名字’+‘后缀’,要存的帧)#cv2.waitKey(1)##new_p

相关推荐

推荐阅读