outputcache(au设备类型mme和asio的区别)
资讯
2023-11-02
356
1. outputcache,au设备类型mme和asio的区别?
AU设备类型MME(Multimedia Extension)和ASIO(Audio Stream Input/Output)是两种不同的音频设备类型,主要区别如下:
1. 支持平台:MME是Windows操作系统的标准音频设备,而ASIO主要是用于专业音乐软件中的音频接口,如Ableton Live、Cubase等。
2. 低延迟:ASIO支持更低的音频延迟,因此更适用于需要实时处理音频的应用程序,如音频录制和混音。而MME的音频延迟相对较高,适用于一些普通的音频的播放。
3. 多路复用:ASIO支持多路音频输入/输出,因此可以同时处理多个音频信号,而MME只支持单一的音频输入/输出。
4. 音频质量:ASIO支持更高音频质量和精度,主要是因为它避免了音频数据的重复缓存和处理,使音频采集和输出更加准确。而MME则受到音频缓存和处理的限制,因此可能会出现一些音频失真和低音质问题。
综上所述,MME适用于一些普通的音频播放应用,而ASIO则更适用于需要处理音频信号的专业音乐应用程序。ASIO支持更高音频质量和精度,并且支持多路音频输入/输出和更低的音频延迟。
2. Java程序员如何拿到50w的年薪?
在这个IT系统动辄就是上亿流量的时代,Java作为大数据时代应用最广泛的语言,诞生了一批又一批的新技术,包括HBase、Hadoop、MQ、Netty、SpringCloud等等 。
一些独角兽公司以及腾讯、阿里、百度、网易等知名大厂对Java人才的需求量连年升级,有2年工作经验的优秀程序员能轻松达到30w+的水平,但与此同时对人才的要求也越来越高。
很多学生都会疑惑,同样是程序员吗,同样是学的Java,同样掌握了相应的技术,为什么自己就不能拿到BAT企业的Offer,不能拿到50W+的年薪?自己和年薪50W+的程序员差距在哪里?
二、工作效率
一个程序员每天写出来的有效代码(刨去自动生成和复制粘贴),大约就100~200行。我们的时间还耗费在需求讨论、思路整理、代码编译、提交、部署测试多个环节,优秀的工程师总能想到很多办法来提升自己的工作效率,所以一线互联网公司架构师,都会配置“效能平台(如阿里的云效平台)”来通过一整套的CI/CD方案来提高研发效率。
三、解决问题的能力
编程一个脑力活,如果把它做成了体力活,这就代表是时候改变一下了。现在的框架封装越来越便捷,通过springboot刚毕业都能把web开发玩的666,通过netty的相关封装,一年新手也能写出来性能不错的网络程序。架构师和普通开发的区别不在于会使用多少新潮的框架或者api,而在于对底层原理了解的程度。如果用的开源框架在生产环境出现问题,出面解决的肯定是年薪50W+的架构师。话说,来看看自己对spring框架原理知多少?
四、学习能力
Java往深往上走,下到C/C++,上到中间件、架构方式,按照IT行业技术发展的速度,不被掉队那就得不断的学习。普通程序员还在纠结业务代码怎么实现,技术领导们每天的工作就是研究各种高并发/新框架/分布式这些东西,慢慢下去差距能不大嘛,只有用很快的速度,掌握必备知识,才有一搏之力!
五、软技能
多少人努力进入大厂,但终究默默无闻进,默默无闻的出来,BAT等一线公司数十万技术人,你知道多少大牛,记住的有多少呢?平台重要,更重要的是,能在平台发展到什么程度。每个公司都有他的管理尿性,有它的企业氛围,软技能决定了能到什么职位!看到差距,才知道哪里需要改进,有了目标,更有学习动力。技术人成长不能再单打独斗,要学会与人交流,学会将别人的知识为自己所用。预祝每一位小伙伴都能拿到自己满意的工资待遇,不负韶华!
六、最火的语言还是Java?
下面是2017年7月编程语言排行榜 Top 10榜单:
Java语言在2016-2017依然占据榜首。Java作为最受欢迎的语言,在编程语言高薪行列里依然名列前茅,无疑是想进入互联网行业,拿高薪的最佳选择。
光有技术还不够
当然除了技术过关以外,还该有Java领域有丰富的经验,独到的见解。能够从前台到后台独立的搭建完成,良好的沟通能力及团队协作能力等。
一般情况下,就算不去大公司,很多小公司的技术总监年薪上也差不了多少呢。当然这些都需要一些时间沉淀,没办法速成。
七、Java程序员的职业发展有几个选择
Java程序员的职业发展有几个选择:专注于技术,成为技术专家;转型到技术型销售、技术支持等;随着技术成长,从技术性管理到高级管理。
这归根结底,都是以Java技术为基础。在扎实的技术基础上(高级软件工程师),如果有着比较强的抽象设计能力,又打算专注于技术开发,架构师是个好的选择;如果性格更适合做管理,情商能力突出,技术型管理应该是下一步的方向。这种发展规划属于‘瘸腿走路’的类型,各有侧重主攻发展的职业方向,也是目前一般程序员普遍采用的发展路线规划。
八、职业规划
已经工作两年以上的程序员可以有几种基本的职业规划:技术专家、软件架构师、实施顾问或销售。其中程序员最主要的发展方向是资深技术专家,无论是 Java、.NET还是数据库领域,都要首先成为专家,然后才可能继续发展为架构师。
尽管架构师的职位可以工作一辈子,待遇也非常好,对于科班出身的程序 员最为适合,但这种工作职位非常有限,因为在中国目前的IT行业,架构师需要的条件比较复杂,而且需求量也比较少,这也是我国软件行业走向国际并有待成熟 的因素之一。
九、管理能力和专业知识技能
目前国内缺乏的是管理能力和专业知识技能并驾齐驱的IT人才。特别是懂得运用目标管理技术的项目管理人才,既掌握核心技术又有全局掌控能力的项目管 理人才,非常难找。我建议对于大多数人来说,首先是要专,只有在技术方向上做得比较深入,才能适当做工作调整,把自己转变为某个领域的专家。
根据自己的情况,决定自己做Java架构师还是高级的顾问销售,另外一部分人可能就会走向管理,这和个人性格与情商有关。软件优秀的管理者都必须懂得技术,掌握核心技能的人才向管理岗位转型有很大的优势,因而提升管理能力成了转型的关键。
十、总之,Java程序员要根据本身的基本素质、技术能力选择开发层次,由低到高,逐步发展。也是一名程序员本身具备相应素质的循序渐进的发展轨迹,包括很强的技术背景和综合管理才能等素养,这也就是所谓‘两条腿走路’的职业规划发展路线。
3. 3dmax怎么设置渲染模板?
在3ds Max中设置渲染模板需要按照以下步骤进行:
1. 打开3ds Max软件并打开您的场景。
2. 在顶部菜单栏中选择“渲染(Render)”选项。
3. 在下拉菜单中选择“渲染设置(Render Setup)”。
4. 渲染设置窗口会弹出。在左侧的菜单中,选择“渲染输出(Render Output)”选项。
5. 在右侧的面板中,您可以进行以下设置:
- 输出大小(Output Size):设置渲染图像的分辨率。您可以手动输入宽度和高度,或者选择预设的常用分辨率。
- 文件输出路径(File Output Path):设置渲染图像保存的路径和文件名。点击右侧的“浏览(Browse)”按钮,选择合适的文件夹和文件名。
- 输出格式(File Format):选择您希望渲染输出为的图像格式,如JPEG、PNG、BMP等。
- 帧范围(Frame Range):设置您希望渲染的帧范围。您可以选择完整序列或特定帧范围。
- 其他设置(Additional Render Output):根据需要,您可以设置其他输出参数,如镜头效果、Alpha通道、多重过滤等。
6. 完成设置后,点击窗口底部的“确定(OK)”按钮。
现在,当您渲染场景时,3ds Max将使用您设置的渲染模板来生成输出图像并保存到指定的文件路径中。
请注意,以上步骤仅适用于3ds Max软件版本2022及更早版本。如果您使用的是不同版本的3ds Max或插件,请参考相关文档或该特定版本的指南以获取准确的操作步骤。
4. 最近在学习pyspark?
Spark提供了一个Python_Shell,即pyspark,从而可以以交互的方式使用Python编写Spark程序。
有关Spark的基本架构介绍参考http://blog.csdn.net/cymy001/article/details/78483614;
有关Pyspark的环境配置参考http://blog.csdn.net/cymy001/article/details/78430892。
pyspark里最核心的模块是SparkContext(简称sc),最重要的数据载体是RDD。RDD就像一个NumPy array或者一个Pandas Series,可以视作一个有序的item集合。只不过这些item并不存在driver端的内存里,而是被分割成很多个partitions,每个partition的数据存在集群的executor的内存中。
引入Python中pyspark工作模块
import pyspark
from pyspark import SparkContext as sc
from pyspark import SparkConf
conf=SparkConf().setAppName("miniProject").setMaster("local[*]")
sc=SparkContext.getOrCreate(conf)
#任何Spark程序都是SparkContext开始的,SparkContext的初始化需要一个SparkConf对象,SparkConf包含了Spark集群配置的各种参数(比如主节点的URL)。初始化后,就可以使用SparkContext对象所包含的各种方法来创建和操作RDD和共享变量。Spark shell会自动初始化一个SparkContext(在Scala和Python下可以,但不支持Java)。
#getOrCreate表明可以视情况新建session或利用已有的session
1
2
3
4
5
6
7
SparkSession是Spark 2.0引入的新概念。SparkSession为用户提供了统一的切入点,来让用户学习spark的各项功能。 在spark的早期版本中,SparkContext是spark的主要切入点,由于RDD是主要的API,我们通过sparkcontext来创建和操作RDD。对于每个其他的API,我们需要使用不同的context。例如,对于Streming,我们需要使用StreamingContext;对于sql,使用sqlContext;对于hive,使用hiveContext。但是随着DataSet和DataFrame的API逐渐成为标准的API,就需要为他们建立接入点。所以在spark2.0中,引入SparkSession作为DataSet和DataFrame API的切入点。SparkSession实质上是SQLContext和HiveContext的组合(未来可能还会加上StreamingContext),所以在SQLContext和HiveContext上可用的API在SparkSession上同样是可以使用的。SparkSession内部封装了SparkContext,所以计算实际上是由SparkContext完成的。
初始化RDD的方法
(1)本地内存中已经有一份序列数据(比如python的list),可以通过sc.parallelize去初始化一个RDD。当执行这个操作以后,list中的元素将被自动分块(partitioned),并且把每一块送到集群上的不同机器上。
import pyspark
from pyspark import SparkContext as sc
from pyspark import SparkConf
conf=SparkConf().setAppName("miniProject").setMaster("local[*]")
sc=SparkContext.getOrCreate(conf)
#(a)利用list创建一个RDD;使用sc.parallelize可以把Python list,NumPy array或者Pandas Series,Pandas DataFrame转成Spark RDD。
rdd = sc.parallelize([1,2,3,4,5])
rdd
#Output:ParallelCollectionRDD[0] at parallelize at PythonRDD.scala:480
#(b)getNumPartitions()方法查看list被分成了几部分
rdd.getNumPartitions()
#Output:4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#(c)glom().collect()查看分区状况
rdd.glom().collect()
#Output:[[1], [2], [3], [4, 5]]
1
2
3
在这个例子中,是一个4-core的CPU笔记本;Spark创建了4个executor,然后把数据分成4个块。colloect()方法很危险,数据量上BT文件读入会爆掉内存……
(2)创建RDD的另一个方法是直接把文本读到RDD。文本的每一行都会被当做一个item,不过需要注意的一点是,Spark一般默认给定的路径是指向HDFS的,如果要从本地读取文件的话,给一个file://开头(windows下是以file:\\开头)的全局路径。
import pyspark
from pyspark import SparkContext as sc
from pyspark import SparkConf
conf=SparkConf().setAppName("miniProject").setMaster("local[*]")
sc=SparkContext.getOrCreate(conf)
#(a)记录当前pyspark工作环境位置
import os
cwd=os.getcwd()
cwd
#Output:'C:\\Users\\Yu\\0JulyLearn\\5weekhadoopspark'
#(b)要读入的文件的全路径
rdd=sc.textFile("file:\\\\\\" + cwd + "\\names\yob1880.txt")
rdd
#Output:file:\\\C:\Users\Yu\0JulyLearn\5weekhadoopspark\names\yob1880.txt MapPartitionsRDD[3] at textFile at NativeMethodAccessorImpl.java:0
#(c)first()方法取读入的rdd数据第一个item
rdd.first()
#Output:'Mary,F,7065'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
甚至可以sc.wholeTextFiles读入整个文件夹的所有文件。但是要特别注意,这种读法,RDD中的每个item实际上是一个形如(文件名,文件所有内容)的元组。读入整个文件夹的所有文件。
import pyspark
from pyspark import SparkContext as sc
from pyspark import SparkConf
conf=SparkConf().setAppName("miniProject").setMaster("local[*]")
sc=SparkContext.getOrCreate(conf)
#记录当前pyspark工作环境位置
import os
cwd=os.getcwd()
cwd
#Output:'C:\\Users\\Yu\\0JulyLearn\\5weekhadoopspark'
rdd = sc.wholeTextFiles("file:\\\\\\" + cwd + "\\names\yob1880.txt")
rdd
#Output:org.apache.spark.api.java.JavaPairRDD@12bcc15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
rdd.first()
Output:
('file:/C:/Users/Yu/0JulyLearn/5weekhadoopspark/names/yob1880.txt',
1
2
3
4
5
其余初始化RDD的方法,包括:HDFS上的文件,Hive中的数据库与表,Spark SQL得到的结果。这里暂时不做介绍。
RDD Transformation
(1)RDDs可以进行一系列的变换得到新的RDD,有点类似列表推导式的操作,先给出一些RDD上最常用到的transformation:
map() 对RDD的每一个item都执行同一个操作
flatMap() 对RDD中的item执行同一个操作以后得到一个list,然后以平铺的方式把这些list里所有的结果组成新的list
filter() 筛选出来满足条件的item
distinct() 对RDD中的item去重
sample() 从RDD中的item中采样一部分出来,有放回或者无放回
sortBy() 对RDD中的item进行排序
1
2
3
4
5
6
如果想看操作后的结果,可以用一个叫做collect()的action把所有的item转成一个Python list。数据量大时,collect()很危险……
import pyspark
from pyspark import SparkContext as sc
from pyspark import SparkConf
conf=SparkConf().setAppName("miniProject").setMaster("local[*]")
sc=SparkContext.getOrCreate(conf)
numbersRDD = sc.parallelize(range(1,10+1))
print(numbersRDD.collect())
#map()对RDD的每一个item都执行同一个操作
squaresRDD = numbersRDD.map(lambda x: x**2) # Square every number
print(squaresRDD.collect())
#filter()筛选出来满足条件的item
filteredRDD = numbersRDD.filter(lambda x: x % 2 == 0) # Only the evens
print(filteredRDD.collect())
#Output:
#[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
#[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
#[2, 4, 6, 8, 10]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import pyspark
from pyspark import SparkContext as sc
from pyspark import SparkConf
conf=SparkConf().setAppName("miniProject").setMaster("local[*]")
sc=SparkContext.getOrCreate(conf)
#flatMap() 对RDD中的item执行同一个操作以后得到一个list,然后以平铺的方式把这些list里所有的结果组成新的list
sentencesRDD=sc.parallelize(['Hello world','My name is Patrick'])
wordsRDD=sentencesRDD.flatMap(lambda sentence: sentence.split(" "))
print(wordsRDD.collect())
print(wordsRDD.count())
#Output:
#['Hello', 'world', 'My', 'name', 'is', 'Patrick']
#6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
对比一下:
这里如果使用map的结果是[[‘Hello’, ‘world’], [‘My’, ‘name’, ‘is’, ‘Patrick’]],
使用flatmap的结果是全部展开[‘Hello’, ‘world’, ‘My’, ‘name’, ‘is’, ‘Patrick’]。
flatmap即对应Python里的如下操作:
l = ['Hello world', 'My name is Patrick']
ll = []
for sentence in l:
ll = ll + sentence.split(" ") #+号作用,two list拼接
ll
1
2
3
4
5
(2)最开始列出的各个Transformation,可以一个接一个地串联使用,比如:
import pyspark
from pyspark import SparkContext as sc
from pyspark import SparkConf
conf=SparkConf().setAppName("miniProject").setMaster("local[*]")
sc=SparkContext.getOrCreate(conf)
def doubleIfOdd(x):
if x % 2 == 1:
return 2 * x
else:
return x
numbersRDD = sc.parallelize(range(1,10+1))
resultRDD = (numbersRDD
.map(doubleIfOdd) #map,filter,distinct()
.filter(lambda x: x > 6)
.distinct()) #distinct()对RDD中的item去重
resultRDD.collect()
#Output:[8, 10, 18, 14]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
(3)当遇到更复杂的结构,比如被称作“pair RDDs”的以元组形式组织的k-v对(key, value),Spark中针对这种item结构的数据,定义了一些transform和action:
reduceByKey(): 对所有有着相同key的items执行reduce操作
groupByKey(): 返回类似(key, listOfValues)元组的RDD,后面的value List 是同一个key下面的
sortByKey(): 按照key排序
countByKey(): 按照key去对item个数进行统计
collectAsMap(): 和collect有些类似,但是返回的是k-v的字典
1
2
3
4
5
import pyspark
from pyspark import SparkContext as sc
from pyspark import SparkConf
conf=SparkConf().setAppName("miniProject").setMaster("local[*]")
sc=SparkContext.getOrCreate(conf)
rdd=sc.parallelize(["Hello hello", "Hello New York", "York says hello"])
resultRDD=(rdd
.flatMap(lambda sentence:sentence.split(" "))
.map(lambda word:word.lower())
.map(lambda word:(word, 1)) #将word映射成(word,1)
.reduceByKey(lambda x, y: x + y)) #reduceByKey对所有有着相同key的items执行reduce操作
resultRDD.collect()
#Output:[('hello', 4), ('york', 2), ('says', 1), ('new', 1)]
result = resultRDD.collectAsMap() #collectAsMap类似collect,以k-v字典的形式返回
result
#Output:{'hello': 4, 'new': 1, 'says': 1, 'york': 2}
resultRDD.sortByKey(ascending=True).take(2) #sortByKey按键排序
#Output:[('hello', 4), ('new', 1)]
#取出现频次最高的2个词
print(resultRDD
.sortBy(lambda x: x[1], ascending=False)
.take(2))
#Output:[('hello', 4), ('york', 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
RDD间的操作
(1)如果有2个RDD,可以通过下面这些操作,对它们进行集合运算得到1个新的RDD
rdd1.union(rdd2): 所有rdd1和rdd2中的item组合(并集)
rdd1.intersection(rdd2): rdd1 和 rdd2的交集
rdd1.substract(rdd2): 所有在rdd1中但不在rdd2中的item(差集)
rdd1.cartesian(rdd2): rdd1 和 rdd2中所有的元素笛卡尔乘积(正交和)
1
2
3
4
import pyspark
from pyspark import SparkContext as sc
from pyspark import SparkConf
conf=SparkConf().setAppName("miniProject").setMaster("local[*]")
sc=SparkContext.getOrCreate(conf)
#初始化两个RDD
numbersRDD = sc.parallelize([1,2,3])
moreNumbersRDD = sc.parallelize([2,3,4])
1
2
3
4
5
6
7
8
9
numbersRDD.union(moreNumbersRDD).collect() #union()取并集
#Output:[1, 2, 3, 2, 3, 4]
numbersRDD.intersection(moreNumbersRDD).collect() #intersection()取交集
#Output:[2, 3]
numbersRDD.subtract(moreNumbersRDD).collect() #substract()取差集
#Output:[1]
numbersRDD.cartesian(moreNumbersRDD).collect() #cartesian()取笛卡尔积
#Output:[(1, 2), (1, 3), (1, 4), (2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4)]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(2)在给定2个RDD后,可以通过一个类似SQL的方式去join它们
import pyspark
from pyspark import SparkContext as sc
from pyspark import SparkConf
conf=SparkConf().setAppName("miniProject").setMaster("local[*]")
sc=SparkContext.getOrCreate(conf)
# Home of different people
homesRDD = sc.parallelize([
('Brussels', 'John'),
('Brussels', 'Jack'),
('Leuven', 'Jane'),
('Antwerp', 'Jill'),
])
# Quality of life index for various cities
lifeQualityRDD = sc.parallelize([
('Brussels', 10),
('Antwerp', 7),
('RestOfFlanders', 5),
])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
homesRDD.join(lifeQualityRDD).collect() #join
#Output:
#[('Antwerp', ('Jill', 7)),
# ('Brussels', ('John', 10)),
# ('Brussels', ('Jack', 10))]
homesRDD.leftOuterJoin(lifeQualityRDD).collect() #leftOuterJoin
#Output:
#[('Antwerp', ('Jill', 7)),
# ('Leuven', ('Jane', None)),
# ('Brussels', ('John', 10)),
# ('Brussels', ('Jack', 10))]
homesRDD.rightOuterJoin(lifeQualityRDD).collect() #rightOuterJoin
#Output:
#[('Antwerp', ('Jill', 7)),
# ('RestOfFlanders', (None, 5)),
# ('Brussels', ('John', 10)),
# ('Brussels', ('Jack', 10))]
homesRDD.cogroup(lifeQualityRDD).collect() #cogroup
#Output:
#[('Antwerp',
# (<pyspark.resultiterable.ResultIterable at 0x73d2d68>,
# <pyspark.resultiterable.ResultIterable at 0x73d2940>)),
# ('RestOfFlanders',
# (<pyspark.resultiterable.ResultIterable at 0x73d2828>,
# <pyspark.resultiterable.ResultIterable at 0x73d2b70>)),
# ('Leuven',
# (<pyspark.resultiterable.ResultIterable at 0x73d26a0>,
# <pyspark.resultiterable.ResultIterable at 0x7410a58>)),
# ('Brussels',
# (<pyspark.resultiterable.ResultIterable at 0x73d2b38>,
# <pyspark.resultiterable.ResultIterable at 0x74106a0>))]
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
# Oops! Those <ResultIterable>s are Spark's way of returning a list
# that we can walk over, without materializing the list.
# Let's materialize the lists to make the above more readable:
(homesRDD
.cogroup(lifeQualityRDD)
.map(lambda x:(x[0], (list(x[1][0]), list(x[1][1]))))
.collect())
#Output:
#[('Antwerp', (['Jill'], [7])),
# ('RestOfFlanders', ([], [5])),
# ('Leuven', (['Jane'], [])),
# ('Brussels', (['John', 'Jack'], [10]))]
1
2
3
4
5
6
7
8
9
10
11
12
13
惰性计算,actions方法
特别注意:Spark的一个核心概念是惰性计算。当你把一个RDD转换成另一个的时候,这个转换不会立即生效执行!!!Spark会把它先记在心里,等到真的有actions需要取转换结果时,才会重新组织transformations(因为可能有一连串的变换)。这样可以避免不必要的中间结果存储和通信。
常见的action如下,当它们出现的时候,表明需要执行上面定义过的transform了:
collect(): 计算所有的items并返回所有的结果到driver端,接着 collect()会以Python list的形式返回结果
first(): 和上面是类似的,不过只返回第1个item
take(n): 类似,但是返回n个item
count(): 计算RDD中item的个数
top(n): 返回头n个items,按照自然结果排序
reduce(): 对RDD中的items做聚合
1
2
3
4
5
6
import pyspark
from pyspark import SparkContext as sc
from pyspark import SparkConf
conf=SparkConf().setAppName("miniProject").setMaster("local[*]")
sc=SparkContext.getOrCreate(conf)
rdd = sc.parallelize(range(1,10+1))
rdd.reduce(lambda x, y: x + y) #reduce(): 对RDD中的items做聚合
#Output:55
1
2
3
4
5
6
7
8
9
10
reduce的原理:先在每个分区(partition)里完成reduce操作,然后再全局地进行reduce。
有时候需要重复用到某个transform序列得到的RDD结果。但是一遍遍重复计算显然是要开销的,所以我们可以通过一个叫做cache()的操作把它暂时地存储在内存中。缓存RDD结果对于重复迭代的操作非常有用,比如很多机器学习的算法,训练过程需要重复迭代。
import pyspark
from pyspark import SparkContext as sc
from pyspark import SparkConf
conf=SparkConf().setAppName("miniProject").setMaster("local[*]")
sc=SparkContext.getOrCreate(conf)
import numpy as np
numbersRDD = sc.parallelize(np.linspace(1.0, 10.0, 10))
squaresRDD = numbersRDD.map(lambda x: x**2)
squaresRDD.cache() # Preserve the actual items of this RDD in memory
avg = squaresRDD.reduce(lambda x, y: x + y) / squaresRDD.count()
print(avg)
#Output:38.5
5. HEVC图像格式你知道多少?
在升级 iOS 11 之后,iPhone 7 及更新的设备内的照片存储将不再用 JPEG 了,而采用了一种新的图片格式 HEIF(发音同 heef),在 iOS 中对应的文件后缀为 .heic ,其编码用的是 HEVC(这个发不了音,哈哈哈)格式,又称 H.265 (这个就很熟悉了 H.264 的下一代),同时视频也用 HEVC 作为编码器,对应的文件后缀还是 .mov 。
这里要注意他们俩的关系, HEIF 是图片格式,而 HEVC 是编码格式(类似 H.264,VP8),HEIF 是图片容器(类似于视频的 mkv,mp4 后缀),而用 HEVC 进行编码的 HEIF 图片就是后缀为 .heic 的图片,也是苹果主要使用的格式。
这两个都是很新的标准,分别在 2015 和 2013 年才被 ISO 批准。这篇文章主要介绍一下 HEIF 格式和与其他图片格式相比的优劣。
发展史如下:
screenshot
什么是 HEIF?
screenshot
这张图是 WWDC Session 511 的形容 Heif 的一句英文诗,JPEG 很大,但是 HEIF 和小。
HEIF 全称 High Efficiency Image Format (HEIF)。是由 Moving Picture Experts Group 制定的,存储图片和图片序列的格式。
相关的介绍位置这边可以看到 nokiatech.github.io/heif/ ,对的,你没有看错,是 Nokia 的技术人员们制定的。相比 JPEG ,PNG 等传统的图片格式来说, HEIF 可算是相当年轻了,但是这种格式相比 JPEG 等有很多的优点。
压缩比高,在相同的图片质量情况下,平比 JPEG 高两倍。
能增加如图片深度信息,透明通道等辅助图片。(苹果正是通过这个就能实现动态的调整图片的景深)。
depth
支持存放多张图片,类似相册和集合。(实现多重曝光的效果)
screenshot
支持多张图片实现 GIF 和 livePhoto 的动画效果。
无类似 JPEG 的最大像素限制。
支持透明像素。
分块加载机制。
支持缩略图。
在这个 Nokia 网站上可以看到相关的例子。
文件组成
在视频文件中,容器和编码是独立开的,比如 mp4,mkv 等格式是容器,而 H.264,VP8 等是编码。但是图像文件中,像 JPEG 就是混合在一起的(所以它不好用啊,哈哈哈哈),HEIF 就把容器和编码分开了,有用来存放单个或者多个图像的容器。
所以基于不同的编码器,会有不同的文件后缀。
Apple 设备中默认使用的都是 HEVC 的编码的 HEIF 格式。
在 Apple 所编码的 HEIF 的文件组成大致如下图,其 mdat - Media Data 中存放的是 exif 信息,缩略图(320*240),和 HEVC 编码后的图片:
screenshot
HEIF 底层是默认 tiled,就是片状的有 512x512 个,由一个个小的图块,组成一张大图,这一特性有如下的优点:
加速解码
缩放加快(不用加载整张图片)
裁剪加快
HeifTile 和 SystemTile (CATiledLayer 等系统所提供的分块加载)是不一样的,但是两者结合会有很大的提升,所以在加载和处理特大图片时,性能有大幅度的提升。
HEIF 所采用的熵编码也和 JPEG 不一样,JPEG 是用的霍夫曼编码(Huffman),而 HEIF 使用的是基于上下文的自适应二进制算术编码(CABAC),编码的是数据量更小且更快。
兼容性
每当一个新的技术推进至工业化,兼容性无疑是最重要的考量点。像 HEIF 这样的图片格式,并不像 JPEG / PNG 等已经被广泛应用和适配了,估计在 Apple 推出之前,大部分开发者和我一样应该都是不知道的。目前工业化的体系内,对 HEIF 几乎是不兼容,Windows 上无法打开 HEIF 文件,10.13 前的 macOS 也无法打开。苹果在推行这一技术的时候,在内部也是做了很多兼容工作的。
对一般用户
一般情况下,用户是对这个格式无感知的,因为只有在新款支持硬解码的 iOS 手机内部是以 heif & hevc 格式来存储照片和视频的,而在用户通过 Airdrop 或者数据线传送到电脑上的时候,对不兼容的设备会自动转换到 JPEG 的格式。所以也不会影响你使用微信,微博等软件。
不过在苹果内部的 app 中,基本都已经用上了这一技术,如照片,FaceTime 等应用。意味着以后同等储存空间能存放更多的照片和视频,同时 FaceTime 也能节省更多了流量,相同网络情况下,FaceTime 也会更加清晰。
通过在 设置 -> 照片 选项中可以设置传到 MAC 或者 PC 上时保持 HEIF 格式。
screenshot
支持的设备
HEIF 图片:
编码:
硬件:**A10 **及以上芯片 iOS 设备。即只有 iPhone7 及以上是支持 HEIF 的(有双摄像头,才带有深度信息)。
软件:iOS11,运行 macOS 系统的 Mac 设备。
解码:
硬件:A9 及以上芯片 iOS 设备(iPhone6s),配备 6 代及以上 Inter Core 处理(Skylake)。
软件:iOS11 和 macOS 支持软解码,但是 iOS11 锁版本代表最低只支持到 5s
HECV 视频:
视频分为 **8 位 / 10 位 两种模式。基本和图片编解码保持一致,唯一有区别的是 10 **位硬解码要求是 7 代 Intel 处理器。
总结一下,就是升到 iOS 11 之后,你的设备就能解析新格式的图片和视频,软硬解码的区别就是,硬解码会更快并更省电。
对开发者
上层框架 PhotoKit
对用户的几乎无感知切换的前提,肯定是上层 API 没有变化,调整的是最底层的 API,所以一般开发者使用上层的框架时,如 PhotoKIt 是不受影响的,不同格式的图片都抽象为了同一个对象。而且对图片增加滤镜和视频的调整修改,最后都会渲染为 JPEG 和 H.264。在这一级别的 API 是接触不到图片文件格式转换所带来的变化的。
下层框架 Image I/O
Image I/O 中可以直接读写 .heic 文件。 iOS 11 中 Image I/O 能够直接读取 heif 的文件,包括读取 exif,xmp 等信息。
let inputURL = URL(fileURLWithPath:Bundle.main.resourcePath! + "/IMG_0513.HEIC") let source = CGImageSourceCreateWithURL(inputURL as CFURL, nil) let image = CGImageSourceCreateImageAtIndex(source!, 0, nil)
let options = [kCGImageSourceCreateThumbnailFromImageIfAbsent as String: true, kCGImageSourceThumbnailMaxPixelSize as String: 320] as [String: Any] let thumb = CGImageSourceCreateThumbnailAtIndex(source!, 0, options as CFDictionary)
guard let cImage = image else { print("not support heic") return; }
当然也可以选择把 CGImage 写入到 HEIC 格式,虽然这样可以节约更多的存储空间,实际使用的时候还是要注意转换操作。
let url = URL(fileURLWithPath: "/tmp/output.heic") guard let destination = CGImageDestinationCreateWithURL(url as CFURL,AVFileType.heic as CFString, 1, nil) else{ fatalError("unable to create CGImageDestination") } CGImageDestinationAddImage(imageDestination, image, nil) CGImageDestinationFinalize(imageDestination)
Apple 提供的很多 API ,供开发者检测设备是否支持新的格式。提供了两种推荐的工作场景事例。
社交网络
在社交软件中,涉及到图片分享之类的内容,是无法确定接受者是否能支持新的格式的, Apple 的策略是建议都进行转换到 JPEG 的操作,比如发送邮件,或者通过 extension 分享的时候,传入其他 app 中的时候,都已经进行了转换了。
p2p 场景
在该场景中,设备和设备间的直接连接,首先把接受者的支持格式告知发送者,然后发送者根据兼容的情况,选择 HEIF 或者 JPEG。比如 AirDrop 。
同时诸如 SDWebImage 目前也正在做对 heif 的兼容。
跨平台
大部分的播放器已经支持了 HEVC 即 H.265 的编码器,但是针对 HEIF 的图片兼容性还是相对较差的。 目前有的是 Nokia 提供了一个 C++ 的读写库,通过该库,支持把 HEIF 的图片的解码到 HEVC 的编码数据。
Android 兼容性:
依赖 Nokia 的库,目前只能通过 CPU 软解。
据说 LG 正在研发支持硬解的手机。(raddit)
网页兼容性:
Nokia 提供 JS 库。
Windows 兼容性:
目前也没有能直接打开 HEIF 文件的应用。
对比
常常拿来与 HEVC 来做对比的是 VP9。两者的性能相近,但是 VP9 是开源的,而 HEVC 是需要专利费的( $2 每设备)。 目前暂时没有通过 VP9 进行编码的图片,所以这里主要对比的是 webp 就是通过 VP8 进行编码的图片。 webp WebP目前支持桌面上的Chrome和Opera浏览器,手机支持仅限于原生的Android浏览器、Android系统上的Chrome浏览器、Opera Mini浏览器。
WebP 有损支持
Google Chrome (desktop) 17+ Google Chrome for Android version 25+ Opera 11.10+ Native web browser, Android 4.0+ (ICS) WebP 有损 / 无损 / 透明支持
Google Chrome (desktop) 23+ Google Chrome for Android version 25+ Opera 12.10+ Native web browser, Android 4.2+ (JB-MR1) Pale Moon 26+ WebP 动画支持
Google Chrome (desktop and Android) 32+ Opera 19+
下面是几个关键技术点的对比,可见 HEIF 功能是最强大的。
压缩效率
下面的数据均是官方提出:
Webp 同等质量下,比 JPEG 图像小 25-34%。
JPEG 平均需要比 HEVC 多 139% 的比特率,意味着同等质量下,JPEG 的大小是 HEVC 的 2.39 倍!
把两个的参考标准统一一下:
Webp 比 JPEG 小 25-34% HEVC 比 JPEG 小 58%
但是在我自己的本地的测试中,测试了五组图片。webp 以 80 的质量进行压缩,hevc 以 crf 18 (视觉无损)压缩,同时增加一个 320x240 的缩略图。
自己进行 HEIF 转码的流程是,将图片通过 ffmpeg 编码到 H.265,再通过 Nokia 的库转成 HEIC 文件(Heif)。
//生成主图像编码 ffmpeg -i $1 -crf 18 -preset slower -pix_fmt yuv420p -f hevc bitstream.265 //生成缩略图编码 ffmpeg -i $1 -vf scale=320:240 -crf 28 -preset slower -pix_fmt yuv420p -f hevc bitstream.thumb.265 //调用 Nokia 的工具 ./writerapp config.json
除了一组特大图的表现不一样外,其他几组图片相比, heif 确实比 webp 压缩效率高 10-20%。
毕竟和 HEVC 对标的技术应该是 VP9,所以 heif 能够领先也是情理之中。
解码的消耗测试
主要测试设备是 iPhone 6s Plus 系统 iOS 11,用的解码方法 webp 为 Google 提供的库,hevc 和 jpeg 用的是 CGImageSource 来解码。 测试用图片还是之前的五组图片,同时对 JPEG 进行了一些压缩,测试了 5 组平均值(去一个最高,去一个最低),估计加载的时候存在缓存,所以第一次读图片数据的时候耗时较大。
第一组用第一张 7.7 mb 的 jpeg 压缩后大小 1.1mb jpeg 7.43 2.77 1.46 1.9 2.14 2.00 【占用率 6 %】 hevc 41.0 3.45 3.35 2.62 2.92 2.66 【占用率 6 %】 webp 216.6 216.8 217.5 261.9 195.2 【占用率 21 %】
第二组用第三张 1.8 mb 的 tff,转换成 jpeg 之后大小为** 815 KB。** hevc 58.4 3.1 2.8 1.13 2.65 2.82 2.02 【 2% 】 jpeg 65.3 2.67 2.76 2.73 2.69 2.87 1.34 【 2% 】 webp 130.2 110.9 117.5 114.8 120.9 112.8 124.84 【 12% 】
第三组用的最后一张 243KB PNG ,转换成 JPEG 之后大小为 43 KB。
hevc 47 3.24 2.7 3.21 2.63 1.74 【 1 %】 jpeg 16 6.70 6.97 4.46 7.2 6.76 7.08 【 1% 】 webp 20.7 20.2 12.5 22.0 19.3 19.4 【 2 % 】
测试结果由于硬解码的支持,jpeg 和 hevc 解码速度和 CPU 占用率都比 webp 快和小很多,jpeg 和 hevc 不相伯仲,可见苹果内部对其优化也相当到位,才把它放到 iOS 11 中。
总结
相比 JPEG 来比很强大,毕竟是下一代技术,但是兼容性可想而知,在“最大效率”和“最大兼容”两者中间还是要根据使用场景进行权衡,目前的优势也只有最新的 iPhone 能体验到,但不久的将来可能所有的手机都去支持照片深度,动态照片,更广的色域等。HEVC 也许和推行 H.264 一样,逐渐成为了工业界的标准,但又可能和推行 acc 的处境一样,只是成为了苹果的标准而已,终究还是要看市场的反应了。
6. NoMTAinstalleddiscardingoutput是什么意思啊该如何处理?
等待缓存更新,或者重新加载No MTA installed discarding output 没有安装输出MTA丢弃【MTA】报文传送代理【installed】v.安置;安装( install的过去式和过去分词 );安顿;使…正式就职e.g:The instrument has been installed. 仪器已经装置好了【discarding】v.丢弃,抛弃( discard的现在分词 );不再使用e.g:Read the manufacturer's guidelines before discarding the box. 在丢掉盒子前先看一下制造商的使用说明。【output】n.输出;产量;作品;[计]输出信号vt.输出e.g:You run the software, you look at the output, you make modifications. 运行该软件,查看输出结果并进行修正。
7. vray5update21怎么设置渲染参数?
一、vray测试渲染参数设置:
1、公用下修改输出大小,测试渲染阶段不宜过大,图像纵横比根据自己的构图需要而设定,记得要锁定纵横比。
2、Vray(基项)—全局开关—关闭默认灯光,图像采样器类型用固定,不勾选抗锯齿过滤器。
3、环境下—打开全局照明环境(天光)覆盖。
4、间接照明(GI)—开,首次反弹引擎:发光(贴)图,二次反弹引擎:灯光缓存,发光贴图—当前预设—非常低;半球细分:20。
灯光缓存:细分:200,进程数与你的cpu核心数一致(不是必设)
5、设置—系统下最大树形深度:90(不设置影响也不是很大),“预置”,每次渲染都要重新设置一次是不是很麻烦,其实我们可以把这些参数保存一下,以后再进行测试渲染,就可以调用了。
6、在配置文件处输入预设名称,点击保存,需要用的时候在预置窗口点击对应的预置文件点击“加载”就可以了。
7、其实渲染过程中,光子图(GI)的渲染占了大多数时间,而我们需要不断的测试渲染,这样就浪费了大量的时间。此时我们可以将上一次渲染的光子图(vrmap)保存下来,并加载使用,这样就节省了大量的时间。
先将进行一次渲染,将发光贴图、灯光缓存的光子图分别保存,后将模式修改为“从文件”,浏览加载刚刚保存的vrmap。
二、出图的参数设置:
1、贴缓冲区:勾选“启动内置贴出缓冲区”;分辨率3200*2400,也可以根据个人情况选择。
2、类型:自适应确定性蒙特卡洛;抗锯齿过滤器:Mitchell-Netravali;全局照片环境下面打钩。
3、勾选前置输出。
4、打开间接照明;二次反弹下面的全局照明引擎改为灯光缓存。
5、发光图:中;半球细分:50;插值采样:30。
6、灯光缓存:细分1800。
参数设置完毕。
本站涵盖的内容、图片、视频等数据系网络收集,部分未能与原作者取得联系。若涉及版权问题,请联系我们删除!联系邮箱:ynstorm@foxmail.com 谢谢支持!
1. outputcache,au设备类型mme和asio的区别?
AU设备类型MME(Multimedia Extension)和ASIO(Audio Stream Input/Output)是两种不同的音频设备类型,主要区别如下:
1. 支持平台:MME是Windows操作系统的标准音频设备,而ASIO主要是用于专业音乐软件中的音频接口,如Ableton Live、Cubase等。
2. 低延迟:ASIO支持更低的音频延迟,因此更适用于需要实时处理音频的应用程序,如音频录制和混音。而MME的音频延迟相对较高,适用于一些普通的音频的播放。
3. 多路复用:ASIO支持多路音频输入/输出,因此可以同时处理多个音频信号,而MME只支持单一的音频输入/输出。
4. 音频质量:ASIO支持更高音频质量和精度,主要是因为它避免了音频数据的重复缓存和处理,使音频采集和输出更加准确。而MME则受到音频缓存和处理的限制,因此可能会出现一些音频失真和低音质问题。
综上所述,MME适用于一些普通的音频播放应用,而ASIO则更适用于需要处理音频信号的专业音乐应用程序。ASIO支持更高音频质量和精度,并且支持多路音频输入/输出和更低的音频延迟。
2. Java程序员如何拿到50w的年薪?
在这个IT系统动辄就是上亿流量的时代,Java作为大数据时代应用最广泛的语言,诞生了一批又一批的新技术,包括HBase、Hadoop、MQ、Netty、SpringCloud等等 。
一些独角兽公司以及腾讯、阿里、百度、网易等知名大厂对Java人才的需求量连年升级,有2年工作经验的优秀程序员能轻松达到30w+的水平,但与此同时对人才的要求也越来越高。
很多学生都会疑惑,同样是程序员吗,同样是学的Java,同样掌握了相应的技术,为什么自己就不能拿到BAT企业的Offer,不能拿到50W+的年薪?自己和年薪50W+的程序员差距在哪里?
二、工作效率
一个程序员每天写出来的有效代码(刨去自动生成和复制粘贴),大约就100~200行。我们的时间还耗费在需求讨论、思路整理、代码编译、提交、部署测试多个环节,优秀的工程师总能想到很多办法来提升自己的工作效率,所以一线互联网公司架构师,都会配置“效能平台(如阿里的云效平台)”来通过一整套的CI/CD方案来提高研发效率。
三、解决问题的能力
编程一个脑力活,如果把它做成了体力活,这就代表是时候改变一下了。现在的框架封装越来越便捷,通过springboot刚毕业都能把web开发玩的666,通过netty的相关封装,一年新手也能写出来性能不错的网络程序。架构师和普通开发的区别不在于会使用多少新潮的框架或者api,而在于对底层原理了解的程度。如果用的开源框架在生产环境出现问题,出面解决的肯定是年薪50W+的架构师。话说,来看看自己对spring框架原理知多少?
四、学习能力
Java往深往上走,下到C/C++,上到中间件、架构方式,按照IT行业技术发展的速度,不被掉队那就得不断的学习。普通程序员还在纠结业务代码怎么实现,技术领导们每天的工作就是研究各种高并发/新框架/分布式这些东西,慢慢下去差距能不大嘛,只有用很快的速度,掌握必备知识,才有一搏之力!
五、软技能
多少人努力进入大厂,但终究默默无闻进,默默无闻的出来,BAT等一线公司数十万技术人,你知道多少大牛,记住的有多少呢?平台重要,更重要的是,能在平台发展到什么程度。每个公司都有他的管理尿性,有它的企业氛围,软技能决定了能到什么职位!看到差距,才知道哪里需要改进,有了目标,更有学习动力。技术人成长不能再单打独斗,要学会与人交流,学会将别人的知识为自己所用。预祝每一位小伙伴都能拿到自己满意的工资待遇,不负韶华!
六、最火的语言还是Java?
下面是2017年7月编程语言排行榜 Top 10榜单:
Java语言在2016-2017依然占据榜首。Java作为最受欢迎的语言,在编程语言高薪行列里依然名列前茅,无疑是想进入互联网行业,拿高薪的最佳选择。
光有技术还不够
当然除了技术过关以外,还该有Java领域有丰富的经验,独到的见解。能够从前台到后台独立的搭建完成,良好的沟通能力及团队协作能力等。
一般情况下,就算不去大公司,很多小公司的技术总监年薪上也差不了多少呢。当然这些都需要一些时间沉淀,没办法速成。
七、Java程序员的职业发展有几个选择
Java程序员的职业发展有几个选择:专注于技术,成为技术专家;转型到技术型销售、技术支持等;随着技术成长,从技术性管理到高级管理。
这归根结底,都是以Java技术为基础。在扎实的技术基础上(高级软件工程师),如果有着比较强的抽象设计能力,又打算专注于技术开发,架构师是个好的选择;如果性格更适合做管理,情商能力突出,技术型管理应该是下一步的方向。这种发展规划属于‘瘸腿走路’的类型,各有侧重主攻发展的职业方向,也是目前一般程序员普遍采用的发展路线规划。
八、职业规划
已经工作两年以上的程序员可以有几种基本的职业规划:技术专家、软件架构师、实施顾问或销售。其中程序员最主要的发展方向是资深技术专家,无论是 Java、.NET还是数据库领域,都要首先成为专家,然后才可能继续发展为架构师。
尽管架构师的职位可以工作一辈子,待遇也非常好,对于科班出身的程序 员最为适合,但这种工作职位非常有限,因为在中国目前的IT行业,架构师需要的条件比较复杂,而且需求量也比较少,这也是我国软件行业走向国际并有待成熟 的因素之一。
九、管理能力和专业知识技能
目前国内缺乏的是管理能力和专业知识技能并驾齐驱的IT人才。特别是懂得运用目标管理技术的项目管理人才,既掌握核心技术又有全局掌控能力的项目管 理人才,非常难找。我建议对于大多数人来说,首先是要专,只有在技术方向上做得比较深入,才能适当做工作调整,把自己转变为某个领域的专家。
根据自己的情况,决定自己做Java架构师还是高级的顾问销售,另外一部分人可能就会走向管理,这和个人性格与情商有关。软件优秀的管理者都必须懂得技术,掌握核心技能的人才向管理岗位转型有很大的优势,因而提升管理能力成了转型的关键。
十、总之,Java程序员要根据本身的基本素质、技术能力选择开发层次,由低到高,逐步发展。也是一名程序员本身具备相应素质的循序渐进的发展轨迹,包括很强的技术背景和综合管理才能等素养,这也就是所谓‘两条腿走路’的职业规划发展路线。
3. 3dmax怎么设置渲染模板?
在3ds Max中设置渲染模板需要按照以下步骤进行:
1. 打开3ds Max软件并打开您的场景。
2. 在顶部菜单栏中选择“渲染(Render)”选项。
3. 在下拉菜单中选择“渲染设置(Render Setup)”。
4. 渲染设置窗口会弹出。在左侧的菜单中,选择“渲染输出(Render Output)”选项。
5. 在右侧的面板中,您可以进行以下设置:
- 输出大小(Output Size):设置渲染图像的分辨率。您可以手动输入宽度和高度,或者选择预设的常用分辨率。
- 文件输出路径(File Output Path):设置渲染图像保存的路径和文件名。点击右侧的“浏览(Browse)”按钮,选择合适的文件夹和文件名。
- 输出格式(File Format):选择您希望渲染输出为的图像格式,如JPEG、PNG、BMP等。
- 帧范围(Frame Range):设置您希望渲染的帧范围。您可以选择完整序列或特定帧范围。
- 其他设置(Additional Render Output):根据需要,您可以设置其他输出参数,如镜头效果、Alpha通道、多重过滤等。
6. 完成设置后,点击窗口底部的“确定(OK)”按钮。
现在,当您渲染场景时,3ds Max将使用您设置的渲染模板来生成输出图像并保存到指定的文件路径中。
请注意,以上步骤仅适用于3ds Max软件版本2022及更早版本。如果您使用的是不同版本的3ds Max或插件,请参考相关文档或该特定版本的指南以获取准确的操作步骤。
4. 最近在学习pyspark?
Spark提供了一个Python_Shell,即pyspark,从而可以以交互的方式使用Python编写Spark程序。
有关Spark的基本架构介绍参考http://blog.csdn.net/cymy001/article/details/78483614;
有关Pyspark的环境配置参考http://blog.csdn.net/cymy001/article/details/78430892。
pyspark里最核心的模块是SparkContext(简称sc),最重要的数据载体是RDD。RDD就像一个NumPy array或者一个Pandas Series,可以视作一个有序的item集合。只不过这些item并不存在driver端的内存里,而是被分割成很多个partitions,每个partition的数据存在集群的executor的内存中。
引入Python中pyspark工作模块
import pyspark
from pyspark import SparkContext as sc
from pyspark import SparkConf
conf=SparkConf().setAppName("miniProject").setMaster("local[*]")
sc=SparkContext.getOrCreate(conf)
#任何Spark程序都是SparkContext开始的,SparkContext的初始化需要一个SparkConf对象,SparkConf包含了Spark集群配置的各种参数(比如主节点的URL)。初始化后,就可以使用SparkContext对象所包含的各种方法来创建和操作RDD和共享变量。Spark shell会自动初始化一个SparkContext(在Scala和Python下可以,但不支持Java)。
#getOrCreate表明可以视情况新建session或利用已有的session
1
2
3
4
5
6
7
SparkSession是Spark 2.0引入的新概念。SparkSession为用户提供了统一的切入点,来让用户学习spark的各项功能。 在spark的早期版本中,SparkContext是spark的主要切入点,由于RDD是主要的API,我们通过sparkcontext来创建和操作RDD。对于每个其他的API,我们需要使用不同的context。例如,对于Streming,我们需要使用StreamingContext;对于sql,使用sqlContext;对于hive,使用hiveContext。但是随着DataSet和DataFrame的API逐渐成为标准的API,就需要为他们建立接入点。所以在spark2.0中,引入SparkSession作为DataSet和DataFrame API的切入点。SparkSession实质上是SQLContext和HiveContext的组合(未来可能还会加上StreamingContext),所以在SQLContext和HiveContext上可用的API在SparkSession上同样是可以使用的。SparkSession内部封装了SparkContext,所以计算实际上是由SparkContext完成的。
初始化RDD的方法
(1)本地内存中已经有一份序列数据(比如python的list),可以通过sc.parallelize去初始化一个RDD。当执行这个操作以后,list中的元素将被自动分块(partitioned),并且把每一块送到集群上的不同机器上。
import pyspark
from pyspark import SparkContext as sc
from pyspark import SparkConf
conf=SparkConf().setAppName("miniProject").setMaster("local[*]")
sc=SparkContext.getOrCreate(conf)
#(a)利用list创建一个RDD;使用sc.parallelize可以把Python list,NumPy array或者Pandas Series,Pandas DataFrame转成Spark RDD。
rdd = sc.parallelize([1,2,3,4,5])
rdd
#Output:ParallelCollectionRDD[0] at parallelize at PythonRDD.scala:480
#(b)getNumPartitions()方法查看list被分成了几部分
rdd.getNumPartitions()
#Output:4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#(c)glom().collect()查看分区状况
rdd.glom().collect()
#Output:[[1], [2], [3], [4, 5]]
1
2
3
在这个例子中,是一个4-core的CPU笔记本;Spark创建了4个executor,然后把数据分成4个块。colloect()方法很危险,数据量上BT文件读入会爆掉内存……
(2)创建RDD的另一个方法是直接把文本读到RDD。文本的每一行都会被当做一个item,不过需要注意的一点是,Spark一般默认给定的路径是指向HDFS的,如果要从本地读取文件的话,给一个file://开头(windows下是以file:\\开头)的全局路径。
import pyspark
from pyspark import SparkContext as sc
from pyspark import SparkConf
conf=SparkConf().setAppName("miniProject").setMaster("local[*]")
sc=SparkContext.getOrCreate(conf)
#(a)记录当前pyspark工作环境位置
import os
cwd=os.getcwd()
cwd
#Output:'C:\\Users\\Yu\\0JulyLearn\\5weekhadoopspark'
#(b)要读入的文件的全路径
rdd=sc.textFile("file:\\\\\\" + cwd + "\\names\yob1880.txt")
rdd
#Output:file:\\\C:\Users\Yu\0JulyLearn\5weekhadoopspark\names\yob1880.txt MapPartitionsRDD[3] at textFile at NativeMethodAccessorImpl.java:0
#(c)first()方法取读入的rdd数据第一个item
rdd.first()
#Output:'Mary,F,7065'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
甚至可以sc.wholeTextFiles读入整个文件夹的所有文件。但是要特别注意,这种读法,RDD中的每个item实际上是一个形如(文件名,文件所有内容)的元组。读入整个文件夹的所有文件。
import pyspark
from pyspark import SparkContext as sc
from pyspark import SparkConf
conf=SparkConf().setAppName("miniProject").setMaster("local[*]")
sc=SparkContext.getOrCreate(conf)
#记录当前pyspark工作环境位置
import os
cwd=os.getcwd()
cwd
#Output:'C:\\Users\\Yu\\0JulyLearn\\5weekhadoopspark'
rdd = sc.wholeTextFiles("file:\\\\\\" + cwd + "\\names\yob1880.txt")
rdd
#Output:org.apache.spark.api.java.JavaPairRDD@12bcc15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
rdd.first()
Output:
('file:/C:/Users/Yu/0JulyLearn/5weekhadoopspark/names/yob1880.txt',
1
2
3
4
5
其余初始化RDD的方法,包括:HDFS上的文件,Hive中的数据库与表,Spark SQL得到的结果。这里暂时不做介绍。
RDD Transformation
(1)RDDs可以进行一系列的变换得到新的RDD,有点类似列表推导式的操作,先给出一些RDD上最常用到的transformation:
map() 对RDD的每一个item都执行同一个操作
flatMap() 对RDD中的item执行同一个操作以后得到一个list,然后以平铺的方式把这些list里所有的结果组成新的list
filter() 筛选出来满足条件的item
distinct() 对RDD中的item去重
sample() 从RDD中的item中采样一部分出来,有放回或者无放回
sortBy() 对RDD中的item进行排序
1
2
3
4
5
6
如果想看操作后的结果,可以用一个叫做collect()的action把所有的item转成一个Python list。数据量大时,collect()很危险……
import pyspark
from pyspark import SparkContext as sc
from pyspark import SparkConf
conf=SparkConf().setAppName("miniProject").setMaster("local[*]")
sc=SparkContext.getOrCreate(conf)
numbersRDD = sc.parallelize(range(1,10+1))
print(numbersRDD.collect())
#map()对RDD的每一个item都执行同一个操作
squaresRDD = numbersRDD.map(lambda x: x**2) # Square every number
print(squaresRDD.collect())
#filter()筛选出来满足条件的item
filteredRDD = numbersRDD.filter(lambda x: x % 2 == 0) # Only the evens
print(filteredRDD.collect())
#Output:
#[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
#[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
#[2, 4, 6, 8, 10]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import pyspark
from pyspark import SparkContext as sc
from pyspark import SparkConf
conf=SparkConf().setAppName("miniProject").setMaster("local[*]")
sc=SparkContext.getOrCreate(conf)
#flatMap() 对RDD中的item执行同一个操作以后得到一个list,然后以平铺的方式把这些list里所有的结果组成新的list
sentencesRDD=sc.parallelize(['Hello world','My name is Patrick'])
wordsRDD=sentencesRDD.flatMap(lambda sentence: sentence.split(" "))
print(wordsRDD.collect())
print(wordsRDD.count())
#Output:
#['Hello', 'world', 'My', 'name', 'is', 'Patrick']
#6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
对比一下:
这里如果使用map的结果是[[‘Hello’, ‘world’], [‘My’, ‘name’, ‘is’, ‘Patrick’]],
使用flatmap的结果是全部展开[‘Hello’, ‘world’, ‘My’, ‘name’, ‘is’, ‘Patrick’]。
flatmap即对应Python里的如下操作:
l = ['Hello world', 'My name is Patrick']
ll = []
for sentence in l:
ll = ll + sentence.split(" ") #+号作用,two list拼接
ll
1
2
3
4
5
(2)最开始列出的各个Transformation,可以一个接一个地串联使用,比如:
import pyspark
from pyspark import SparkContext as sc
from pyspark import SparkConf
conf=SparkConf().setAppName("miniProject").setMaster("local[*]")
sc=SparkContext.getOrCreate(conf)
def doubleIfOdd(x):
if x % 2 == 1:
return 2 * x
else:
return x
numbersRDD = sc.parallelize(range(1,10+1))
resultRDD = (numbersRDD
.map(doubleIfOdd) #map,filter,distinct()
.filter(lambda x: x > 6)
.distinct()) #distinct()对RDD中的item去重
resultRDD.collect()
#Output:[8, 10, 18, 14]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
(3)当遇到更复杂的结构,比如被称作“pair RDDs”的以元组形式组织的k-v对(key, value),Spark中针对这种item结构的数据,定义了一些transform和action:
reduceByKey(): 对所有有着相同key的items执行reduce操作
groupByKey(): 返回类似(key, listOfValues)元组的RDD,后面的value List 是同一个key下面的
sortByKey(): 按照key排序
countByKey(): 按照key去对item个数进行统计
collectAsMap(): 和collect有些类似,但是返回的是k-v的字典
1
2
3
4
5
import pyspark
from pyspark import SparkContext as sc
from pyspark import SparkConf
conf=SparkConf().setAppName("miniProject").setMaster("local[*]")
sc=SparkContext.getOrCreate(conf)
rdd=sc.parallelize(["Hello hello", "Hello New York", "York says hello"])
resultRDD=(rdd
.flatMap(lambda sentence:sentence.split(" "))
.map(lambda word:word.lower())
.map(lambda word:(word, 1)) #将word映射成(word,1)
.reduceByKey(lambda x, y: x + y)) #reduceByKey对所有有着相同key的items执行reduce操作
resultRDD.collect()
#Output:[('hello', 4), ('york', 2), ('says', 1), ('new', 1)]
result = resultRDD.collectAsMap() #collectAsMap类似collect,以k-v字典的形式返回
result
#Output:{'hello': 4, 'new': 1, 'says': 1, 'york': 2}
resultRDD.sortByKey(ascending=True).take(2) #sortByKey按键排序
#Output:[('hello', 4), ('new', 1)]
#取出现频次最高的2个词
print(resultRDD
.sortBy(lambda x: x[1], ascending=False)
.take(2))
#Output:[('hello', 4), ('york', 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
RDD间的操作
(1)如果有2个RDD,可以通过下面这些操作,对它们进行集合运算得到1个新的RDD
rdd1.union(rdd2): 所有rdd1和rdd2中的item组合(并集)
rdd1.intersection(rdd2): rdd1 和 rdd2的交集
rdd1.substract(rdd2): 所有在rdd1中但不在rdd2中的item(差集)
rdd1.cartesian(rdd2): rdd1 和 rdd2中所有的元素笛卡尔乘积(正交和)
1
2
3
4
import pyspark
from pyspark import SparkContext as sc
from pyspark import SparkConf
conf=SparkConf().setAppName("miniProject").setMaster("local[*]")
sc=SparkContext.getOrCreate(conf)
#初始化两个RDD
numbersRDD = sc.parallelize([1,2,3])
moreNumbersRDD = sc.parallelize([2,3,4])
1
2
3
4
5
6
7
8
9
numbersRDD.union(moreNumbersRDD).collect() #union()取并集
#Output:[1, 2, 3, 2, 3, 4]
numbersRDD.intersection(moreNumbersRDD).collect() #intersection()取交集
#Output:[2, 3]
numbersRDD.subtract(moreNumbersRDD).collect() #substract()取差集
#Output:[1]
numbersRDD.cartesian(moreNumbersRDD).collect() #cartesian()取笛卡尔积
#Output:[(1, 2), (1, 3), (1, 4), (2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4)]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(2)在给定2个RDD后,可以通过一个类似SQL的方式去join它们
import pyspark
from pyspark import SparkContext as sc
from pyspark import SparkConf
conf=SparkConf().setAppName("miniProject").setMaster("local[*]")
sc=SparkContext.getOrCreate(conf)
# Home of different people
homesRDD = sc.parallelize([
('Brussels', 'John'),
('Brussels', 'Jack'),
('Leuven', 'Jane'),
('Antwerp', 'Jill'),
])
# Quality of life index for various cities
lifeQualityRDD = sc.parallelize([
('Brussels', 10),
('Antwerp', 7),
('RestOfFlanders', 5),
])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
homesRDD.join(lifeQualityRDD).collect() #join
#Output:
#[('Antwerp', ('Jill', 7)),
# ('Brussels', ('John', 10)),
# ('Brussels', ('Jack', 10))]
homesRDD.leftOuterJoin(lifeQualityRDD).collect() #leftOuterJoin
#Output:
#[('Antwerp', ('Jill', 7)),
# ('Leuven', ('Jane', None)),
# ('Brussels', ('John', 10)),
# ('Brussels', ('Jack', 10))]
homesRDD.rightOuterJoin(lifeQualityRDD).collect() #rightOuterJoin
#Output:
#[('Antwerp', ('Jill', 7)),
# ('RestOfFlanders', (None, 5)),
# ('Brussels', ('John', 10)),
# ('Brussels', ('Jack', 10))]
homesRDD.cogroup(lifeQualityRDD).collect() #cogroup
#Output:
#[('Antwerp',
# (<pyspark.resultiterable.ResultIterable at 0x73d2d68>,
# <pyspark.resultiterable.ResultIterable at 0x73d2940>)),
# ('RestOfFlanders',
# (<pyspark.resultiterable.ResultIterable at 0x73d2828>,
# <pyspark.resultiterable.ResultIterable at 0x73d2b70>)),
# ('Leuven',
# (<pyspark.resultiterable.ResultIterable at 0x73d26a0>,
# <pyspark.resultiterable.ResultIterable at 0x7410a58>)),
# ('Brussels',
# (<pyspark.resultiterable.ResultIterable at 0x73d2b38>,
# <pyspark.resultiterable.ResultIterable at 0x74106a0>))]
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
# Oops! Those <ResultIterable>s are Spark's way of returning a list
# that we can walk over, without materializing the list.
# Let's materialize the lists to make the above more readable:
(homesRDD
.cogroup(lifeQualityRDD)
.map(lambda x:(x[0], (list(x[1][0]), list(x[1][1]))))
.collect())
#Output:
#[('Antwerp', (['Jill'], [7])),
# ('RestOfFlanders', ([], [5])),
# ('Leuven', (['Jane'], [])),
# ('Brussels', (['John', 'Jack'], [10]))]
1
2
3
4
5
6
7
8
9
10
11
12
13
惰性计算,actions方法
特别注意:Spark的一个核心概念是惰性计算。当你把一个RDD转换成另一个的时候,这个转换不会立即生效执行!!!Spark会把它先记在心里,等到真的有actions需要取转换结果时,才会重新组织transformations(因为可能有一连串的变换)。这样可以避免不必要的中间结果存储和通信。
常见的action如下,当它们出现的时候,表明需要执行上面定义过的transform了:
collect(): 计算所有的items并返回所有的结果到driver端,接着 collect()会以Python list的形式返回结果
first(): 和上面是类似的,不过只返回第1个item
take(n): 类似,但是返回n个item
count(): 计算RDD中item的个数
top(n): 返回头n个items,按照自然结果排序
reduce(): 对RDD中的items做聚合
1
2
3
4
5
6
import pyspark
from pyspark import SparkContext as sc
from pyspark import SparkConf
conf=SparkConf().setAppName("miniProject").setMaster("local[*]")
sc=SparkContext.getOrCreate(conf)
rdd = sc.parallelize(range(1,10+1))
rdd.reduce(lambda x, y: x + y) #reduce(): 对RDD中的items做聚合
#Output:55
1
2
3
4
5
6
7
8
9
10
reduce的原理:先在每个分区(partition)里完成reduce操作,然后再全局地进行reduce。
有时候需要重复用到某个transform序列得到的RDD结果。但是一遍遍重复计算显然是要开销的,所以我们可以通过一个叫做cache()的操作把它暂时地存储在内存中。缓存RDD结果对于重复迭代的操作非常有用,比如很多机器学习的算法,训练过程需要重复迭代。
import pyspark
from pyspark import SparkContext as sc
from pyspark import SparkConf
conf=SparkConf().setAppName("miniProject").setMaster("local[*]")
sc=SparkContext.getOrCreate(conf)
import numpy as np
numbersRDD = sc.parallelize(np.linspace(1.0, 10.0, 10))
squaresRDD = numbersRDD.map(lambda x: x**2)
squaresRDD.cache() # Preserve the actual items of this RDD in memory
avg = squaresRDD.reduce(lambda x, y: x + y) / squaresRDD.count()
print(avg)
#Output:38.5
5. HEVC图像格式你知道多少?
在升级 iOS 11 之后,iPhone 7 及更新的设备内的照片存储将不再用 JPEG 了,而采用了一种新的图片格式 HEIF(发音同 heef),在 iOS 中对应的文件后缀为 .heic ,其编码用的是 HEVC(这个发不了音,哈哈哈)格式,又称 H.265 (这个就很熟悉了 H.264 的下一代),同时视频也用 HEVC 作为编码器,对应的文件后缀还是 .mov 。
这里要注意他们俩的关系, HEIF 是图片格式,而 HEVC 是编码格式(类似 H.264,VP8),HEIF 是图片容器(类似于视频的 mkv,mp4 后缀),而用 HEVC 进行编码的 HEIF 图片就是后缀为 .heic 的图片,也是苹果主要使用的格式。
这两个都是很新的标准,分别在 2015 和 2013 年才被 ISO 批准。这篇文章主要介绍一下 HEIF 格式和与其他图片格式相比的优劣。
发展史如下:
screenshot
什么是 HEIF?
screenshot
这张图是 WWDC Session 511 的形容 Heif 的一句英文诗,JPEG 很大,但是 HEIF 和小。
HEIF 全称 High Efficiency Image Format (HEIF)。是由 Moving Picture Experts Group 制定的,存储图片和图片序列的格式。
相关的介绍位置这边可以看到 nokiatech.github.io/heif/ ,对的,你没有看错,是 Nokia 的技术人员们制定的。相比 JPEG ,PNG 等传统的图片格式来说, HEIF 可算是相当年轻了,但是这种格式相比 JPEG 等有很多的优点。
压缩比高,在相同的图片质量情况下,平比 JPEG 高两倍。
能增加如图片深度信息,透明通道等辅助图片。(苹果正是通过这个就能实现动态的调整图片的景深)。
depth
支持存放多张图片,类似相册和集合。(实现多重曝光的效果)
screenshot
支持多张图片实现 GIF 和 livePhoto 的动画效果。
无类似 JPEG 的最大像素限制。
支持透明像素。
分块加载机制。
支持缩略图。
在这个 Nokia 网站上可以看到相关的例子。
文件组成
在视频文件中,容器和编码是独立开的,比如 mp4,mkv 等格式是容器,而 H.264,VP8 等是编码。但是图像文件中,像 JPEG 就是混合在一起的(所以它不好用啊,哈哈哈哈),HEIF 就把容器和编码分开了,有用来存放单个或者多个图像的容器。
所以基于不同的编码器,会有不同的文件后缀。
Apple 设备中默认使用的都是 HEVC 的编码的 HEIF 格式。
在 Apple 所编码的 HEIF 的文件组成大致如下图,其 mdat - Media Data 中存放的是 exif 信息,缩略图(320*240),和 HEVC 编码后的图片:
screenshot
HEIF 底层是默认 tiled,就是片状的有 512x512 个,由一个个小的图块,组成一张大图,这一特性有如下的优点:
加速解码
缩放加快(不用加载整张图片)
裁剪加快
HeifTile 和 SystemTile (CATiledLayer 等系统所提供的分块加载)是不一样的,但是两者结合会有很大的提升,所以在加载和处理特大图片时,性能有大幅度的提升。
HEIF 所采用的熵编码也和 JPEG 不一样,JPEG 是用的霍夫曼编码(Huffman),而 HEIF 使用的是基于上下文的自适应二进制算术编码(CABAC),编码的是数据量更小且更快。
兼容性
每当一个新的技术推进至工业化,兼容性无疑是最重要的考量点。像 HEIF 这样的图片格式,并不像 JPEG / PNG 等已经被广泛应用和适配了,估计在 Apple 推出之前,大部分开发者和我一样应该都是不知道的。目前工业化的体系内,对 HEIF 几乎是不兼容,Windows 上无法打开 HEIF 文件,10.13 前的 macOS 也无法打开。苹果在推行这一技术的时候,在内部也是做了很多兼容工作的。
对一般用户
一般情况下,用户是对这个格式无感知的,因为只有在新款支持硬解码的 iOS 手机内部是以 heif & hevc 格式来存储照片和视频的,而在用户通过 Airdrop 或者数据线传送到电脑上的时候,对不兼容的设备会自动转换到 JPEG 的格式。所以也不会影响你使用微信,微博等软件。
不过在苹果内部的 app 中,基本都已经用上了这一技术,如照片,FaceTime 等应用。意味着以后同等储存空间能存放更多的照片和视频,同时 FaceTime 也能节省更多了流量,相同网络情况下,FaceTime 也会更加清晰。
通过在 设置 -> 照片 选项中可以设置传到 MAC 或者 PC 上时保持 HEIF 格式。
screenshot
支持的设备
HEIF 图片:
编码:
硬件:**A10 **及以上芯片 iOS 设备。即只有 iPhone7 及以上是支持 HEIF 的(有双摄像头,才带有深度信息)。
软件:iOS11,运行 macOS 系统的 Mac 设备。
解码:
硬件:A9 及以上芯片 iOS 设备(iPhone6s),配备 6 代及以上 Inter Core 处理(Skylake)。
软件:iOS11 和 macOS 支持软解码,但是 iOS11 锁版本代表最低只支持到 5s
HECV 视频:
视频分为 **8 位 / 10 位 两种模式。基本和图片编解码保持一致,唯一有区别的是 10 **位硬解码要求是 7 代 Intel 处理器。
总结一下,就是升到 iOS 11 之后,你的设备就能解析新格式的图片和视频,软硬解码的区别就是,硬解码会更快并更省电。
对开发者
上层框架 PhotoKit
对用户的几乎无感知切换的前提,肯定是上层 API 没有变化,调整的是最底层的 API,所以一般开发者使用上层的框架时,如 PhotoKIt 是不受影响的,不同格式的图片都抽象为了同一个对象。而且对图片增加滤镜和视频的调整修改,最后都会渲染为 JPEG 和 H.264。在这一级别的 API 是接触不到图片文件格式转换所带来的变化的。
下层框架 Image I/O
Image I/O 中可以直接读写 .heic 文件。 iOS 11 中 Image I/O 能够直接读取 heif 的文件,包括读取 exif,xmp 等信息。
let inputURL = URL(fileURLWithPath:Bundle.main.resourcePath! + "/IMG_0513.HEIC") let source = CGImageSourceCreateWithURL(inputURL as CFURL, nil) let image = CGImageSourceCreateImageAtIndex(source!, 0, nil)
let options = [kCGImageSourceCreateThumbnailFromImageIfAbsent as String: true, kCGImageSourceThumbnailMaxPixelSize as String: 320] as [String: Any] let thumb = CGImageSourceCreateThumbnailAtIndex(source!, 0, options as CFDictionary)
guard let cImage = image else { print("not support heic") return; }
当然也可以选择把 CGImage 写入到 HEIC 格式,虽然这样可以节约更多的存储空间,实际使用的时候还是要注意转换操作。
let url = URL(fileURLWithPath: "/tmp/output.heic") guard let destination = CGImageDestinationCreateWithURL(url as CFURL,AVFileType.heic as CFString, 1, nil) else{ fatalError("unable to create CGImageDestination") } CGImageDestinationAddImage(imageDestination, image, nil) CGImageDestinationFinalize(imageDestination)
Apple 提供的很多 API ,供开发者检测设备是否支持新的格式。提供了两种推荐的工作场景事例。
社交网络
在社交软件中,涉及到图片分享之类的内容,是无法确定接受者是否能支持新的格式的, Apple 的策略是建议都进行转换到 JPEG 的操作,比如发送邮件,或者通过 extension 分享的时候,传入其他 app 中的时候,都已经进行了转换了。
p2p 场景
在该场景中,设备和设备间的直接连接,首先把接受者的支持格式告知发送者,然后发送者根据兼容的情况,选择 HEIF 或者 JPEG。比如 AirDrop 。
同时诸如 SDWebImage 目前也正在做对 heif 的兼容。
跨平台
大部分的播放器已经支持了 HEVC 即 H.265 的编码器,但是针对 HEIF 的图片兼容性还是相对较差的。 目前有的是 Nokia 提供了一个 C++ 的读写库,通过该库,支持把 HEIF 的图片的解码到 HEVC 的编码数据。
Android 兼容性:
依赖 Nokia 的库,目前只能通过 CPU 软解。
据说 LG 正在研发支持硬解的手机。(raddit)
网页兼容性:
Nokia 提供 JS 库。
Windows 兼容性:
目前也没有能直接打开 HEIF 文件的应用。
对比
常常拿来与 HEVC 来做对比的是 VP9。两者的性能相近,但是 VP9 是开源的,而 HEVC 是需要专利费的( $2 每设备)。 目前暂时没有通过 VP9 进行编码的图片,所以这里主要对比的是 webp 就是通过 VP8 进行编码的图片。 webp WebP目前支持桌面上的Chrome和Opera浏览器,手机支持仅限于原生的Android浏览器、Android系统上的Chrome浏览器、Opera Mini浏览器。
WebP 有损支持
Google Chrome (desktop) 17+ Google Chrome for Android version 25+ Opera 11.10+ Native web browser, Android 4.0+ (ICS) WebP 有损 / 无损 / 透明支持
Google Chrome (desktop) 23+ Google Chrome for Android version 25+ Opera 12.10+ Native web browser, Android 4.2+ (JB-MR1) Pale Moon 26+ WebP 动画支持
Google Chrome (desktop and Android) 32+ Opera 19+
下面是几个关键技术点的对比,可见 HEIF 功能是最强大的。
压缩效率
下面的数据均是官方提出:
Webp 同等质量下,比 JPEG 图像小 25-34%。
JPEG 平均需要比 HEVC 多 139% 的比特率,意味着同等质量下,JPEG 的大小是 HEVC 的 2.39 倍!
把两个的参考标准统一一下:
Webp 比 JPEG 小 25-34% HEVC 比 JPEG 小 58%
但是在我自己的本地的测试中,测试了五组图片。webp 以 80 的质量进行压缩,hevc 以 crf 18 (视觉无损)压缩,同时增加一个 320x240 的缩略图。
自己进行 HEIF 转码的流程是,将图片通过 ffmpeg 编码到 H.265,再通过 Nokia 的库转成 HEIC 文件(Heif)。
//生成主图像编码 ffmpeg -i $1 -crf 18 -preset slower -pix_fmt yuv420p -f hevc bitstream.265 //生成缩略图编码 ffmpeg -i $1 -vf scale=320:240 -crf 28 -preset slower -pix_fmt yuv420p -f hevc bitstream.thumb.265 //调用 Nokia 的工具 ./writerapp config.json
除了一组特大图的表现不一样外,其他几组图片相比, heif 确实比 webp 压缩效率高 10-20%。
毕竟和 HEVC 对标的技术应该是 VP9,所以 heif 能够领先也是情理之中。
解码的消耗测试
主要测试设备是 iPhone 6s Plus 系统 iOS 11,用的解码方法 webp 为 Google 提供的库,hevc 和 jpeg 用的是 CGImageSource 来解码。 测试用图片还是之前的五组图片,同时对 JPEG 进行了一些压缩,测试了 5 组平均值(去一个最高,去一个最低),估计加载的时候存在缓存,所以第一次读图片数据的时候耗时较大。
第一组用第一张 7.7 mb 的 jpeg 压缩后大小 1.1mb jpeg 7.43 2.77 1.46 1.9 2.14 2.00 【占用率 6 %】 hevc 41.0 3.45 3.35 2.62 2.92 2.66 【占用率 6 %】 webp 216.6 216.8 217.5 261.9 195.2 【占用率 21 %】
第二组用第三张 1.8 mb 的 tff,转换成 jpeg 之后大小为** 815 KB。** hevc 58.4 3.1 2.8 1.13 2.65 2.82 2.02 【 2% 】 jpeg 65.3 2.67 2.76 2.73 2.69 2.87 1.34 【 2% 】 webp 130.2 110.9 117.5 114.8 120.9 112.8 124.84 【 12% 】
第三组用的最后一张 243KB PNG ,转换成 JPEG 之后大小为 43 KB。
hevc 47 3.24 2.7 3.21 2.63 1.74 【 1 %】 jpeg 16 6.70 6.97 4.46 7.2 6.76 7.08 【 1% 】 webp 20.7 20.2 12.5 22.0 19.3 19.4 【 2 % 】
测试结果由于硬解码的支持,jpeg 和 hevc 解码速度和 CPU 占用率都比 webp 快和小很多,jpeg 和 hevc 不相伯仲,可见苹果内部对其优化也相当到位,才把它放到 iOS 11 中。
总结
相比 JPEG 来比很强大,毕竟是下一代技术,但是兼容性可想而知,在“最大效率”和“最大兼容”两者中间还是要根据使用场景进行权衡,目前的优势也只有最新的 iPhone 能体验到,但不久的将来可能所有的手机都去支持照片深度,动态照片,更广的色域等。HEVC 也许和推行 H.264 一样,逐渐成为了工业界的标准,但又可能和推行 acc 的处境一样,只是成为了苹果的标准而已,终究还是要看市场的反应了。
6. NoMTAinstalleddiscardingoutput是什么意思啊该如何处理?
等待缓存更新,或者重新加载No MTA installed discarding output 没有安装输出MTA丢弃【MTA】报文传送代理【installed】v.安置;安装( install的过去式和过去分词 );安顿;使…正式就职e.g:The instrument has been installed. 仪器已经装置好了【discarding】v.丢弃,抛弃( discard的现在分词 );不再使用e.g:Read the manufacturer's guidelines before discarding the box. 在丢掉盒子前先看一下制造商的使用说明。【output】n.输出;产量;作品;[计]输出信号vt.输出e.g:You run the software, you look at the output, you make modifications. 运行该软件,查看输出结果并进行修正。
7. vray5update21怎么设置渲染参数?
一、vray测试渲染参数设置:
1、公用下修改输出大小,测试渲染阶段不宜过大,图像纵横比根据自己的构图需要而设定,记得要锁定纵横比。
2、Vray(基项)—全局开关—关闭默认灯光,图像采样器类型用固定,不勾选抗锯齿过滤器。
3、环境下—打开全局照明环境(天光)覆盖。
4、间接照明(GI)—开,首次反弹引擎:发光(贴)图,二次反弹引擎:灯光缓存,发光贴图—当前预设—非常低;半球细分:20。
灯光缓存:细分:200,进程数与你的cpu核心数一致(不是必设)
5、设置—系统下最大树形深度:90(不设置影响也不是很大),“预置”,每次渲染都要重新设置一次是不是很麻烦,其实我们可以把这些参数保存一下,以后再进行测试渲染,就可以调用了。
6、在配置文件处输入预设名称,点击保存,需要用的时候在预置窗口点击对应的预置文件点击“加载”就可以了。
7、其实渲染过程中,光子图(GI)的渲染占了大多数时间,而我们需要不断的测试渲染,这样就浪费了大量的时间。此时我们可以将上一次渲染的光子图(vrmap)保存下来,并加载使用,这样就节省了大量的时间。
先将进行一次渲染,将发光贴图、灯光缓存的光子图分别保存,后将模式修改为“从文件”,浏览加载刚刚保存的vrmap。
二、出图的参数设置:
1、贴缓冲区:勾选“启动内置贴出缓冲区”;分辨率3200*2400,也可以根据个人情况选择。
2、类型:自适应确定性蒙特卡洛;抗锯齿过滤器:Mitchell-Netravali;全局照片环境下面打钩。
3、勾选前置输出。
4、打开间接照明;二次反弹下面的全局照明引擎改为灯光缓存。
5、发光图:中;半球细分:50;插值采样:30。
6、灯光缓存:细分1800。
参数设置完毕。
本站涵盖的内容、图片、视频等数据系网络收集,部分未能与原作者取得联系。若涉及版权问题,请联系我们删除!联系邮箱:ynstorm@foxmail.com 谢谢支持!