備忘錄_20160105(定位)
修改
回首頁
程式 2022-05-29 16:04:29 1653811469 100
製作類似卡拉OK字幕逐字亮起的流程─04產生frames
製作類似卡拉OK字幕逐字亮起的流程─04產生frames
●20220608_makeFrames.py
from datetime import datetime
import numpy as np
import cv2
#from decimal import Decimal
import math
strExeBeginTime=datetime.now().strftime("%H:%M:%S")
iWidth=1920 # pixels
iHeight=2541 # pixels
fFPS=24 # frames/second
# total 445 seconds
iIdxFrameBegin=1
iIdxFrameEnd=445*fFPS
oEncode=cv2.VideoWriter_fourcc(*'mp4v') # 指定視訊編碼
oOut=cv2.VideoWriter( '../videos/tb14_only_video.mp4',oEncode,fFPS,(iWidth,iHeight),True)
class TheData:
def __init__(self,straItem):
self.strDescription=straItem[0]
self.fSecs1=float(straItem[1])
self.fSecs2=float(straItem[2])
self.strBGFileName=straItem[3].strip()
self.strFGFileName="fg"+(straItem[3].strip())[2:]
# 由上而下 或 由左而右 不一定,全看 beg -> end
self.fBegX1=float(straItem[4])
self.fBegY1=float(straItem[5])
self.fBegX2=float(straItem[6])
self.fBegY2=float(straItem[7])
'''
if self.fBegX1>self.fBegX2:
self.fBegX1,self.fBegX2=self.fBegX2,self.fBegX1
self.fBegY1,self.fBegY2=self.fBegY2,self.fBegY1
'''
self.fEndX1=float(straItem[8])
self.fEndY1=float(straItem[9])
self.fEndX2=float(straItem[10])
self.fEndY2=float(straItem[11])
'''
if self.fEndX1>self.fEndX2:
self.fEndX1,self.fEndX2=self.fEndX2,self.fEndX1
self.fEndY1,self.fEndY2=self.fEndY2,self.fEndY1
'''
# 讀取檔案內容
oFile=open('20220608_data_from_excel_utf8.txt','r',encoding='UTF-8')
oContent=oFile.read()
oFile.close()
# 拿掉註解行
straSourceLine=("\n".join(("\n".join(oContent.split("\r\n"))).split("\r"))).split("\n")
straWantedLine=[]
for i in range(0,len(straSourceLine)):
straItem=straSourceLine[i].split("\t")
iItems=0
for j in range(0,len(straItem)):
if straItem[j].strip()!="":
iItems+=1
if iItems>=12:
straWantedLine.append(TheData(straItem))
iMaxLenOfNum=len(str(iIdxFrameEnd)) # 數字總共幾位數
iIdxFrameBegin-=1 # base 0
iIdxFrameEnd-=1 # base 0
iIdxLastTime=-1
#oLog=open("log.log","w")
for iIdxFrame in range(iIdxFrameBegin,iIdxFrameEnd+1):
fCurrentSeconds=(iIdxFrame+1)/fFPS
strCurrentBGFileName=""
strCurrentFGFileName=""
iIdxDataBegin=-1
iIdxDataEnd=-1
# 尋找這個頁面的最後一筆
if iIdxLastTime!=-1: iIdxTmpBegin=iIdxLastTime
else: iIdxTmpBegin=0
for iIdxData in range(iIdxTmpBegin, len(straWantedLine)):
if fCurrentSeconds<straWantedLine[iIdxData].fSecs1:
iIdxDataEnd=iIdxData-1
if iIdxDataEnd<0: iIdxDataEnd=0
strCurrentBGFileName=straWantedLine[iIdxDataEnd].strBGFileName
strCurrentFGFileName=straWantedLine[iIdxDataEnd].strFGFileName
break
if iIdxDataEnd==-1:
iIdxDataEnd=len(straWantedLine)-1
strCurrentBGFileName=straWantedLine[iIdxDataEnd].strBGFileName
strCurrentFGFileName=straWantedLine[iIdxDataEnd].strFGFileName
# 尋找這個頁面的第一筆
if iIdxLastTime!=-1:
iIdxDataBegin=iIdxLastTime
else:
for iIdxData in range(iIdxDataEnd,-1,-1):
if strCurrentBGFileName==straWantedLine[iIdxData].strBGFileName:
iIdxDataBegin=iIdxData
else:
break
# nmomtf
#print("iIdxFrame="+str(iIdxFrame), "iIdxDataBegin="+str(iIdxDataBegin), "iIdxDataEnd="+str(iIdxDataEnd))
# 繪製這個 frame
for iIdxCB in range(iIdxDataBegin,iIdxDataEnd+1):
if straWantedLine[iIdxCB].fSecs1==straWantedLine[iIdxCB].fSecs2:
oFG=cv2.imread('../images/'+strCurrentFGFileName,cv2.IMREAD_COLOR)
oBG=cv2.imread('../images/'+strCurrentBGFileName,cv2.IMREAD_COLOR)
else:
# 方向 beg -> end
# 目前先是矩形好了,未來才用平行四邊形
'''
fX=straWantedLine[iIdxCB].fBegX1
fY=straWantedLine[iIdxCB].fBegY1
fW=straWantedLine[iIdxCB].fBegX2-straWantedLine[iIdxCB].fBegX1
fH=straWantedLine[iIdxCB].fEndY1-straWantedLine[iIdxCB].fBegY1
if fCurrentSeconds<straWantedLine[iIdxCB].fSecs2:
fH=fH*(fCurrentSeconds-straWantedLine[iIdxCB].fSecs1)/(straWantedLine[iIdxCB].fSecs2-straWantedLine[iIdxCB].fSecs1)
x1=math.floor(fX)
y1=math.floor(fY)
x2=math.floor(fX+fW)
y2=math.floor(fY+fH)
'''
fBegX1=straWantedLine[iIdxCB].fBegX1
fBegY1=straWantedLine[iIdxCB].fBegY1
fBegX2=straWantedLine[iIdxCB].fBegX2
fBegY2=straWantedLine[iIdxCB].fBegY2
fEndX1=straWantedLine[iIdxCB].fEndX1
fEndY1=straWantedLine[iIdxCB].fEndY1
fEndX2=straWantedLine[iIdxCB].fEndX2
fEndY2=straWantedLine[iIdxCB].fEndY2
fNewEndX1=fEndX1
fNewEndY1=fEndY1
fNewEndX2=fEndX2
fNewEndY2=fEndY2
if fCurrentSeconds<straWantedLine[iIdxCB].fSecs2:
fTheRatio=(fCurrentSeconds-straWantedLine[iIdxCB].fSecs1)/(straWantedLine[iIdxCB].fSecs2-straWantedLine[iIdxCB].fSecs1)
fNewEndX1=fTheRatio*(fEndX1-fBegX1)+fBegX1
fNewEndY1=fTheRatio*(fEndY1-fBegY1)+fBegY1
fNewEndX2=fTheRatio*(fEndX2-fBegX2)+fBegX2
fNewEndY2=fTheRatio*(fEndY2-fBegY2)+fBegY2
x1 = math.floor(min(fBegX1, fBegX2, fNewEndX1, fNewEndX2))
y1 = math.floor(min(fBegY1, fBegY2, fNewEndY1, fNewEndY2))
x2 = math.floor(max(fBegX1, fBegX2, fNewEndX1, fNewEndX2))
y2 = math.floor(max(fBegY1, fBegY2, fNewEndY1, fNewEndY2))
#oLog.write(str(fBegX1)+','+str(fBegY1)+','+str(fBegX2)+','+str(fBegY2)+"\n")
#oLog.write(str(fNewEndX1)+','+str(fNewEndY1)+','+str(fNewEndX2)+','+str(fNewEndY2)+"\n")
#oLog.write(str(x1)+','+str(y1)+','+str(x2)+','+str(y2)+','+"\n\n")
oBG[y1:y2,x1:x2]=oFG[y1:y2,x1:x2]
#strNewFileName='frame'+str(iIdxFrame+1).zfill(iMaxLenOfNum)+'.jpg'
#cv2.imwrite('frames/'+strNewFileName, oBG, [cv2.IMWRITE_JPEG_QUALITY, 100])
oOut.write(oBG)
print("Frame "+str(iIdxFrame)+" / "+str(iIdxFrameEnd+1)+" finished.")
iIdxLastTime=iIdxDataEnd
oOut.release()
print("All finished!")
strExeEndTime=datetime.now().strftime("%H:%M:%S")
print(strExeBeginTime, strExeEndTime)
#oLog.close()
●20220528_makeFrames_slow.py
import numpy as np
import cv2
#from decimal import Decimal
import math
iWidth=1920 # pixels
iHeight=1080 # pixels
fFPS=24 # frames/second
# total 2793.576 seconds
# want 2800 seconds => 2800*24 => 67200 frames
iIdxFrameBegin=1
iIdxFrameEnd=2800*fFPS
class TheData:
def __init__(self,straItem):
self.strDescription=straItem[0]
self.fSecs1=float(straItem[1])
self.fSecs2=float(straItem[2])
self.strBGFileName=straItem[3].strip()
self.strFGFileName="fg"+(straItem[3].strip())[2:]
# 假定由上而下
self.fBegX1=float(straItem[4])
self.fBegY1=float(straItem[5])
self.fBegX2=float(straItem[6])
self.fBegY2=float(straItem[7])
if self.fBegX1>self.fBegX2:
self.fBegX1,self.fBegX2=self.fBegX2,self.fBegX1
self.fBegY1,self.fBegY2=self.fBegY2,self.fBegY1
self.fEndX1=float(straItem[8])
self.fEndY1=float(straItem[9])
self.fEndX2=float(straItem[10])
self.fEndY2=float(straItem[11])
if self.fEndX1>self.fEndX2:
self.fEndX1,self.fEndX2=self.fEndX2,self.fEndX1
self.fEndY1,self.fEndY2=self.fEndY2,self.fEndY1
# 讀取檔案內容
oFile=open('20220528_data_from_excel_utf8.txt','r',encoding='UTF-8')
oContent=oFile.read()
oFile.close()
# 拿掉註解行
straSourceLine=("\n".join(("\n".join(oContent.split("\r\n"))).split("\r"))).split("\n")
straWantedLine=[]
for i in range(0,len(straSourceLine)):
straItem=straSourceLine[i].split("\t")
iItems=0
for j in range(0,len(straItem)):
if straItem[j].strip()!="":
iItems+=1
if iItems>=12:
straWantedLine.append(TheData(straItem))
iMaxLenOfNum=len(str(iIdxFrameEnd)) # 數字總共幾位數
iIdxFrameBegin-=1 # base 0
iIdxFrameEnd-=1 # base 0
for iIdxFrame in range(iIdxFrameBegin,iIdxFrameEnd+1):
fCurrentSeconds=(iIdxFrame+1)/fFPS
strCurrentBGFileName=""
strCurrentFGFileName=""
iIdxDataBegin=-1
iIdxDataEnd=-1
# 首先尋找這個頁面的最後一筆
for iIdxData in range(len(straWantedLine)-1,-1,-1):
if fCurrentSeconds>=straWantedLine[iIdxData].fSecs1:
iIdxDataEnd=iIdxData
strCurrentBGFileName=straWantedLine[iIdxData].strBGFileName
strCurrentFGFileName=straWantedLine[iIdxData].strFGFileName
break
# 再來尋找這個頁面的第一筆
for iIdxData in range(iIdxDataEnd,-1,-1):
if strCurrentBGFileName==straWantedLine[iIdxData].strBGFileName:
iIdxDataBegin=iIdxData
else:
break
# 繪製這個 frame
oBG=cv2.imread('../'+strCurrentBGFileName,cv2.IMREAD_COLOR)
oFG=cv2.imread('../'+strCurrentFGFileName,cv2.IMREAD_COLOR)
for iIdxCB in range(iIdxDataBegin,iIdxDataEnd+1):
if straWantedLine[iIdxCB].fSecs1==straWantedLine[iIdxCB].fSecs2:
# 可以省略
pass
else:
# 假定是由上而下
fX=straWantedLine[iIdxCB].fBegX1
fY=straWantedLine[iIdxCB].fBegY1
fW=straWantedLine[iIdxCB].fBegX2-straWantedLine[iIdxCB].fBegX1
fH=straWantedLine[iIdxCB].fEndY1-straWantedLine[iIdxCB].fBegY1
if fCurrentSeconds<straWantedLine[iIdxCB].fSecs2:
fH=fH*(fCurrentSeconds-straWantedLine[iIdxCB].fSecs1)/(straWantedLine[iIdxCB].fSecs2-straWantedLine[iIdxCB].fSecs1)
x1=math.floor(fX)
y1=math.floor(fY)
x2=math.floor(fX+fW)
y2=math.floor(fY+fH)
oBG[y1:y2,x1:x2]=oFG[y1:y2,x1:x2]
strNewFileName='frame'+str(iIdxFrame+1).zfill(iMaxLenOfNum)+'.jpg'
cv2.imwrite('frames/'+strNewFileName, oBG, [cv2.IMWRITE_JPEG_QUALITY, 100])
print("Frame "+str(iIdxFrame)+" / "+str(iIdxFrameEnd+1)+" finished.")
print("All finished!")
●20220521_makeFrames_very_slow.htm
<!doctype html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title></title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
*
{
font-family: "WenQuanYi Zen Hei","文泉驛正黑","Heiti TC","黑體-繁","LiHei Pro","儷黑 Pro","PingFang TC","Droid Sans","Roboto","Microsoft JhengHei","微軟正黑體",sans-serif;
/* 【linux的字型】【ios字型】【android字型】【微軟正黑體】【無襯線字=黑體】 */
}
html, body
{
margin: 0;
border: 0;
padding: 0;
width: 100%;
height: 100%;
}
a:link, a:visited
{
color: blue;
text-decoration: none;
}
#divWrapper
{
padding: 1em;
}
.classSmallImage
{
}
.classBlock
{
display: inline-block;
padding: 6px;
margin: 6px;
background-color: lightpink;
border-radius: 6px;
}
</style>
</head>
<body>
<div id="divWrapper">
<div style="color: red;">Firefox, about:config, set security.fileuri.strict_origin_policy to false</div>
<div style="color: red;">Firefox, 設定存檔時不用詢問,並改到想要的位置。</div>
請從 excel 把資料貼過來。<br>
description,secs1,secs2,imageName,begX1,begY1,begX2,begY2,endX1,endY1,endX2,endY2<br>
<div class="classBlock">畫面大小<input type="text" name="inpScreenWidth" id="inpScreenWidth" value="1920"> x <input type="text" name="inpScreenHeight" id="inpScreenHeight" value="1080"></div>
<div class="classBlock"><input type="text" name="inpFPS" id="inpFPS" value="24">fps</div>
<div class="classBlock">total <input type="text" name="inpTotalSecs" id="inpTotalSecs" value="2793.576">secs</div>
<br>
<div class="classBlock">繪製frame起訖<input type="text" name="inpFrameBegin" id="inpFrameBegin" value="1"> ~ <input type="text" name="inpFrameEnd" id="inpFrameEnd" value="67200"></div>
<br>
<textarea id="taInput" rows="10" cols="120"></textarea>
<br>
<button type="button" onclick="goDrawing();">進行繪製</button>
<br>
<div>
<canvas id="myCanvas"></canvas>
</div>
</div>
<hr>
<hr>
<hr>
<hr>
<div id="divImages"></div>
<script>
if(!String.prototype.ltrim) { String.prototype.ltrim = function() { return this.replace(/^\s+/,''); }; }
if(!String.prototype.rtrim) { String.prototype.rtrim = function() { return this.replace(/\s+$/,''); }; }
if(!String.prototype.trim ) { String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g,''); }; }
function gebi(strId)
{
return document.getElementById(strId);
}
for(var j=0; j<2; j++)
{
var strHead="fg";
if(j==1) { strHead="bg"; }
for(var i=1; i<=15; i++)
{
var oImg=document.createElement("img");
oImg.setAttribute("id",strHead+("00"+i).substr(-2));
oImg.setAttribute("src",strHead+("00"+i).substr(-2)+".jpg");
oImg.setAttribute("class","classSmallImage");
gebi("divImages").appendChild(oImg);
}
}
window.addEventListener(
"load",
function()
{
}
);
</script>
<script>
var iWidth, iHeight, fFPS, fTotalSecs, fTotalFrames;
function getStraData()
{
var straLine=gebi("taInput").value.split("\r\n").join("\n").split("\r").join("\n").split("\n");
var straBuffer=[];
// 去掉空白行以及不符合規格的資料
for(var i=0; i<straLine.length; i++)
{
var straItem=straLine[i].split("\t");
var iCount=0;
for(var j=0; j<straItem.length; j++)
{
if(straItem[j].trim()!="")
{
iCount++;
}
}
if(iCount>=12)
{
straBuffer.push(straLine[i]);
}
}
return straBuffer;
}
function getOAnalyzed(str1)
{
var straItem=str1.split("\t");
var strDescription=straItem[0];
var fSecs1=straItem[1];
var fSecs2=straItem[2];
var strImageName=straItem[3];
var strBGImageId=strImageName.substring(0,strImageName.length-4);
var strFGImageId=strBGImageId.split("bg").join("fg");
var fBegX1=straItem[4];
var fBegY1=straItem[5];
var fBegX2=straItem[6];
var fBegY2=straItem[7];
var fEndX1=straItem[8];
var fEndY1=straItem[9];
var fEndX2=straItem[10];
var fEndY2=straItem[11];
return {
strDescription:strDescription,
fSecs1:fSecs1,
fSecs2:fSecs2,
strImageName:strImageName,
strBGImageId:strBGImageId,
strFGImageId:strFGImageId,
fBegX1:fBegX1,
fBegY1:fBegY1,
fBegX2:fBegX2,
fBegY2:fBegY2,
fEndX1:fEndX1,
fEndY1:fEndY1,
fEndX2:fEndX2,
fEndY2:fEndY2};
}
var iIdxFrame, iIdxFrameBegin, iIdxFrameEnd;
var straDrawingData, oCanvas, oCtx;
var oaDrawingData;
function goDrawing()
{
straDrawingData=getStraData();
oaDrawingData=[];
for(var i=0; i<straDrawingData.length; i++)
{
oaDrawingData.push(getOAnalyzed(straDrawingData[i]));
}
iWidth=gebi("inpScreenWidth").value*1;
iHeight=gebi("inpScreenHeight").value*1;
fFPS=gebi("inpFPS").value*1;
fTotalSecs=gebi("inpTotalSecs").value*1;
fTotalFrames=fTotalSecs*fFPS;
oCanvas=gebi("myCanvas");
oCanvas.setAttribute("width", iWidth);
oCanvas.setAttribute("height", iHeight);
oCtx=oCanvas.getContext("2d");
iIdxFrameBegin=gebi("inpFrameBegin").value*1-1; // base0
//iIdxFrameEnd=fTotalFrames-1; // nmomtf
iIdxFrameEnd=gebi("inpFrameEnd").value*1-1; // base0
iIdxFrame=iIdxFrameBegin-1;
goDrawingCore();
}
function goDrawingCore()
{
iIdxFrame++;
if(iIdxFrame>iIdxFrameEnd) { return; }
{
//var fCurrentSeconds=((iIdxFrame+1)/fTotalFrames)*fTotalSecs;
var fCurrentSeconds=(iIdxFrame+1)/fFPS;
var strCurrentImageName="";
var iIdxDataBegin=-1;
var iIdxDataEnd=-1;
for(var iIdxData=(oaDrawingData.length-1); iIdxData>-1; iIdxData--)
{
var o1=oaDrawingData[iIdxData];
if(fCurrentSeconds>=o1.fSecs1)
{
iIdxDataEnd=iIdxData;
strCurrentImageName=o1.strImageName;
break;
}
}
for(var iIdxData=iIdxDataEnd; iIdxData>-1; iIdxData--)
{
var o1=oaDrawingData[iIdxData];
if(o1.strImageName==strCurrentImageName)
{
iIdxDataBegin=iIdxData;
}
else
{
break;
}
}
// 繪製這個 frame
for(var iIdxCB=iIdxDataBegin; iIdxCB<=iIdxDataEnd; iIdxCB++)
{
var o1=oaDrawingData[iIdxCB];
if(o1.fSecs1==o1.fSecs2)
{
oCtx.drawImage(gebi(o1.strBGImageId),0,0);
}
else
{
// void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
var x=o1.fBegX1;
var y=o1.fBegY1;
var w=o1.fBegX2-o1.fBegX1;
var h=o1.fEndY1-o1.fBegY1;
if(fCurrentSeconds<o1.fSecs2)
{
h=(fCurrentSeconds-o1.fSecs1)/(o1.fSecs2-o1.fSecs1)*h;
}
oCtx.drawImage(gebi(o1.strFGImageId),x,y,w,h,x,y,w,h);
}
}
// 儲存
oCanvas.toBlob(myBlobCallback.bind(null, iIdxFrame),'image/jpeg',1);
}
}
function myBlobCallback(iIdxFrame, oBlob)
{
var oUrl=URL.createObjectURL(oBlob);
var oLink=document.createElement('a');
oLink.innerText='Download';
oLink.href=oUrl;
oLink.download='frame'+(iIdxFrame+1)+'.jpg';
oLink.click();
window.setTimeout(goDrawingCore, 1);
}
</script>
</body>
</html>