精华内容
下载资源
问答
  • 谈谈文字图片粒子

    2021-01-13 07:33:10
    之前写了谈谈文字图片像素,主要是为了将文字和图片像素后的坐标提取出来,而本篇所讲即为像素后的粒子过程。先上一个简单的demo ->粒子demo,本篇的目的就是为了讲解怎样做这样的简单demo(大牛请无视)...

    之前写了谈谈文字图片像素化,主要是为了将文字和图片像素化后的坐标提取出来,而本篇所讲即为像素化后的粒子化过程。

    先上一个简单的demo -> 粒子化demo,本篇的目的就是为了讲解怎样做这样的简单demo(大牛请无视);同时会介绍一些优秀的demo供大家参考。

    主要思路

    首先我们谈谈粒子化的主要思路。

    像素化后(不知道怎样像素化,参考谈谈文字图片像素化),我们得到了所需图像或者文字的具体坐标,我们将它们形象地用一个个的粒子表示(这里用了圆形),得到的坐标即是粒子的最终位置。粒子的初始位置在哪里?粒子从初始位置到最终位置的运动又是如何?这些是我们可以自由发挥的。所以粒子化过程究其根本,就是怎样表示粒子的运动过程。

    基本准备

    canvas自适应电脑屏幕(不出现滚条):

    css部分:

    body {margin:0; padding:0; wdith:100%; height: 100%}

    canvas {display:block; background-color:#000}

    js部分:

    window.canvas = document.createElement('canvas');

    document.body.appendChild(window.canvas);

    canvas.height = window.height = window.innerHeight;

    canvas.width = window.width = window.innerWidth;

    关于像素化再补充几句:

    像素化过程简单描述就是将所需的文字fillText到画布上或者将图片drawImage到画布上(设置一个离屏的canvas),然后利用getImagedata这个api将像素点提取出来。值得注意的是getImagedata并不会获取背景点的像素,所以canvas的背景使用怎样的颜色并不会影响像素点的提取。

    一般像素点的提取是根据某个像素点rgba的a值进行判断,a值的取值是0~255(rgba当做颜色属性赋值时a的取值是0~1),通常做法是判断a值非0或者半透(>125)。这里我也做了个简单的测试,当color值取black或者#000时,a值如下:

               

    关于阴影

    粒子化效果一般在黑夜最漂亮,所以背景我一般设置成黑色。如果要更带感,需要一点阴影效果。

    其实很简单,调整一下透明度即可。比如这样:

    body {margin:0; padding:0; wdith:100%; height: 100%}

    canvas {display:block; background-color:#000}

    // window.onload = function() {

    window.canvas = document.createElement('canvas');

    document.body.appendChild(window.canvas);

    canvas.height = window.height = window.innerHeight;

    canvas.width = window.width = window.innerWidth;

    window.ctx = canvas.getContext('2d');

    // }

    var x = 0, y = 0;

    setInterval(function() {

    ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';

    ctx.fillRect(0, 0, width,height);

    x+=5; y+=5;

    ctx.beginPath();

    ctx.fillStyle = 'red'

    ctx.arc(x, y, 5, 0, Math.PI * 2);

    ctx.fill();

    }, 1000 / 60)

    当然用globalAlpha也是一样的。

    粒子具有的属性:

    将粒子放入运动场中,我们可以赋予它一些属性,比如速度,加速度,位移等等,而这些属性都可以用矢量来表示。

    粒子运动

    之前我说过,以上皆是铺垫,运动才是粒子化的关键。因为粒子最终要汇聚成一定的形状,所以运动的某一段的终点已知,那么粒子怎样能运动到对应的位置?

    我们从最简单的直线运动说起。

    直线运动:

    谈谈文字图片像素化中用的demo就是直线运动 -> 像素化

    如何用js描述一个直线运动?首先我们要明白在怎样的情况下物体会做直线运动?学过物理我们知道速度和加速度在一条直线时,物体做直线运动。

    那么很简单,已知粒子的终点,随机粒子的起点,速度方向就确定了(起点指向终点),如果有必要,还可以设置加速度。

    曲线运动:

    仅仅做直线运动或许视觉效果太差,曲线运动会不会产生更好的视觉效果?

    什么情况下物体会做曲线运动?速度和加速度不在一条直线时,比如抛物运动。

    先看这个demo:demoHome(W·Axes) 我们取它的第一个过程进行分析,实际上就是一个二维的曲线运动。和抛物运动的不同之处是,加速度方向指向粒子的终点,为的是能够准确达到终点位置。W.Axes的另一个粒子demo也大同小异:粒子化,相比于前一个demo只是改变了粒子的初始位置和初始速度,以及粒子运动顺序。

    我们尝试着来完成一个demo。

    前面说到,为了能使粒子能到达指定的位置,我们给了粒子一个指向指定位置的加速度,在粒子的每一帧运动中,可以将该加速度分解到x和y轴分别进行计算。比如这样:

    update: function() {

    var v = this.pos2.minusNew(this.now);

    var angle = v.getAngle();

    this.v.x = (this.v.x + this.a * Math.cos(angle) / 1000 * 60) * 0.96;

    this.v.y = (this.v.y + this.a * Math.sin(angle) / 1000 * 60) * 0.96;

    this.now.x += this.v.x;

    this.now.y += this.v.y;

    }

    a表示粒子的加速度值,/1000*60表示每帧的速度增加,*0.96模拟能量损失。这样的运动的话粒子通过曲线运动就能到达指定位置。

    模拟屏幕左上角到屏幕中心的曲线运动:

    粒子运动

    body {margin:0; padding:0; wdith:100%; height: 100%}

    canvas {display:block; background-color:#000}

    window.canvas = document.createElement('canvas');

    document.body.appendChild(window.canvas);

    canvas.height = window.height = window.innerHeight;

    canvas.width = window.width = window.innerWidth;

    window.ctx = canvas.getContext('2d');

    window.vpx = width / 2;

    window.vpy = height / 2;

    var ball = {

    pos2: new Vector2(width/2, height/2),

    now: new Vector2(0, 0),

    r: 5,

    color: getRandomColor(),

    v: new Vector2(10, 0),

    a: 10,

    draw: function() {

    ctx.clearRect(0, 0, width, height);

    ctx.beginPath();

    ctx.fillStyle = this.color;

    ctx.arc(this.now.x, this.now.y, this.r, 0, Math.PI * 2);

    ctx.fill();

    },

    update: function() {

    var v = this.pos2.minusNew(this.now);

    var angle = v.getAngle();

    this.v.x = (this.v.x + this.a * Math.cos(angle) / 1000 * 60) * 0.96;

    this.v.y = (this.v.y + this.a * Math.sin(angle) / 1000 * 60) * 0.96;

    this.now.x += this.v.x;

    this.now.y += this.v.y;

    }

    };

    setInterval(function() {

    ball.draw();

    ball.update();

    }, 1000 / 60);

    当然你也可以自己设置运动函数,不过我觉得这样比较方便而已。

    关于缓动:

    有时为了效果我们需要设计一个缓动,什么是缓动?就是速度越来越小的运动。

    或许涉及到x和y轴同时变化的运动太过复杂,我们假设一个物体沿着x轴正方向运动,并且物体运动速度越来越小,即为缓动。

    最简单的缓动如下:

    update: function() {

    var v = this.pos2.minusNew(this.now);

    v.scale(0.05);

    this.now.add(v);

    }

    稍微复杂点的缓动v-s图像可以设计成二次函数或者三角函数,因为某点的切线的斜率即为速度,而切线斜率越来越小即为速度越来越小。

    二维三维:

    三维和二维环境下的粒子化其实大同小异,不了解三维的话可以参考rotate 3d基础试着实现一个球体的3d旋转demo。

    关于创意

    其实粒子化本身并不难,难的是创意。这里稍微介绍几个有创意的demo。

    codepen上的一个demo,代码很长,其实实现思路也大同小异,就是变着花样的粒子的运动。

    同样只是粒子的运动,只是多了点创意,构造了动态文字效果。

    总结

    文字图片粒子化其实就是粒子系统的一部分,不妨可以从最简单的一个粒子系统开始,一步步实现一个简单的粒子化demo。

    觉得不尽兴,可以参考下面更多文章:

    展开全文
  • 实际初始代码是初始寄存器配置。 时钟配置,是初始时钟相关的寄存器配置 定时器配置,是初始定时器相关的寄存器配置 中断配置,是初始中断相关的寄存器配置 这里引申出一个重要概念,寄存器。 展示的...

     

    框架与要点

    编写单片机类的程序,最核心、最重要的是上面的框架。

    • 硬件支持:单片机
    • 软件框架
      • 初始化代码
        • 时钟配置
        • 定时器配置
        • 中断配置
      • 功能代码

    通过此框架我们会明白,初始化代码是重中之重。实际初始化代码是初始化寄存器配置。

    • 时钟配置,是初始化时钟相关的寄存器配置
    • 定时器配置,是初始化定时器相关的寄存器配置
    • 中断配置,是初始化中断相关的寄存器配置

    这里引申出一个重要概念,寄存器

    展示的时间到了,实际上寄存器就是一排开关,一排管道,一排过滤器。

    如果寄存器的一位(一个开关)是红色的,代表这个这个位管理这个通道(每个色块)关闭,如果是绿色的代表这个通道打开,当寄存器1~5的一位都变成绿色,蓝色的电子才能通过,进入后续的电路通道。

    编写单片机程序,实际大部分工作就是在配置这些通道。

    • 什么情况下打开通道,什么情况下关闭通道
    • 什么时间打开通道,什么时间关闭通道
    • 同时打开几条通道,关闭几条通道
    • 检查这个通道的状态,是关闭了还是开着

    对以上措辞转换个说法:

    • 什么情况下打开寄存器的某位(置位),什么情况下关闭寄存器的某位(清零)
    • 什么时间打开寄存器某位(置位),什么时间关闭寄存器某位(清零)
    • 同时打开寄存器的多个位(置位),关闭寄存器多个位(清零)
    • 检查寄存器某位的状态(读取)

    寄存器操作是优先于其他功能代码实现的,因为这些管道不打开,其他功能实现不了,例如我要控制某个单片机引脚输出电流/电压(变成高电平)首先要配置这个引脚的控制寄存器能够输出(实际也可以控制器关闭或者输入),部分单片机还可以控制其输出电流的大小,然后才能写代码控制引脚变为高电平。(部分51单片机内部做了这个动作,不用控制这个寄存器,可以直接控制读取引脚输出、输入)

    //注意以下代码不是stc系列51的控制,是另一款51的配置方式,仅做参考 
    
    //P4OE就是P4的控制寄存器 
    //P4的第一个引脚设为输出,其他全部为输入引脚
    P4OE |= 0B11111101; 
    
    P4_1=1;  //P4_1 引脚输出高电平

    单片机编程大约70~80%的工作是在控制寄存器操作(配置、读取)理解了寄存器的配置原理,懂点基础C语言,剩下的编程是水到渠成的工作。

    寄存器和数字电路知识再参考下面的第一个链接:

    你管这破玩意叫 CPU ? - 码农的荒岛求生的文章 - 知乎 码农的荒岛求生:你管这破玩意叫 CPU ?
    你管这破玩意叫编程语言? - 码农的荒岛求生的文章 - 知乎 码农的荒岛求生:你管这破玩意叫编程语言?

    因为开设单片机训练营用的单片机是STC89C52RC(硬件支持),所以用它为例将寄存器初始化配置。这样保持和课程一致,实际其他51类的单片机是类似的。选择此款单片机另一个初衷是它的程序完全可以无须改动或者需要极少的改动就可以在proteus运行,这样不用开发板我们也可以先开始学习单片机编程。

    中文版规格书下载链接:https://www.stcmcudata.com/datasheet/STC89C52.pdf

    一、时钟配置(晶振选择)

    目标是让外部晶振或者内部晶振正常工作。

    个人喜欢用人来类比单片机。

    第一个解决的问题就是时钟配置。

    时钟就是脉搏、心跳,晶振就是心脏,心脏每秒跳动的次数就是频率。

    单片机分为内部晶振和外部晶振,也就是说它可以有两个心脏,你可以选择只用一个,并且是心脏跳动的频率你可以选择,这个工作就是时钟配置。STC89C52 只支持外部晶振。

    先看单片机实物:

    再看它的外部晶振(外部心脏)

    然后将他们组合成电路:

    这个时候我们再来看电路图

    这款产品没什么时钟寄存器配置,直接连上晶振就能用。另外注意,部分单片机时钟配置不是在程序中完成的,而是在编写程序前,在编辑器、下载软件中配置的。

    二、定时器配置

    目标正确配置定时器,能够让定时器间隔一定时间工作,
    实现1ms定时。

    STC89C52 三个定时器,定时器0、定时器1、定时器2。

    学会一个定时器,其他定时器的配置类似,触类旁通,使用定时器0为例学习。

    • 定时器位于单片机内部
    • 定时器需要配置定时寄存器后才能使用
      • 初始化定时寄存器
      • 定时器中断程序处理
      • 定时器功能程序代码编写

    初始化定时器的工作先后顺序?

    1. 选择那个定时器启用(0、1、2)
    2. 设定定时器的计数模式
    3. 配置定时器计数值
    4. 启动定时器
    5. 启用定时器中断
    6. 总中断开启

    中文版规格书下载链接:https://www.stcmcudata.com/datasheet/STC89C52.pdf (以下图片见189页)

    总共TCON、TMOD、TL0、TL1、TH0、TH1, 6个寄存器参与了配置定时器0。

    中文版规格书下载链接:https://www.stcmcudata.com/datasheet/STC89C52.pdf (以下图片见190页)

    很多时候,我们写规格书的工程师,都是理科人才,理科人才俗成“一根筋”(非贬义,不是这种特质,太灵活而不严谨也做不好技术),他是以自己的智力来衡量大部分人的智力,也就是跳跃式思维写东西,我们看上面简单的寄存器TCON表格,实际上就是不友好的表示方式,SFR name TCON 实际上上面已经说明了这是寄存器TCON 表格里又出现它,增加脑力损耗,应删掉,Address 88H 地址是寄存器集中在一块,每个寄存器都会分配个地址,但是对我们编程来说,基本上不用管它,所以这个也可以不列在这里。然后bit name 又写的太省了,bit应该是位名称(或者称位序号), name应该是功能名称,所以我给他改改。

    所以我们看规格书的过程,是从庞杂的信息中提取有效的信息,辨别能力不是一开始就有的,而是我们不断在实践中建立的,所以初学者看规格书会看到头大,就是规格书不是入门书,而是工具书,需要你具备很多专业和边缘知识才能更好的理解,更容易获取到自己有用的信息。

    向导推荐逆向学习法,先了解框架,然后先实现功能,倒查为什么这样能够实现。

    最上面是写了单片机编程的核心框架,然后我们找到一个完整的程序,先看这个例子,它的实现,然后倒推。

    1. 下载STC-ISP stc系列单片机的烧写程序 http://www.stcmcudata.com/STCISP/stc-isp-15xx-v6.88F.zip
    2. 它内部有示例代码,我们直接看
    3. 分析其示例代码,倒推寄存器设置的要求

    //为了利于说明,我把大部分注释代码删除
    #include "reg51.h"
    
    typedef unsigned char BYTE;
    typedef unsigned int WORD;
    
    #define FOSC 11059200L
    
    #define T1MS (65536-FOSC/12/1000)   //1ms timer calculation method in 12T mode
    
    sbit TEST_LED = P1^0;               //work LED, flash once per second
    
    WORD count;                         //1000 times counter
    
    void tm0_isr() interrupt 1
    {
        TL0 = T1MS;                     //reload timer0 low byte
        TH0 = T1MS >> 8;                //reload timer0 high byte
        if (count-- == 0)               //1ms * 1000 -> 1s
        {
            count = 1000;               //reset counter
            TEST_LED = ! TEST_LED;      //work LED flash
        }
    }
    
    void main()
    {
        TMOD = 0x01;                    //set timer0 as mode1 (16-bit)
        TL0 = T1MS;                     //initial timer0 low byte
        TH0 = T1MS >> 8;                //initial timer0 high byte
        TR0 = 1;                        //timer0 start running(定时器0开始运行)
        ET0 = 1;                        //enable timer0 interrupt
        EA = 1;                         //open global interrupt switch
        count = 0;                      //initial counter
    
        while (1);                      //loop
    }
    
    

    看上面代码整个TCON 寄存器中,8个位,只用到了一个,TR0=1(启动定时器0,开始运行),其他未用,也就是说我们实现目标1ms定时,很多其他通道(寄存器位)不用去管它,它默认是开是关大部分时候不影响我们的程序运行。

    下面逐一反推,定时器寄存器的配置方式

    void main()
    {
    TMOD = 0x01; //set timer0 as mode1 (16-bit)
    TL0 = T1MS; //initial timer0 low byte
    TH0 = T1MS >> 8; //initial timer0 high byte
    TR0 = 1; //timer0 start running(定时器0开始运行)
    ET0 = 1; //enable timer0 interrupt
    EA = 1; //open global interrupt switch
    count = 0; //initial counter
    
    while (1); //loop
    }

    以上是程序的主体(主程序),是单片机的灵魂,也是一个程序从出生活动到死亡的过程。

        TMOD = 0x01;                    //set timer0 as mode1 (16-bit)
        TL0 = T1MS;                     //initial timer0 low byte
        TH0 = T1MS >> 8;                //initial timer0 high byte
        TR0 = 1;                        //timer0 start running(定时器0开始运行)
        ET0 = 1;                        //enable timer0 interrupt
        EA = 1;                         //open global interrupt switch

    以上6句代码就是做了我们开始说的定时器初始化工作

    1. 选择那个定时器启用(0、1、2)
    2. 设定定时器的计数模式
    3. 配置定时器计数值
    4. 启动定时器
    5. 启用定时器中断
    6. 总中断开启

    这一部分全部是寄存器的配置,有些单片机寄存器只能整个的控制,也就是处理一个8位的寄存器,要同时处理8个位,这款51单片机可以处理单个位,上面说的TR0我们看看在哪里。

    TR0 在TCON寄存器的第四位,8位寄存器,实际就是8个通道,8个存储区域。每个存储区域只能存放0和1,0代表通道打开,1代表通道关闭。

    看这里,寄存器1是怎么配置的,0000 0010 也就是关关关关 关关开关。

    TR0=1 代表只是TCON第四位开,其他开关不管它。

    规格书:https://www.stcmcudata.com/datasheet/STC89C52.pdf (以下图片见190页)

    类似的,现在我们把TMOD、TL0、TH0、ET0、EA都从规格书中找出来,看看它位于那个寄存器或者它是哪个寄存器。

    规格书:https://www.stcmcudata.com/datasheet/STC89C52.pdf (以下图片见191页)

    注意这里设置TMOD和设置TR0的区别,TMOD是整个TMOD寄存器,TMOD=0x01(十六进制表示)=0b00000001 (二进制表示)这个8位寄存器所有的位通道都配置了,只是填充0还是填充1。TR0=1 只是TCON寄存器第四位填充1,打开一个通道,其他7个通道不管它。

    TMOD = 0x01;                    //设置定时器0 作为16位模式 //set timer0 as mode1 (16-bit)

    能设置16位模式,意味着也能设置8位模式、13位模式,这个位数越大,可以计数定时的范围越宽,意味着定时时间可以更长。

    TL0 = T1MS;                     //initial timer0 low byte
    TH0 = T1MS >> 8;                //initial timer0 high byte

    上面两句作为一组来看,主要原因是TL0 TH0两个寄存器一般是同时使用的。

    TH0 TL0 是定时器的两个计数容器,也就是计数寄存器,计算机、单片机一般以一个字节为单位,一个定时器计数寄存器实际上就是一个字节(8位)的容器,TH0 TL0 两个合起来就是两个字节(16位)的计数容器。

    TH0 TL0 都是粮仓,粮食就是填充值,到了一定的数量,触发开关,证明计数完成。计数实际上和定时不分家的,假设我们的输送带匀速往里面送粮食,1秒送一颗粮食,计数到10颗就是10秒,所以定时器又叫做定时计数器。这样定时器可以同时完成两个工作。

    实际上简单点我们从1数到10,大家尝试下就明白了,是需要时间的。

    假设我们不是从空粮仓开始计数,而是里面已经灌满了半仓,那么我们填满粮仓的时间就减少一半。所以设定TL0 TH0的数值,就可以控制定时的时间。

    这里有同学就奇怪了,TL0 =10,TH0=100;我理解,TL0=TIMS,TH0=TIMS>>8什么鬼?

    #define FOSC 11059200L
    #define T1MS (65536-FOSC/12/1000)   

    #define 在C语言中我们经常翻译过来是“宏定义”,实际上这样的翻译可能造成误解,实际是替代、指代、起小名、起外号的意思。

    张三的外号是二狗子,实际指代的是一个人。

    11059200L 是张三,现在给这一串数字另起个名字二狗子 FOSC

    FOSC 完全等于 11059200L,是一个东西。

    同样的,T1MS和(65536-FOSC/12/1000)是一个东西,你愿意写前面好写的短名还是后面这一长串计算公式?是人都知道,简单好记最好。T1MS 仅仅就是起个外号,来代表后面的一串公式而已。

    这样我们就知道,

    1. TL0 = TIMS 进行转换
    2. TL0 =(65536-FOSC/12/1000)
    3. TL0 = (65536-11059200L/12/1000)
    4. TL0 = 65536-921.6 = 64,614.4 ≈ 64614

    / 是C语言中除法的意思 11059200L 是个十进制长整型数,加上L是代表C语言中的一类数字,实际我们平常都是说的十进制数字 1 2 3...100.. 但是计算机要处理各种各样类型的数字,并且这些数字的长度、精度、进制类型都不用,所以用各种符号代替不同的数。

    • 0x01 代表十六进制的1
    • 0b00000001 代表二进制的1
    • 1 默认代表十进制的1

    以上是进制表示

    • 整型 1、-1、25
    • 长整型 11059200L
    • 浮点数 11.15 、22.44446 (其实就是带小数的数字)
    • 布尔型 true/false 0/1
    C语言中,整型常量和长整型常量,有什么区别?
    C 数据类型 | 菜鸟教程

    为什么计算机要分这么多的类型?

    计算机、单片机实际上到现在还是没有那么智能,抽象能力还是比较差劲,如果你不写明确,它不认识这各种数字,不像人脑可以一眼分别。

    继续:

    • TL0 = 64614
    • 实际是不是64614 就能存到TL0中了呢?
    • 前面说过TL0 是一个字节的8位寄存器,8位最大能存的数是2的8次方-1=256-1=255
    • 这个时候计算机(实际上单片机的编译程序)在内部自动进行了一个转换,把存不了的数给舍弃了,怎么转换的?
    1. 64614 是十进制数 为了好理解,我们把它转换为16进制数和二进制数(windows自带计算机就可以,到程序员模式)
    2. 十六进制 FC66 二进制 1111 1100 0110 0110
    3. TL0 只能存二进制的8位数据(16进制的两位数据,16进制一位可以直接对应4位二进制)
    4. 实际上TL0 能存进去的就是二进制的0110 0110 也就是十六进制的66
    5. 那么能存进去的十进制数是什么?102
    6. 所以 TL0 = 64614 但是存到单片机中自动变成了 TL0 = 102

    但是这不符合我们的设计要求,1ms定时,必须要存进64614个计数值,才能准确定时。

    我只存了个102进去算怎么回事。这个时候就是需要16位定时器模式的原因。

    就需要我们用两个寄存器 TH0 TL0的组合,现在我们不是把低8位存到TL0了,我们再把64614的高八位存到TH0就好了。怎么存?

    TH0 = T1MS >> 8; 

    因为TH0实际也是8位寄存器,所以用了>>这个符号,把T1MS右移8位,然后存进去,当我买说到右移,这又是二进制的运算。

    • 我们知道T1MS实际等于64614
    • 64614 = 0xFC66(十六进制写法)=0B1111110001100110(二进制写法)
    • 0xFC66 右移8位 实际上是十六进制右移2位 将FC移到66的位置 = 0x00FC
    • 0B1111110001100110 二级制右移8位 就是从第9~16个数字(0/1)每个都向右移动8位,前面补0.
    • 变成0B0000000011111100
    • 因为TH0 也只能存8位(低8位) 所以TH0=0B11111100=0xFC=252(十进制写法)

    重点来了,当我们一旦说到“位”、“左移”、“右移”这个名词的时候,在单片机编程中,大部分时候我们说的是二级制数字,不要把它想成十进制了,这里就涉及到进制转换问题。

    进制一个关键概念就是用多少个字符来表示全部的数字,我们十进制是0~9,二进制只能用0和1来表示全体的数,八进制,只能用0~7来表示所有的数字,十六进制是0~9 再加上A、B、C、D、E、F来表示全部的数字。只能用有限的字母符号排列出来。以8进制为例子,117可以是个8进制数字,但是118不可能,因为8进制里面根本没有8这个符号,这样只能进位,变成了120 注意这个120不是十进制的120,是8进制的,两个进制的数写法相同但是大小不等,所以也就有了前面的说法,不同进制、不同类型的数计算机是不认识的。我描述了这一段人大体能够看明白,计算机、单片机它不明白。具体关于进制概念和换算参考下面链接:

    二进制数系统
    1.详解二进制,十进制,十六进制—聊聊计算机,靠谱学院,星月_哔哩哔哩_bilibili

    认真看以上文字的话,到这里我们应该能够理解这三句C语言代码

        TMOD = 0x01;                    //set timer0 as mode1 (16-bit)
        TL0 = T1MS;                     //initial timer0 low byte
        TH0 = T1MS >> 8;                //initial timer0 high byte
    • 设置定时器0 启用,并且设置为16位模式
    • 因为设置了16位模式,所以我们必须要用两个8位寄存器才能把计数值存完整
    • 所以我们用到了定时器0专用的两个计数寄存器 TH0 TL0
    • 把16位的计数数据低8位直接存到TL0中(T是定时器Timer首字母 L low 低的首字母 TL0就是低8位定时器0的寄存器 TH0同理)
    • 把16位的技术数据高8位先右移8位放到低字节位置,然后再装入TH0
    • 完成计数的初始值填入

    以上三句话配置好了定时器0(1ms定时)

        TR0 = 1;                        //timer0 start running(定时器0开始运行)
        ET0 = 1;                        //enable timer0 interrupt
        EA = 1;                         //open global interrupt switch

    TR0=1; //启动定时器0  开始计数+1
    ET0 =1 //允许定时器0 中断

    1ms定时,T1MS代表的公式算出来就是64614 当这个数不断+1 ,加到65536时,专业术语叫做溢出,实际上是超出了TH0 TL0能存放的最大数,两个加起来是16位,16位最大能存放的数是2的16次方-1 )这个时候证明时间到了正好是1ms(每次+1 有单片机的晶振时钟脉冲决定,一个脉冲或者几个脉冲完成才能+1,一个脉冲的时间也确定了,也就确定这个计数值+1用的时间。

    所以64614叫做计数初始值,不断+1,加了922个1后变成了65536,溢出,定时时间到,然后就会触发一个开关,这个开关叫做定时器中断(设置了ET0 =1)中断开关开启后,实际上就是允许一段独立中断代码,我们可以在这段代码中做自己想做的一部分工作了。

    单片机中有很多中断,例如定时器0 、定时器1中断、 串口中断、看门狗中断... 所以设置了一个总中断开关 EA=1 代表的意思是所有中断都允许,符合一定条件就会到达专门的中断程序处理一段代码。

    本例程序从外部看同时运行两段程序(并行执行,实际是时间片运行模式,暂时不要考虑这个细节)

    • 主程序,只有一个
    • 中断程序,可以是多个

    关于中断,参考链接,后续会继续深入讲解

    向导:M20 中断、定时器与程序存储方式

    count=0; 

    count=0 是初始化中唯一不是配置寄存器的代码。它是什么,专业术语讲是初始化变量。

    初始化这个词有意思,看过电影《黑衣人》《明日之战》... 有个仪器洗脑的

    清除记忆,从头再来,实际上电影中的设定作为“初始化”的定义就比较准确,清除记忆实际上也是分两种的,一个是完全清除,一张白纸,另一种是恢复原先的记忆,清除最新的记忆。

    所以初始化

    • 清零 = 0
    • 设置中需要的原始数据 例如寄存器的填充计数值 T1MS

    初始化在程序中是非常重要的步骤,为了保证程序能够按照设定的规则运行,一般程序运行起始就是初始化,我们打开电脑一个软件,每次都出现同样的一个弹窗画面,实际就是在运行初始化代码

    while(1);

    本例主程序代码很简单,出现了while(1); 不要小看这一句,实际上程序大部分工作都是它完成的,只是我们这个例子没有做太多的工作,所以写的简单,实际上就是这一句,完整的写法是

    while(1)
    {
    ;
    }

    这个是主程序,但是主程序没有做什么工作,我们这个例子是中断程序做了定时器的工作,主程序在空运行,就是我们开车停在哪里,发动机空转,只要不熄火就一直空转,但是不走。

    本节课篇幅已经够了,开始转入下节课

    M50 让人迷糊的“函数"

    推荐阅读:

    向导:总目录:最好的电子、计算机从入门到工程师教程

    为了对初学者友好,没有更好的办法,我们能做的就是认真点再认真点,细致点再细致点,通俗些更通俗些...

    展开全文
  • 有很多非 IT 行业的朋友都在学 Python,他们的目的也很简单,就是想提高下工作效率,简单重复的工作希望用 Python 搞定。因此我准备写一些 Python 自动办公系列相关文章,...

    有很多非 IT 行业的朋友都在学 Python,他们的目的也很简单,就是想提高下工作效率,简单重复的工作希望用 Python 搞定。

    因此我准备写一些 Python 自动化办公系列相关文章,代码都是可以在 Python3 上正确运行的,复制下代码,再调整下细节,就可以使用。

    首先发在公众号上,然后同步到知识星球。

    为什么同步到知识星球,因为公众号的文章修改起来非常麻烦,而知识星球就比较简单了,这样文章里的代码可以不停迭代更新,重要的是有问题还可以一对一进行提问。加入星球的方式后续会公布。

    在日常的工作生活中,我们会经常与图片打交道,比如下载图片,压缩图片,删除图片的元数据防止隐私泄漏,拼接长图,图片文字识别,加水印等等。

    今天就来分享下如何简单的使用 Python 来玩转这些操作。

    1、下载图片

    下载图片是最简单的操作了,无非就是先找到图片的 url,使用标准库或者 requests 库去请求这个 url,然后将得到的数据保存为文件即可。

    下面分享三种方法来下载图片。

    方法一,使用标准库。

    from urllib.request import urlretrieve
    from pathlib import Path
    import ssl
    def urllib_download(img_url, download_path):
        ssl._create_default_https_context = ssl._create_unverified_context
        urlretrieve(img_url, Path(download_path) / 'image1.png') 
    
    

    方法二,使用 requests。

    
    import requests
    def request_download(img_url, download_path):
        r = requests.get(img_url)
        with open(f'{download_path}/image2.png', 'wb') as f:
            f.write(r.content)
    

    方法二,使用 requests 的流式下载,适用于较大,网速慢,容易下载失败的图片。

    import requests
    def requests_chunk_download(img_url, download_path):
        r = requests.get(img_url, stream=True)    
        with open(f'{download_path}/image3.png', 'wb') as f:
            for chunk in r.iter_content(chunk_size=32):
                f.write(chunk)
    

    分别下载一个图片看看结果:

    if __name__ == '__main__':
        img_url = 'https://tinypng.com/images/panda-developing-2x.png'
        download_path = Path('/Users/aaron/tmp/images')
        download_path.mkdir(exist_ok=True)
        urllib_download(img_url,download_path.as_posix())
        request_download(img_url, download_path.as_posix())
        requests_chunk_download(img_url, download_path.as_posix())
    

    三种方法下载图片的大小是一样的。

    如果自动下载某网站的全部图片,其实一点也不复杂,无非就是找规律,如何获取全部图片的 url,然后循环调用以上三个函数。

    获取图片 url 可能会涉及正则表达式,关于正则表达式,可以参考前文学会正则表达式,玩弄文本于股掌之中

    2、压缩图片

    有一次我用邮箱向老板发送 5 张图片时,foxmail 提示我是否启用 QQ 邮箱的超大附件功能,原来 5 张图片已经 40+ MB,现在的手机拍摄的真是太清晰了。

    不过工作中的图片能看清楚内容就可以了,完全没有必要整那么清晰,文件太大,发给老板,老板打开图片也会卡,体验非常不好,于是我就想如何使用 Python 来压缩图片。

    找了很多方法,都不是很理想,有的软件下载后才发现是付费的,有的在使用时直接导致程序卡死,有的压缩率不够需要多次压缩,有的要求原始图片大小不能超过 5 M 有的失真有些严重。

    直到我用了 tinypng 的 api 接口,才发现这真的好用,图片几乎不失真,大多都控制在 1 MB 之内,在此分享给大家。

    先打开 https://tinypng.com/developers, 在下方输入你的用户名和邮箱,就可以获取一个 API KEY。

    然后 pip 安装一下这个库:

    pip install tinify
    

    编写三行代码就可以对图片进行压缩处理了:

    import tinify
    tinify.key = '此处填入你的key'
    tinify.from_file(src_img_path).to_file(dst_img_path)
    

    其中 src_img_path 是原图片,dst_img_path 是压缩后的图片。

    比如找个目录,对文件批量压缩一下:

    import tinify
    from pathlib import Path
    import os
    
    tinify.key = '此处填入你的key'
    path = "/Users/aaron/Documents/个人/origin" # 图片存放的路径
    
    for dirpath, dirs, files in os.walk(path):
        for file in files:
            file = Path(dirpath)/Path(file)
            if file.suffix.lower() in ['.jpg','.png','.gif']:
                print("compressing ..."+ file.as_posix())
                tinify.from_file(file.as_posix()).to_file(file.with_suffix(".compressed.jpg").as_posix())
    

    可以看到,压缩后的文件大小在 1M 左右或 1M 之内,打开文件对比,基本看不出任何区别:


    3、删除图片的元数据

    现在大部分快递已经可以对地址信息进行加密,大家的隐私保护意识也越来越高,可是一不小心,你随手发布的照片就可能暴露了你的位置信息。

    因此,用户发布照片时去除照片的位置、设备、时间等隐私信息显得很有必要,这些信息又叫元数据,也就是 metadata。

    Python 删除图片的元数据是有一个三方库 piexif,我使用它删除后,再用 exiftool 查看时,仍然可以查到许多隐私信息。

    也就是说 piexif 删除的不够彻底,于是我用 Python 封装了 exiftool,这下,图片的元数据可以删除的干干净净。

    文件 exif_tool.py 代码如下:

    import subprocess
    import os
    import json
    from pathlib import Path
    class ExifTool(object):
    
        sentinel = "{ready}\n"
        #windows
        #sentinel = "{ready}\r\n"
    
        def __init__(self, executable="/usr/bin/exiftool"):
            exiftool1 = Path("/usr/bin/exiftool")
            exiftool2 = Path("/usr/local/bin/exiftool")
            self.executable = executable
            if exiftool1.exists():
                self.executable = exiftool1.as_posix()
            elif exiftool2.exists():
                self.executable = exiftool2.as_posix()
            else:
                if Path(self.executable).exists():
                    pass
                else:
                    raise FileNotFoundError(self.executable)
    
    
        def __enter__(self):
            self.process = subprocess.Popen(
                [self.executable, "-stay_open", "True",  "-@", "-"],
                universal_newlines=True,
                stdin=subprocess.PIPE, stdout=subprocess.PIPE)
            return self
    
        def __exit__(self, exc_type, exc_value, traceback):
            self.process.stdin.write("-stay_open\nFalse\n")
            self.process.stdin.flush()
    
        def execute(self, *args):
            args = args + ("-execute\n",)
            self.process.stdin.write(str.join("\n", args))
            self.process.stdin.flush()
            output = ""
            fd = self.process.stdout.fileno()
            while not output.endswith(self.sentinel):
                # output += os.read(fd, 4096).decode('utf-8',errors=)
                output += os.read(fd, 4096).decode('utf-8',"ignore")
            return output[:-len(self.sentinel)]
    
        def get_metadata(self, *filenames):
            """
            返回多个文件的 exif 信息
            """
            return json.loads(self.execute("-G", "-j", "-n", *filenames))
    
        def get_exif_info(self, source_img):
            """
            返回单个文件的 exif 信息
            """
            return self.get_metadata(source_img)[0]
    
        def delete_exif_info(self, source_img):
            '''
            删除 exif 信息后,返回剩余的 exif 信息
            '''
            self.execute("-all=",source_img)
            metadata = self.get_metadata(source_img)
            return metadata[0]
    
    

    使用前先确保操作系统已经安装了 exiftool,程序默认读取两个位置:

    /usr/bin/exiftool
    /usr/local/bin/exiftool
    

    也可以自己传入 exiftool 的执行路径。

    使用举例:

    from pprint import pprint
    if __name__ == '__main__':
        with ExifTool() as e:
            exif = e.get_exif_info('/Users/aaron/Documents/个人/origin/文件1.jpg')
            pprint(exif)
            exif = e.delete_exif_info('/Users/aaron/Documents/个人/origin/文件1.jpg')
            print("========删除 exif 信息后========")
            pprint(exif)
    

    大家可以用 piexif 和我这里提供的 exif_tool 做个对比,看看哪个删除的更彻底,有问题请留言讨论。

    4、拼接长图

    思路也简单,也把要拼接的图片放在数组里面,然后计算图片的最大宽度作为拼接后图片的宽度,然后一张一张拼接即可。

    排版可以选择靠左对齐,或者水平居中对齐,空白位置的颜色也可以自己定义。

    具体代码如下:

    from pathlib import Path
    
    from PIL import Image
    
    if __name__ == '__main__':
        img_list = []
        imgs_path = Path('/Users/aaron/tmp/images')
        for img in imgs_path.iterdir():
            if img.suffix.lower() in ['.jpg','.png']:
                img_list.append(Image.open(img.as_posix()))
    
        width = 0
        height = 0
        for img in img_list:
            # 单幅图像尺寸
            w, h = img.size
            height += h
            # 取最大的宽度作为拼接图的宽度
            width = max(width, w)
    
        # 创建空白长图,这里可以传入 color 设置空白地方的颜色,默认黑色
        result = Image.new(img_list[0].mode, (width, height))
        # 拼接图片
        height = 0
        for img in reversed(img_list):
            w, h = img.size
            # 图片水平居中
            result.paste(img, box=(round(width / 2 - w / 2), height))
            height += h
        # 保存图片
        result.save('拼接长图.png')
    
    

    执行后的效果如下所示:

    5、如何识别图片上的文字

    这其实就是 OCR 了,非常实用,不过个人很难训练出优秀的模型,不如直接用大厂的 API。举个例子,百度云的 AI 产品,你可以在终端下执行这样一个命令来进行安装。

    pip install baidu-aip
    

    在这里我使用了百度云提供的在线文字识别产品,提供了 AipOcr 函数实现用户验证、client.basicGeneral 函数实现文字识别功能。

    代码如下:

    
    from aip import AipOcr
    """ 你的 APPID AK SK """
    APP_ID = '你的 App ID'
    API_KEY = '你的 Api Key'
    SECRET_KEY = '你的 Secret Key'
    client = AipOcr(APP_ID, API_KEY, SECRET_KEY)
    """ 读取图片 """
    def get_file_content(filePath):
        with open(filePath, 'rb') as fp:
            return fp.read()
    image = get_file_content('example.png')
    """ 调用通用文字识别, 图片参数为本地图片 """
    result = client.basicGeneral(image)
    print(result)
    

    在这段代码里,实现了三个功能,分别是用户验证、读取图片和识别图片。

    为了更直观地看到效果,我这里对着书拍个照片,然后让它识别一下:

    原图如下:

    识别结果如下:

    6、给图片加水印

    添加自己的水印来防止别人盗图,也可以宣传品牌形象,如果要为大量图片添加文字水印,不妨使用以下方法。

    from PIL import Image, ImageDraw, ImageFont
    
    def add_text_watermark(img, text):
        img = Image.open(img)
        draw = ImageDraw.Draw(img)
        myfont = ImageFont.truetype('/System/Library/Fonts/PingFang.ttc', size=100)
        fillcolor = "#ff0000"
        width, height = img.size
        draw.text((width - 700, height - 250), text, font=myfont, fill=fillcolor)
        return img
    
    
    if __name__ == '__main__':
        image = '/Users/aaron/Documents/个人/IMG_2288.compressed.jpg'
        img1 = add_text_watermark(image,'@Python七号')
        img1.save("result_text_watermark.jpg","jpeg")
    

    说明 draw.text((width - 700, height - 250), '@Python七号', font=myfont, fill=fillcolor)第一个括号填写的是位置,左上角的坐标是 (0,0),右下角的坐标是(width,heigth),本例中 (width - 700, height - 250) 相当于是右下角。

    效果如下(红色部分是程序添加的):

    那你可能又问下,如果加图片水印呢?比如现在有一个 logo 想添加到图片上,代码如下:

    from PIL import Image
    def add_img_watermark(img, img_watermark):
        rgba_image = Image.open(img).convert("RGBA")
        rgba_watermark = Image.open(img_watermark).convert("RGBA")
        image_x, image_y = rgba_image.size
        watermark_x, watermark_y = rgba_watermark.size
        # 缩放图片
        scale = 10
        watermark_scale = max(image_x / (scale * watermark_x), image_y / (scale * watermark_y))
        new_size = (int(watermark_x * watermark_scale), int(watermark_y * watermark_scale))
        rgba_watermark = rgba_watermark.resize(new_size, resample=Image.ANTIALIAS)
        # 透明度
        rgba_watermark_mask = rgba_watermark.convert("L").point(lambda x: min(x, 180))
        rgba_watermark.putalpha(rgba_watermark_mask)
    
        watermark_x, watermark_y = rgba_watermark.size
        # 水印位置
        rgba_image.paste(rgba_watermark, ( (image_x - watermark_x)//2, image_y - watermark_y-100), rgba_watermark_mask)  # 右上角
    
        return rgba_image.convert("RGB")
    
    if __name__ == '__main__':
        image = '/Users/aaron/Documents/个人/IMG_2288.compressed.jpg'
        img_watermark = "/Users/aaron/Downloads/IMG_2568.JPG"
        img2 = add_img_watermark(image, img_watermark)
        img2.save("result_img_watermark.jpg")
    

    效果如下图所示:

    最后的话

    图片是我们接触最多的媒体文件了,这里分享了 6 种关于图片的实用操作,需要的可以直接复制这里的代码使用。

    如果想了解关于图片的更多的实用操作,请留言,后续再发这方面的文章。

    留言

    展开全文
  • 手写HTML语言代码之《在文字中加条横线、插入图片》一、运用给网页加条横线在以下“教师自制教学软件的背景”后面输入我的第一个网页教师自制教学软件的背景 随着教育体制的改革和信息时代的到来,可能越来越多的...

    千里之堤始于足下!!

    学习网页制作的心得体会5.........手写HTML语言代码之《在文字中加条横线、插入图片》

    一、运用


    给网页加条横线

    在以下“教师自制教学软件的背景”后面输入


    我的第一个网页
    教师自制教学软件的背景

    老师板书  随着教育体制的改革和信息时代的到来,可能越来越多的同学己经不再适应看着教师的板书、记着笔记的学习方式,教师也开始不习惯将自己的备课笔记一遍又一遍地写在黑板上。一堂40或45分钟的课,老师用于板书的时间不下15 分钟,也就是说真正用于教学的时间不到70 % ,这在知识信息化的21 世纪,己经捉襟见肘了;而且板书的信息含量很小,师生之问互动不强,即使很高明的教师也很难课件教学把黑底白字表现得很形象,因此难免把一些原本很感性的东西解释得比较抽象,让同学难以接受。这时,多媒体演示课件应运而生,顾名思义,多媒体演示课件可习理解为利用图、文、声、像等各种多媒体素材,组成一个可以替代传统的教学板书,并能配合老师讲解、进行课堂内容演示的多媒体程序。随着多媒体技术的发展,现在的多媒体演示课件一般能实现如下几个基本功能:

    1、与课堂上的讲课内容配套,将所讲内容从黑板移植到多媒体计算机中。

    2、通过计算机,能把讲课内容按照课堂听讲的习惯演示出来。

    3、充分发挥计算机的多媒体特性,制作并利用各种多媒体素材讲解课堂内容。

    4、具备一定的交互性,使得师生间能通过课件进行交流,以达到“教学相长”的目的。

    保存并刷新后,在题目的下面就会出现一条横线。那么“


    ”是什么意思?它有哪些属性?“
    ”这个标识,可以加一条横线,我们还可以给它加一些属性,比如
    ,意思如下:

    “size”..........是线的粗细

    “color”.........是线的颜色

    “with”..........是线的长度

    “align”..........是线的位置

    上面网页中的横线修改后就变成如下:

    我的第一个网页
    教师自制教学软件的背景

    展开全文
  • 其原理即通过一定的规则来提取图片中的像素点,从而得到了所需图像或者文字内容的具体坐标,然后按照规则将它们形象地用一个个的粒子进行表示,从而使图片呈现出抽象的观赏效果。 一般用于制作社交头像和颗粒文字...
  • 经过图片文字、音乐、说明等方法,更好的全面展现企事业形象及产品的细节特征,增强交互体会,更好的传递信息。 移动互联地图使用 能够在手机、PAD、PC、接触一体机等各种终端进行展现,让高清图片展现变得...
  • 之前的两篇文章介绍了图片系统的技术组件选型和技术方案设计,从这篇文章开始我们将搭建工程进行详细的编码开发和效果测试。整个图片服务工程代码会上传到了CSDN的下载区,如果对工程感兴趣那么读者可以直接下载。
  • 大屏数据可视设计指南

    万次阅读 多人点赞 2019-01-03 14:25:31
    把相对复杂、抽象的数据通过可视的方式以人们更易理解的形式展示出来的一系列手段叫做数据可视,数据可视是为了更形象地表达数据内在的信息和规律,促进数据信息的传播和应用。 在当前新技术支持下,数据可视...
  • 在目前的大数据趋势中,数据的大屏可视成为大家所推崇的一种互动展示模式。如果我们能够早一些了解和掌握这方面的技术,相信对我们的未来将会非常有帮助! 我们知道,通过报表工具实现大屏展示可以通过单张报表、...
  • 涂鸦的图片可以复原吗

    千次阅读 2020-12-18 18:56:47
    大家好,我是时间财富网智能客服时间君,上述问题将由我为大家进行解答。涂鸦的图片不可以复原。涂鸦会破坏图片的很多细节,这个过程是不...到了21世纪,许多年轻人把涂鸦与时尚的嘻哈元素结合,向多元发展。涂...
  • 灵活应用 形象表达——图文的排列与布局 发表于736 天前 ⁄ 如何制作PPT ⁄ 评论数 14 我们常说“文不如字,字不如图”,在PPT中插入图片可使得页面瞬间鲜活起来,摆脱满篇文字的枯燥与乏味。 ...
  • 一、教学设计思想现代教育理论提出“学生...《在WORD文档中插入图片》这节课,我根据学生和教材实际,设计出以“教师为主导,学生为主体”的符合学科特点的教学与管理模式。在教学过程中,明确地提出“根据内容插入...
  • 在创作内容时,头条号作者们会寻找一些合适的图片作为配图,增加文章的可读性,尤其是娱乐、体育、新闻类内容。然而大部分作者往往对配图的来源不太在意,随便在网络上找到合适的图片就下载使用,这其中其实隐藏着...
  • 通过图片文字、音乐、解说等方式,更好的全面展示企事业形象及产品的细节特征,增强交互体验,更好的传递信息。 移动互联3D应用 可以在手机、PAD、PC、触摸一体机等各种终端进行展示,让全景展示变得随心所欲...
  • 【任务说明】 任务四 : 数据可视 (20 分 ) 本任务中 所需要 的原始数据四 存放于任务四 MySQL 的 的 project...1、 连锁酒店一般都具有全国统一的品牌形象识别系统、全国统一的会员体系和营销体系、价格相比较很
  • 数据可视Data Visualization:就是指将结构或非结构数据转换成适当的可视图表,然后将隐藏在数据中的信息直接展现于人们面前。 Canvas标签用于绘制图形的 HTML 元素,canvas元素本身并没有绘制能力,它仅仅是图形的...
  • 通过图片文字、音乐、解说等方式,更好的全面展示企事业形象及产品的细节特征,增强交互体验,更好的传递信息。 移动互联3D应用 可以在手机、PAD、PC、触摸一体机等各种终端进行展示,让全景展示变得随心所欲,...
  • 三、数据形象化 指标值形象化 对数据指标值用图形的方式展现,并进行视觉优化,如使用三维立体图形 通过构建场景来表现 前提是要把握数据之间的内在联系,如简单到复杂、从前到后等,可以使用阶梯式(如受教育的...
  • 大数据可视平台是通过三维表现技术来表示复杂的信息,实现对海量数据的立体呈现。可视技术借助人脑的视觉思维能力,通过挖掘数据之间重要的关联关系将若干关联性的可视数据进行汇总处理,揭示数据中隐含的规律...
  • 数学课件ppt背景图片

    2021-06-27 06:39:05
    数学课件ppt背景图片数学作为一门有趣的课程,更加少不了课件的配合,以下关于数学课件ppt供大家下载!数学课件1.利用课件教学,使得教学形式多样,有助于激发学生的学习兴趣。传统数学教学模式单一,在数学课堂...
  • 简单CSS3+JQ实现图片的3D翻转

    千次阅读 2018-05-10 23:19:31
    主要实现效果:鼠标从不同的方向进入图片图片所在的正方体就会以此方向翻转,显示文字介绍。 效果如下图 此处的正方体只是形象的比喻方便理解。 如下图为计算机的坐标系,文字介绍所在块(后面简称B)与图片...
  • 对于做网页的初学者可能更习惯于使用一些漂亮的图片作为自己网页的背景,但是,浏览一下大型的商业网站,你会发现他们更多运用的是白色、蓝色、黄色等,使得网页显得典雅,大方和温馨。更重要的是,这样可以大大加快...
  • 十二大相似图片搜索网站(以图搜图)

    千次阅读 2019-09-11 17:52:53
    十二大相似图片搜索网站(以图搜图) 如何凭着一张现有图片找出它的原始图片,或者是凭着一张小的缩略图找出原始大图?下面的搜索引擎可以帮你实现、以图找图、以图搜图。 以图搜图是颠覆性的搜索方式,基于图片的...
  • 来源丨高下制图你眼中的数据可视的作品是否是各种条形图、柱状图等等堆积在一起进行数据的展示?今天看完为大家整理的17个数据可视优秀作品,你就知道原来数据可视作品还可以这样做,欢迎大家分...
  • 网页颜色搭配技巧 文字字体、字号、字体排版等 一般来说,网页的背景色应该柔和一些、素一些、淡一些,再配上深色的文字,使人看起来自然、舒畅。 对于做网页的初学者可能更习惯于使用一些漂亮的图片作为自己...
  • 图片文字分割的时候,常用的方法有两种。一种是投影法,适用于排版工整,字间距行间距比较宽裕的图像;还有一种是用OpenCV的轮廓检测,适用于文字不规则排列的图像。
  • 喜欢用 Python 做项目的小伙伴不免会遇到这种情况:做图表时,用哪种好看又实用的可视工具包呢?之前文章里出现过漂亮的图表时,也总有读者在后台留言问该图表时用什么工具做的。下面,作者介...
  • 来源:机器之心喜欢用 Python 做项目的小伙伴不免会遇到这种情况:做图表时,用哪种好看又实用的可视工具包呢?之前文章里出现过漂亮的图表时,也总有读者在后台留言问该图表时用什么工具做的...
  • 本书为大学物理学的配套代码,每一部分都精心选取和编制了10个范例,共有140个范例。...此代码可按操作直接运行,对深刻学习物理和形象化理解物理规律很有帮助,让物理不再是刻板而枯燥的文字和公式。
  • 8个流行的Python可视工具包。

    千次阅读 2020-07-22 08:00:43
    使用 Pygal 非常简单: 实例化图片; 用图片目标属性格式; 用 figure.add() 将数据添加到图片中。 我在使用 Pygal 的过程中遇到的主要问题在于图片渲染。必须要用 render_to_file 选项,然后在 web 浏览器中打开...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 12,544
精华内容 5,017
关键字:

形象化字体图片