トーンカーブスクリプト
トーンカーブ
画像はピクセルでできています。
ピクセルは3つ(RGB)の輝度値を持ちます。
画像処理でピクセルの輝度値を思い通りに動かせたら楽しいですよね?
直観的に画像の明暗をコントロールできるものにトーンカーブがあります。
x軸が変化前、y軸が変化後の輝度値になるように曲線を作ることで、画像のコントラスト等を調整することができます。
https://scrapbox.io/files/6607a8e175e9b50024cc156f.png
曲線の実装
曲線を表現するものには様々ありますが、今回のトーンカーブスクリプトでは3次スプライン補間を使います。
数学的な説明や、アルゴリズムについてはこの記事を参考にします。 3次スプライン補間でピクセルの輝度値毎に明暗を求めていくことになりますが、ピクセル毎に3次スプライン補間していたらとても大変です。なので、ピクセル毎に処理する前に計算値をテーブルに入れることにします。
code:lua
local curve_table={}
for i=0,255 do
curve_tablei+1=mcast(mcurve(i/255,px,py,c)*255) end
ヒストグラムの実装
トーンカーブは画像のコントラストを調整しますが、画像をパッと見ただけでは画像の特性が分からなかったりします。そのようなときは、ヒストグラムがあると便利です。
予めテーブルを用意しておいて、輝度値毎に愚直に数え上げて、それを画像に重ねて表示してあげると分かり易いです。また、そのままではデータに偏りがあった際に読み取りづらいグラフとなってしまうので、データの最大値で正規化してあげます。更に平滑化処理でデータをなめらかにすれば完璧ですね。
スクリプト
スクリプトです。
code:ToneCurve_M.lua
--[[
ToneCurve_M.anm
コントラストとかを微調整するスクリプトです
]]
--track0:anchor,3,16,3,1
--track1:histo,1,500,100,0.01
--track2:point,1,500,10,0.01
--track3:smooth,0,100,10,1
--check0:graph,1
--dialog:set_anchor,pos={-obj.w/2,obj.h/2,0,0,obj.w/2,-obj.h/2};
--tdma
local function mtdma(a,b,c,d)
local n=#a
for i=2,n do
end
local i=n-1
while i>=1 do
i=i-1
end
return d
end
--spline
local function mspline(x,y)
local max_p=#y
local a,b,c,d,h,w={},{},{},y,{},{}
for i=1,max_p-1 do
end
for i=2,max_p-1 do
end
b=mtdma(a,w,c,b)
for i=1,max_p-1 do
end
return {a,b,c,d}
end
--sort
local function msort(x,y)
for i=1,#x-1 do
for j=i+1,#x do
end
end
return x,y
end
--cubic
local function mcubic(d,c)
return ((c1*d+c2)*d+c3)*d+c4 end
--cast
local function mcast(d)
return math.max(math.min(math.floor(d+0.5),255),0)
end
--curve
local function mcurve(d,x,y,c)
else
local ix=1
while true do
else ix=ix+1 end
end
end
return d
end
--blur
local function msmoothing(ary,index,size,weight)
local a,mi,ma,c=0,math.max(index-size,1),math.min(index+size,#ary),1
for i=mi,ma do
c=c+1
end
return a/(2*size+1)
end
--main
obj.setanchor("pos",obj.track0)
local px,py={},{}
for i=1,#pos/2 do
end
px,py=msort(px,py)
local c=mspline(px,py)
local curve_table={}
for i=0,255 do
curve_tablei+1=mcast(mcurve(i/255,px,py,c)*255) end
local data,w,h=obj.getpixeldata()
local ffi=require("ffi")
pcall(ffi.cdef,typedef struct Pixel_ {uint8_t b,g,r,a;} Pixel;)
local cdata=ffi.cast("Pixel*",data)
local cr,cg,cb,cv={},{},{},{}
for i=1,256 do
end
local ix=0
for y=0,h-1 do
for x=0,w-1 do
cdataix.r=curve_table[cdataix.r+1] cdataix.g=curve_table[cdataix.g+1] cdataix.b=curve_table[cdataix.b+1] local r,g,b=cdataix.r,cdataix.g,cdataix.b local v=mcast((r+g+b)/3)+1
r,g,b=r+1,g+1,b+1
ix=ix+1
end
end
obj.putpixeldata(data)
obj.effect()
obj.draw()
if obj.check0==true then
local ary={{},{},{},{}}
local col={0xff0000,0x00ff00,0x0000ff,0x000000}
local fs,w2,h2=w/256,w/2,h/2
local ss,sw,total_sw=obj.track3,{},0
if ss==0 then
sw={1}
else
for i=1,ss*2+1 do
local s=(i-1)-ss
swi=math.exp(-(s*s)/(2*ss*ss))/(math.sqrt(2*math.pi)*ss) end
for i=1,ss*2+1 do
end
end
local aary={cr,cg,cb,cv}
local size=1
for j=1,4 do
for i=1,256 do
aryji=msmoothing(aaryj,i,ss,sw) if size<aryji then size=aryji end end
end
size=(h/size)*obj.track1*0.01
for i=1,#ary do
obj.load("figure","四角形",coli,1) obj.alpha=0.3
for j=0,255 do
local p={fs*j-w2,fs*(j+1)-w2,h2-(aryij+1*size)} obj.drawpoly(
end
end
obj.load("figure","円",0xff0000,obj.track2)
obj.alpha=0.5
for i=0,255 do
obj.draw((i/255-0.5)*w,(0.5-curve_tablei+1/255)*h) end
obj.load("figure","円",0xff0000,obj.track2*2)
obj.alpha=0.5
for i=0,#pos/2-1 do
end
end
pos=nil
by metaphysical bard