作为MATLAB初学者,你猜我为什么要班门弄斧不自量力写教程?QAQ
这里面可能有代码优化、叙述不当等问题,望大家指正以及包涵。感谢!
文中图片由本人自己制作绘画或拍摄,部分代码由网络、课本、他人代码基础上修改,仅作学习交流用。
文章转载请注明出处。
事情是这样的,这学期MATLAB期末考试有一题,用PTB编一整个实验程序。程序框架在这里不做赘述。实验对刺激材料在屏幕上的呈现有严格要求,要求是黑底白字,在屏幕上不止要求比例,还要较为精确的绝对尺寸。具体尺寸要求如下:
我们知道,现在市面上的显示器大小不一,相对分辨率、绝对分辨率(dpi)都不一样,要想生成这样满足要求的刺激并不容易(我相信有大神很不屑于这个,但对我来说真的很难啊有木有)。作为一个初学者的教程,我们从最基础的东西讲起吧。
================废话铺垫篇================
在我讲教程之前,先介绍几个概念。
分辨率,指的是在位图和点阵中指这个位图点阵可被识别的程度。分为三种(其实后两种可以合并):
1、 相对分辨率(Resolution),也就是我们常说的像素,电子位图显示器、位图图片,都由像素组成。对心理学编程来说,在MATLAB里用的都是正方形像素。像素与像素构成一个点阵,根据上面的颜色、灰度等变化构成一个图像。通常来说,像素越多越细腻,图像越清晰。相对分辨率只代表了这个点阵像素的个数,与绝对长度没有直接关系。也就是说,一个1920*1080的屏幕,可以是一台5英寸手机屏幕,也可以是一台50寸的液晶电视,还可能是200寸的投影屏。
2、 绝对分辨率(dpi) 这里用比较常用的dpi来讲。Dpi,dots per inch,每英寸的点阵个数。这个就可以把点阵个数与绝对长度单位联系起来了——插一句,网上外行经常把信息技术里的"像素""分辨率""dpi"等概念混淆,就像把"内存"和"存储空间"混淆一样,都习惯了——同时,dpi在描述图片、文印、屏幕点阵过程中,数字越大则代表越清晰越细腻。
3、 PPI 如今的屏幕已经不止是"点"了,而是一个个有颜色、亮度、透明度的像素。因此在屏幕上我们通常称dpi是PPI(pixels per inch),每英寸的像素个数。嘛对于一个学心理的来说这个怎么叫应该无所谓吧(别打我)。
绝对长度、像素分辨率、PPI只要知道其中的两个,就可以求出第三个,对应关系是
长度(英寸)=像素个数/PPI
1 inch=25.4 mm
比如,1920*1080,180PPI的屏幕,其
宽度是1920 / 180 = 10.67 inch = 27.09 cm;
高度是1080 / 180 = 6 inch = 15.24 cm ;
其屏幕尺寸(对角线)是 ( 10.67^2 + 6^2 )^0.5 = 12.24 inch
一个十二寸的1080P屏幕。
在大多数的心理学试验中,特别是行为实验,只要求被试可以无压力识别刺激即可,对刺激本身的清晰度要求并不高。同时为了减少因为实验仪器计算压力过大而产生的时差,因此采用够用的低分辨率的屏幕,比如1024*768,甚至有些地方只用800*600。
=====================正题篇=====================
回到这个题目(终于回归正题),我们最先想到的,就是利用显示器的像素和PPI来达到目的,然后调整字号就可以啦!
教程到此结束,谢谢观看!
。
。
。
。
。
。
。
。
。
。
才怪嘞!还有一个问题别忽略了!
那就是
字体
(捂脸)
不同字体、粗细,都会影响切边,从而无法达到实验目的。
考虑上面各种因素后,MATLAB编程怎么没有程序呢?所以终于进入程序部分。
===================真の正题===================
代码是个枯燥的部分,基本就是给代码+解释说明+运行结果。
下面的部分除了截图外我尽量用MATLAB能够识别的格式,让需要的同学直接复制粘贴就能用。由于初学,我的编写习惯不是很好,如果有优化代码请多多指教。
第一步: 算出你的数据
之前铺垫了很多关于分辨率PPI的知识,现在就派上用场了。
本猴用的是ThinkPad X1tablet ,如果不了解的童鞋,先到村里(中关村在线)查配置。屏幕标称12英寸2K屏,PPI为216。
额当然你也可以自己算一下。量出来屏幕宽度正好是10英寸,PPI是216
然后就是位置计算啦,统一单位为我们熟知的厘米,那么PPI换算成PPC(pixels per centimeter,像素每厘米)只要除以2.54就行。
216 PPI = 216 pixels / 2.54 cm = 85.03937007874016… PPC
是吧,我说没法完全精确吧~
如果把数字外框看成矩形的话,那么我们可以把题目中的数字换算成像素(这个数值仅适配于PPI为216的屏幕)。
这一步都还好,细心的同学就会发现几个问题了:
如何保证数字顶在方格里面?
两个数字高宽比不一样啊,怎么办?
没事,我们开始一一解决。
第二步:顶格的数字素材
这段代码修改自2017年6月30日 @juno芊 的生成素材代码,主要修改了说明和优化了循环体。字体选用和字号设置都是她经过多次实践测试后得出的。
% 终于开始用matlab写程序了。经过我们的经验,很多字体并不能很好押在框内。我们最后选用了Arial字体,能够较好匹配,调整较少。 %% 生成所需的数字图片1-9 % 背景的颜色是黑色 % 文字的颜色是白色,字体是Arial,文字的字号大小为默认 % 文字尽可能顶着四边,不留缝隙,减小误差 % 数字1为特殊,不做要求 % 文字在画布的居中位置 clear;clc; %日常清理 mkdir image %创建一个图片文件夹叫image。不做描述它会创建在这个m文件所在目录 for i =1:9 %循环9次,每次生成一个数字 NumName(i) = num2str(i); %每次生成的文字内容即循环的序号 %第1次生成图片1,第2次生成图片2,以此类推。但内容必须是字符 figure; % 打开一个绘图窗口 set(gcf,'color','Black',... 'units','pixels',... 'position',[100 100 360 540]); %让figure背景变为黑色。其中gcf表示get current figure,指代当前窗口。 %窗口大小是360*540,算好的。 t_text = text(0.5,0.5,NumName(i)); %建立一个text,写上"1"-'9', set(t_text,'FontName','Arial',... %设置我们精挑细选的字体Arial,居中正好 'FontSize',560,... %设置文字大小560,也是算好的。 'Color',[1 1 1],'HorizontalAlignment', 'center','VerticalAlignment','middle') %设置文字颜色为白色,位置居中 axis off %关闭坐标轴 NewNumName = sprintf('%s%s%s','pic_',NumName(i),'.jpg'); %临时新名字,用于指代文件名(带后缀) f=getframe(gcf); %别忘了把现在的窗口"截图"变成图形矩阵 imwrite(f.cdata,['image\',NewNumName]) %写入图片 close gcf; %关闭当前窗口,不然你屏幕上一堆窗口。 end
这个程序成功运行之后,你会发现一个新的文件夹,里面的素材基本上都顶齐了。
当然个别数字顶不齐主要怪字体。大家可以尝试不同的字体与比例搭配,这里就不列举了。
第三步:拼出你的刺激画面
又是一通计算,算出来结果如图。不过值得一提的是,在普通情况下,MATLAB的起始像素点从屏幕左下角开始算,而在PTB里是从左上角开始算。
所以在PTB下我们需要算出两个数字矩形的左上角的坐标。这里算好了
左边数字左上角(x , y)是(825 ,656),右下角是(910,784);
右边数字左上角(x , y)是(1220,622),右下角是(1365,818)。
第四步:打出来!
在PTB里写好实验程序,包括主程序和每个trail,反应时记录、被试按键记录等等,这里不做赘述。就讲讲刺激呈现的页面。
假设其它程序框架已经搭好了,现在只呈现单个trail的刺激。
这段代码在蒋老师的整个实验程序 Singletrail Function功能代码基础上截取修改。主要修改了脱离主程序可以运行、屏幕图片参数修改、加入位置和位移、插入详细解释说明、结束段等。
Screen('Preference','SkipSyncTests', 1); Screen('Preference', 'ConserveVRAM', 64); %设置屏幕,这两句话通常写在主函数前面的,这里为了方便大家运行可以直接拷贝 %以下是单个trail里的显示内容 w = Screen('OpenWindow',0); %打开窗口,全屏 Screen('FillRect',w,0); %创建黑屏 pic1name = 'pic_2.jpg'; pic2name = 'pic_3.jpg'; LPIC = imread(['image\',pic1name]); RPIC = imread(['image\',pic2name]); % 读取 .jpg 图片 lGratingIndex = Screen('MakeTexture', w, LPIC); rGratingIndex = Screen('MakeTexture', w, RPIC); %将读取好的图制作成图片,用GratingIndex来指代。 %这是PTB的一种图片显示机制,给显示器运算用的 GRect = Screen('Rect',lGratingIndex); GRect = Screen('Rect',rGratingIndex); % 求当前图片的位置 Screen('DrawTexture',w,lGratingIndex, GRect, [825,656,910,784]); Screen('DrawTexture',w,rGratingIndex, GRect, [1220,622,1365,818]); %将GratingIndex画到屏幕画板上,GRect是图片原始位置 %后一个是目标位置,坐标分别是上一步算好的第一个,第二个 Screen('Flip',w); %出现吧,屏幕! KbWait; % 按任意键继续 ShowCursor; % 显示 Screen('CloseAll'); %结束,关闭屏幕
大功告成!!
如果想要正式实验,还要把trail变成一个功能(function),输入参数是一个随机好的表单,数字文件名称被很好随机过替换即可。这里就不列举了。
附加:猴子你出来!这是怎么回事!?
刚刚的程序明明写好了,算好了,没问题,为啥有的朋友呈现结果是这样的↓
有人就说了:猴子你出来!保证不打死你!为啥右下半边被吃了?
吼吼吼那我来解释一下吧。
我们先用下面代码跑一遍看看:
Screen('Preference','SkipSyncTests', 1); Screen('Preference', 'ConserveVRAM', 64); [w, wRect] = Screen('OpenWindow',0); Screen('CloseAll');
跑完以后,PTB检测到的屏幕分辨率参数就显示在wRect里面了。
不看不知道,一看吓一跳,为啥我的2K屏幕变成1234*823了?
难道是MATLAB的分辨率检测代码出问题?
其实是因为你用了Windows 8及以上系统,里面有个系统画面缩放搞的鬼。
以我的win10系统为例,桌面,右键,显示设置,就可以看到,画面被放大了175%
也就是说,2160的横向分辨率只有2160/1.75=1234了。
那么,解决方案有两个。
一个是,按照1234*823的分辨率重新算PPI和像素位置。
第二个,把175%在实验时改成100%。
两个办法各有利弊,反正我懒,选了后者。然后我就开启了瞎眼模式……
(高分屏的孩子懂得)
不过最后尺寸还算令猴满意。
=====================分割线====================
最后,感谢教我MATLAB的蒋老师,让我在短短四个月强行进入MATLAB殿堂。感谢一起学习的同学们,在互相交流经验中学习成长。最后,感谢您能读到这里。
这个程序本身可能有瑕疵,比如这样的代码不够高效,程序效率低下,普通处理器可能延迟较高等,以后有机会还能努力学习优化。再次感谢您的观看!来源:知乎 www.zhihu.com
作者:Avmonkey
【知乎日报】千万用户的选择,做朋友圈里的新鲜事分享大牛。 点击下载
没有评论:
发表评论