Aegisub 拆字
本文最后更新于 106 天前。

很早就想接触的坑,因为忙(lan)一直没下手,这两天有朋友来问了关于文字连筋的问题,虽然没能解决,但是还是逼自己学一下拆字。实际上核心代码并没有看懂,先抄作业,用着再说。

拆字的本质是拆分绘图的连通域。把每个字单独的部分独立拆出来,形成了新的绘图。[1]
用到了DOMO的算法。[2]

Comment: 0,0:00:00.00,0:00:05.00,Default,text to shape,0,0,0,code syl noblank,style=syl.style   text = _G.decode.create_font(style.fontname, style.bold, style.italic, style.underline, style.strikeout, style.fontsize).text_to_shape(syl.text_stripped) text=string.gsub(text," c","") text1=_G.Yutils.shape.filter(text, function(x,y) return math.round(x),math.round(y) end)
Comment: 0,0:00:00.00,0:00:05.00,Default,,0,0,0,code once,function pointmeshshape(shape,x,y)   local pixels=_G.Yutils.shape.to_pixels(shape)  local n=1  local x=math.round(x)  local y=math.round(y)  local x1,y1,x2,y2=_G.Yutils.shape.bounding(shape)   if x<x1 or x>x2 or y<y1 or y>y2 then return false end                                                         for i=1,#pixels do                                                                                                                           if x==pixels[i].x and y==pixels[i].y then  n=0 end  end    if n==0 then return true else return false   end end
Comment: 0,0:00:00.00,0:00:05.00,Default,,0,0,0,code once,function text_split(text_shape) local  function delete_empty(tbl,mode)    if mode == 1 then       for i=#tbl,1,-1 do          if tbl[i] == "" then                _G.table.remove(tbl, i)         end     end elseif mode==2 then     for i=#tbl,1,-1 do      if (tbl[i].shape_i == "" or tbl[i].shape == nil) then               _G.table.remove(tbl, i)         end     end end return tbl   end  local  function is_include(value, tbl)    for k,v in _G.ipairs(tbl) do      if v == value then          return true        end     end    return false  end   local function string_split(str, split_char)              local sub_str_tab = {}        while true do                      local pos = string.find(str, split_char)             if not pos then                              _G.table.insert(sub_str_tab,str)                break            end              local sub_str = string.sub(str, 1, pos - 1)                      _G.table.insert(sub_str_tab,sub_str)            str = string.sub(str, pos + 1, string.len(str))        end              return sub_str_tab    end  local function split_by_m(text)          text = string_split(text,"m")       _G.table.remove(text,1)         point = {}          for i=1,#text do            point[i] = {}           text[i] = "m"..text[i]              for x,y in string.gmatch(text[i],"[m|l|b] (-?%d+) (-?%d+)") do                  _G.table.insert(point[i],{x = x,y = y})             end         end         return text      end     local  function rec(text)          new_shape_tbl = {}          record = {}         for i=1,#text do            for j=1,#text do                x,y = _G.tonumber(point[j][1].x),_G.tonumber(point[j][1].y)                                 if (pointmeshshape(text[i],x,y) and i~=j) then                      _G.table.insert(record,{inner = j,outer = i})               else                    _G.table.insert(record,"")                  end             end         end         return record      end      local  function record_handler(record_tbl,text)         info_tbl = {}       outer = {}          inner = {}                  for i=1,#record_tbl do              outer[i]={}             inner[i]={}             for j=1,#record_tbl do                  if record_tbl[j].inner == i then                    _G.table.insert(outer[i],record_tbl[j].outer)               end                 if record_tbl[j].outer == i then                    _G.table.insert(inner[i],record_tbl[j].inner)               end             end                     _G.table.insert(info_tbl,{shape_i = i, o_count = #outer[i],outer = outer[i],in_count = #inner[i],inner=inner[i], shape = text[i]})          end         return delete_empty(info_tbl)      end  local function shape_assembler(info_tbl)        new_t = {}          delete_empty(info_tbl,2)        _G.table.sort(info_tbl,function (a,b) return a.o_count > b.o_count end)         info_tbl["n"]= #info_tbl                for i=1,info_tbl.n do                   if ((info_tbl[i].o_count==0 or info_tbl[i].o_count%2==0) and info_tbl[i].in_count==0) then                  new_t[i] = info_tbl[i]                  elseif (info_tbl[i].in_count>=1 and info_tbl[i].o_count~=0 and info_tbl[i].o_count%2==0) then                               for j=i,1,-1 do                         if is_include(info_tbl[i].shape_i,info_tbl[j].outer) then                       info_tbl[i].shape = info_tbl[i].shape.." "..info_tbl[j].shape                       end                 end             new_t[i] = info_tbl[i]                      elseif (info_tbl[i].o_count==0 and info_tbl[i].in_count~=0) then                                for j=i,1,-1 do                                 if is_include(info_tbl[i].shape_i,info_tbl[j].outer) and #info_tbl[j].outer==1 then                         info_tbl[i].shape = info_tbl[i].shape.." "..info_tbl[j].shape                   end                 end                 new_t[i] = info_tbl[i]              else                new_t[i] =""            end         end         new_t = delete_empty(new_t,1)                   for i=1,#new_t do           new_t[i] = new_t[i].shape       end         return new_t      end local text_tbl=split_by_m(text_shape)   local record_tbl=rec(text_tbl)    local new_table=record_handler(record_tbl,text_tbl)  local shapes=shape_assembler(new_table)  return shapes end
Comment: 0,0:00:00.00,0:00:05.00,Default,,0,0,0,code syl noblank,aaa = text_split(text1)
Comment: 0,0:00:00.00,0:00:00.00,Default,,0,0,0,template syl noblank notext,!maxloop(#aaa)!{\an7\bord3\pos($left,$top)\p1\blur2}!aaa[j]!
Comment: 0,0:00:00.00,0:00:05.00,Default,,0,0,0,karaoke,{\k100}你{\k100}好{\k100} {\k100}世{\k100}界
Dialogue: 0,0:00:00.00,0:00:05.00,Default,,0,0,0,fx,{\an7\bord3\pos(858,1000)\p1\blur2}m 12 15 b 9 22 5 29 1 33 2 35 3 38 4 39 5 38 6 37 7 35 l 7 60 12 60 12 27 b 14 24 16 20 17 17 
Dialogue: 0,0:00:00.00,0:00:05.00,Default,,0,0,0,fx,{\an7\bord3\pos(858,1000)\p1\blur2}m 41 29 b 40 31 40 33 40 35 l 45 36 b 45 33 46 28 47 24 l 43 24 42 24 25 24 b 26 21 27 19 27 17 l 22 15 b 20 22 17 29 14 33 15 34 17 35 18 37 20 34 21 32 23 29 l 28 29 28 54 b 28 54 28 55 28 55 27 55 25 55 23 55 24 56 25 59 25 60 28 60 30 60 32 59 34 58 34 57 34 54 l 34 29 
Dialogue: 0,0:00:00.00,0:00:05.00,Default,,0,0,0,fx,{\an7\bord3\pos(858,1000)\p1\blur2}m 21 37 b 19 42 17 47 15 51 16 52 18 53 20 54 22 50 25 44 26 38 
Dialogue: 0,0:00:00.00,0:00:05.00,Default,,0,0,0,fx,{\an7\bord3\pos(858,1000)\p1\blur2}m 36 38 b 38 43 40 50 41 54 l 46 52 b 46 48 44 41 41 36
Dialogue: 0,0:00:00.00,0:00:05.00,Default,,0,0,0,fx,{\an7\bord3\pos(906,1000)\p1\blur2}m 18 25 l 17 25 12 25 b 12 22 13 19 13 16 l 7 15 b 7 18 7 21 6 25 l 2 25 2 30 5 30 b 4 34 3 38 2 41 5 43 7 45 10 48 7 51 4 54 1 56 2 57 4 59 4 60 8 58 11 55 14 51 16 53 17 55 18 57 l 22 52 b 21 50 19 48 17 46 19 41 21 34 22 25  m 16 30 b 15 35 14 39 12 42 11 41 10 40 8 39 9 37 10 33 11 30 
Dialogue: 0,0:00:00.00,0:00:05.00,Default,,0,0,0,fx,{\an7\bord3\pos(906,1000)\p1\blur2}m 47 35 l 37 35 37 31 b 41 28 44 24 46 21 l 42 18 41 18 23 18 23 23 37 23 b 35 26 33 29 31 30 l 31 35 21 35 21 40 31 40 31 54 b 31 55 31 55 30 55 29 55 26 55 24 55 25 56 26 59 26 60 30 60 32 60 34 59 36 58 37 57 37 54 l 37 40 47 40
Dialogue: 0,0:00:00.00,0:00:05.00,Default,,0,0,0,fx,{\an7\bord3\pos(965,1000)\p1\blur2}m 40 47 l 40 32 46 32 46 27 40 27 40 16 34 16 34 27 27 27 27 16 21 16 21 27 15 27 15 17 9 17 9 27 2 27 2 32 9 32 9 58 45 58 45 52 15 52 15 32 21 32 21 47 m 34 32 l 34 41 27 41 27 32 
Dialogue: 0,0:00:00.00,0:00:05.00,Default,,0,0,0,fx,{\an7\bord3\pos(1014,1000)\p1\blur2}m 42 37 l 42 17 7 17 7 37 15 37 b 11 40 7 43 2 44 3 45 5 48 5 49 9 48 12 46 14 44 l 14 46 b 14 49 13 53 5 56 6 57 8 59 9 61 19 57 20 51 20 46 l 20 43 16 43 b 18 41 20 39 22 37 l 27 37 b 28 40 30 41 33 43 l 29 43 29 60 35 60 35 45 b 37 47 40 48 43 49 44 47 46 45 47 44 42 43 37 40 34 37 m 13 21 l 21 21 21 25 13 25  m 36 25 l 27 25 27 21 36 21  m 36 33 l 27 33 27 29 36 29  m 13 29 l 21 29 21 33 13 33 

文字转绘图

关于文字转绘图,请参考Yutils库 绘图相关

函数:判断点是否在绘图上

遍历算法,用图形转像素来判断。[3]

function pointmeshshape(shape,x,y)   
    local pixels=_G.Yutils.shape.to_pixels(shape)  
    local n=1  
    local x=math.round(x)  
    local y=math.round(y)  
    local x1,y1,x2,y2=_G.Yutils.shape.bounding(shape)   
    if x<x1 or x>x2 or y<y1 or y>y2 then 
        return false 
    end
    for i=1,#pixels do
        if x==pixels[i].x and y==pixels[i].y then  
            n=0 
        end  
    end    
    if n==0 then 
        return true 
    else 
        return false   
    end 
end

使拆分出来的绘图控制点在图形中心

xy={} 
for i=1, #aaa do  
    if aaa[i]~=nil then 
        a,b,c,d=_G.Yutils.shape.bounding(aaa[i]) xy[i]={x=(a+c)/2,y=(b+d)/2}    
        aaa[i]=_G.Yutils.shape.move(aaa[i],-xy[i].x,-xy[i].y)  
    end 
end

应用到字幕文件中,

Comment: 0,0:00:00.00,0:00:05.00,Default,text to shape,0,0,0,code syl noblank,style=syl.style   text = _G.decode.create_font(style.fontname, style.bold, style.italic, style.underline, style.strikeout, style.fontsize).text_to_shape(syl.text_stripped) text=string.gsub(text," c","") text1=_G.Yutils.shape.filter(text, function(x,y) return math.round(x),math.round(y) end)
Comment: 0,0:00:00.00,0:00:05.00,Default,判断点是否在图形上,0,0,0,code once,function pointmeshshape(shape,x,y)   local pixels=_G.Yutils.shape.to_pixels(shape)  local n=1  local x=math.round(x)  local y=math.round(y)  local x1,y1,x2,y2=_G.Yutils.shape.bounding(shape)   if x<x1 or x>x2 or y<y1 or y>y2 then return false end                                                         for i=1,#pixels do                                                                                                                           if x==pixels[i].x and y==pixels[i].y then  n=0 end  end    if n==0 then return true else return false   end end
Comment: 0,0:00:00.00,0:00:05.00,Default,DOMO拆字 函数,0,0,0,code once,function text_split(text_shape) local  function delete_empty(tbl,mode)   if mode == 1 then       for i=#tbl,1,-1 do          if tbl[i] == "" then                _G.table.remove(tbl, i)         end     end elseif mode==2 then     for i=#tbl,1,-1 do      if (tbl[i].shape_i == "" or tbl[i].shape == nil) then               _G.table.remove(tbl, i)         end     end end return tbl   end  local  function is_include(value, tbl)    for k,v in _G.ipairs(tbl) do      if v == value then          return true        end     end    return false  end   local function string_split(str, split_char)              local sub_str_tab = {}        while true do                      local pos = string.find(str, split_char)             if not pos then                              _G.table.insert(sub_str_tab,str)                break            end              local sub_str = string.sub(str, 1, pos - 1)                      _G.table.insert(sub_str_tab,sub_str)            str = string.sub(str, pos + 1, string.len(str))        end              return sub_str_tab    end  local function split_by_m(text)          text = string_split(text,"m")       _G.table.remove(text,1)         point = {}          for i=1,#text do            point[i] = {}           text[i] = "m"..text[i]              for x,y in string.gmatch(text[i],"[m|l|b] (-?%d+) (-?%d+)") do                  _G.table.insert(point[i],{x = x,y = y})             end         end         return text      end     local  function rec(text)          new_shape_tbl = {}          record = {}         for i=1,#text do            for j=1,#text do                x,y = _G.tonumber(point[j][1].x),_G.tonumber(point[j][1].y)                                 if (pointmeshshape(text[i],x,y) and i~=j) then                      _G.table.insert(record,{inner = j,outer = i})               else                    _G.table.insert(record,"")                  end             end         end         return record      end      local  function record_handler(record_tbl,text)         info_tbl = {}       outer = {}          inner = {}                  for i=1,#record_tbl do              outer[i]={}             inner[i]={}             for j=1,#record_tbl do                  if record_tbl[j].inner == i then                    _G.table.insert(outer[i],record_tbl[j].outer)               end                 if record_tbl[j].outer == i then                    _G.table.insert(inner[i],record_tbl[j].inner)               end             end                     _G.table.insert(info_tbl,{shape_i = i, o_count = #outer[i],outer = outer[i],in_count = #inner[i],inner=inner[i], shape = text[i]})          end         return delete_empty(info_tbl)      end  local function shape_assembler(info_tbl)        new_t = {}          delete_empty(info_tbl,2)        _G.table.sort(info_tbl,function (a,b) return a.o_count > b.o_count end)         info_tbl["n"]= #info_tbl                for i=1,info_tbl.n do                   if ((info_tbl[i].o_count==0 or info_tbl[i].o_count%2==0) and info_tbl[i].in_count==0) then                  new_t[i] = info_tbl[i]                  elseif (info_tbl[i].in_count>=1 and info_tbl[i].o_count~=0 and info_tbl[i].o_count%2==0) then                               for j=i,1,-1 do                         if is_include(info_tbl[i].shape_i,info_tbl[j].outer) then                       info_tbl[i].shape = info_tbl[i].shape.." "..info_tbl[j].shape                       end                 end             new_t[i] = info_tbl[i]                      elseif (info_tbl[i].o_count==0 and info_tbl[i].in_count~=0) then                                for j=i,1,-1 do                                 if is_include(info_tbl[i].shape_i,info_tbl[j].outer) and #info_tbl[j].outer==1 then                         info_tbl[i].shape = info_tbl[i].shape.." "..info_tbl[j].shape                   end                 end                 new_t[i] = info_tbl[i]              else                new_t[i] =""            end         end         new_t = delete_empty(new_t,1)                   for i=1,#new_t do           new_t[i] = new_t[i].shape       end         return new_t      end local text_tbl=split_by_m(text_shape)   local record_tbl=rec(text_tbl)    local new_table=record_handler(record_tbl,text_tbl)  local shapes=shape_assembler(new_table)  return shapes end
Comment: 0,0:00:00.00,0:00:05.00,Default,拆出来的存到表aaa,0,0,0,code syl noblank,aaa = text_split(text1)
Comment: 0,0:00:00.00,0:00:05.00,Default,移动到中心,0,0,0,code syl noblank,xy={} for i=1, #aaa do  if aaa[i]~=nil then a,b,c,d=_G.Yutils.shape.bounding(aaa[i]) xy[i]={x=(a+c)/2,y=(b+d)/2}    aaa[i]=_G.Yutils.shape.move(aaa[i],-xy[i].x,-xy[i].y)  end end
Comment: 0,0:00:00.00,0:00:00.00,Default,,0,0,0,template syl noblank notext,!maxloop(#aaa)!{\an7\pos(!$left+xy[j].x!,!$top+xy[j].y!)\p1\blur2\1c!_G.ass_color(_G.HSV_to_RGB(math.random(0,300),1,1))!}!aaa[j]!
Comment: 0,0:00:00.00,0:00:05.00,Default,,0,0,0,karaoke,{\k125}测{\k125}试{\k125}字{\k125}幕

image.png

参考

  1. ^https://www.bilibili.com/video/BV1w7411V7tk?t=307
  2. ^https://vmoe.info/6652
  3. ^https://www.bilibili.com/video/BV1w7411V7GH
标题:Aegisub 拆字
作者:IKK
除转载和特殊声明外,所有文章采用 CC BY-NC-SA 4.0协议
上一篇
下一篇