iOS 圖片、視頻轉字符畫

0. 效果

  • 圖片

  • 視頻

1. 實現原理

1.1 RGB轉灰度值

首先,咱們知道在OpenGL中顏色有4個通道RGBA,對於通常圖片A=1.0。那還有3個通道須要處理,RGB。git

而咱們的字符畫使用1個字符表示1塊顏色,即咱們須要將RGB三個通道進行某種處理(3個值),讓它們變爲1個值,咱們才能對應某1個字符。github

上面所說的某種處理就是:RGB值轉換爲灰度值性能

這個部分咱們能夠經過shader進行轉換,shader來自GPUImageGrayscaleFilterflex

precision highp float;
varying vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
const highp vec3 W = vec3(0.2125, 0.7154, 0.0721);
void main()
{
   lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
   float luminance = dot(textureColor.rgb, W);
   gl_FragColor = vec4(vec3(luminance), textureColor.a);
}
複製代碼

經過上面的處理,咱們就把RGB值轉換爲了灰度值,或者shader中的luminance(亮度值)。ui

此時,RGB值均等於luminance。(後面直接使用RGB中任何一個值便可)spa

1.2 灰度值轉字符

如今的灰度值範圍爲[0,1.0],咱們將其量化爲15個等級。code

等級細分可根據需求本身肯定。orm

由低到高爲[0, 1/15, 2/15,...,1.0]cdn

圖中文字可自行選擇,保證其在圖中黑白佔比接近對應的等級便可。視頻

1.3 灰度圖尺寸轉換

若是咱們使用一個像素表示一個字符,確定是看不出字符的形狀的,因此通常採用多個像素點表示一個字符的形式來進行顯示。因此未轉換成字符的時候,用多個點表示一個灰度,就會獲得下面這張馬賽克風格的圖。

示例中,我採用了10*10的像素點來表示一個灰度值。10*10比較難畫,下面我用5*5的像素點來解釋。

若是用5*5的像素點來表示1個灰度值,咱們須要用25個點的灰度值算一個平均,而後再用這個灰度值去填充25個像素格子。那若是我把圖片的長和寬都縮小5倍,而後用灰度值來繪製,那麼GPU會幫咱們完成計算,並且如今我只須要1個格子。

咱們再來一個具體的例子,假設我有一張1000*1000的圖,經過灰度shader和在0.1倍的frame buffer上進行繪製,就能夠獲得一個100*100的灰度圖查詢的紋理。

即,對於原始圖中座標(x,y),x∈[0,9],y∈[0,9]的這些像素點,只須要使用灰度圖查詢紋理(0,0)這一個像素點的灰度值便可。

1.4 灰度值映射字符紋理

varying highp vec2 textureCoordinate; // 紋理座標
varying highp vec2 textureCoordinate2; // 紋理座標(未用到)

uniform sampler2D inputImageTexture; //字符紋理
uniform sampler2D inputImageTexture2; // 灰度值參考紋理

uniform highp vec2 textureSize; // 原圖尺寸

void main()
{
  // 像素點座標
  highp vec2 coordinate = textureCoordinate * textureSize;

  // demo這裏寫死,能夠根據實際狀況調整
  highp float width = 10.0;
  // 計算width*width的區域的中點
  highp vec2 midCoor = min((floor(coordinate / width) * width + width * 0.5) / textureSize, 1.0);
  // 獲得中點的灰度值
  lowp vec4 color = texture2D(inputImageTexture2, midCoor);
  // 一個字符的歸一化紋理座標
  coordinate = mod(coordinate, width) / width;
  // 爲了節約性能,15個字符咱們放在一個紋理上,須要根據灰度值進行x偏移
  coordinate.x = (floor(color.r * 14.0) + coordinate.x) / 15.0;

  gl_FragColor = texture2D(inputImageTexture, coordinate);
}
複製代碼
  1. 咱們根據紋理座標和紋理的尺寸算出對應的像素點座標。

  2. 計算width*width的區域的中點,並獲得中心點的灰度值。

    因爲小尺寸的灰度紋理咱們是分開獲得的,不能保證必定知足咱們上面提到的理想效果,因此採用了中心點的灰度值。

  3. 咱們用width*width的像素點表示一個字符,計算出對應字符的歸一化紋理座標。

  4. 爲了節約性能,因爲15個字符紋理咱們橫向合併在一個紋理中,因此要根據灰度值進行偏移,灰度值選擇對應的字符紋理。

2. Demo

Demo代碼

若是以爲本文對你有所幫助,給我點個贊吧~

相關文章
相關標籤/搜索