View Full Version : Append a still image respecting aspect ratio and possibly adding borders
PerDeConf
5th April 2020, 14:16
I'm looking to append a still image to a video clip.
I found an old thread (https://forum.doom9.org/showthread.php?t=155836) that deals with this issue.
Unfortunately I'm facing distortion problems and can't adapt the script.
Gavino mentioned at the time that this script should be improved...
the photo is narrower than the video. When the photo is wider, you want to add borders on the top and bottom instead of the sides
Depending on the initial size of the video, the image is exaggeratedly distorted making its content unreadable.
Can someone show me the way because abstraction is not my strong point? (such scripts probably already exist)
https://i.postimg.cc/LXbDtm2z/screenshot-397.png
v=DirectShowSource("F:\Vidéos\Enfants\Minuscule\gay-minus.avi")
pic="D:\pic\screenshot_395.png"
t=10 #[time in sec]
fc=Round(t*FrameRate(v)) #frame count
####################################################
# Option Zoom
d=20 # start zooming
n = 50 # no of frames for still
endzoom = 4.0
####################################################
ImageSource(pic).ConvertToRGB32()
AssumeFPS(v).Loop().Trim(0,fc-1)
#~ Animate(last, d, 119, "SimpleZoomBox", 1.0, 0, 0, endzoom, (endzoom * -0.10), (endzoom * 0.9)) # Option Zoom
AudioDub(BlankClip(v, length=t))
fv=last # frozen vid
vasr=float(v.Height)/float(v.Width) #video aspect ratio
pasr=float(fv.Height)/float(fv.Width) #picture aspect ratio
nW=INT(fv.Width*(pasr*vasr)) #new Width
xx=(v.Width-nW) #sum of the lateral borders
Spline36Resize(nW, v.Height).AddBorders(xx/2,0,xx/2,0)
#~ ConvertToYV12()
#~ v ++ last #joining both clips
last #only for picture clip
FranceBB
5th April 2020, 15:43
Ok so basically if I got it right you want to append images of different sizes by respecting the Aspect Ratio and therefore have them resized automatically without distortion but by adding borders instead?
Well, that seems a pretty good job for Frosty Border! (https://forum.doom9.org/showthread.php?t=175039)
StainlessS
5th April 2020, 16:19
May be of interest [ImageSplicer, Included in RT_Stats zip]:- https://forum.doom9.org/showthread.php?p=1713501&highlight=ImageSplicer#post1713501
Predecessor [PicVidShow]:- https://forum.doom9.org/showthread.php?p=1604043#post1604043
EDIT: This seems to be an update of PicVidShow. [remove GScript Wrapper for AVS+]
# EDIT To Suit # https://forum.doom9.org/showthread.php?p=1604043#post1604043
VID = True # True == Video : False == Pics, see IN_FILES directories/wildcards
IN_FILES = (VID) ? "D:\VID\*.AVI|MP*G" : "D:\Pics\*.BMP|JP*G|PNG" # Added '|' Pipe separator for multiple Wildcard in Extensions ONLY.
AUTOCROP = True # Crop off black borders.
SHOWBORDER = True # True Show replaced border in Red, else Black. (Test AUTOCROP)
AUTOLEVEL_STRENGTH = 0.666 # AutoLevel Strength 0.0 -> 1.0
SHOWAUTOLEVEL = True # Right Hand Side Only AutoLeveled
#
FAR = 4.0/3.0 # Required Output Frame Aspect Ratio
SzLimit = 1024
DEBUG = False # Debug
# ---------------------
BORDER = (SHOWBORDER) ? $FF0000: (VID) ? $101010 : $000000 # Color for replaced borders.
SUBS = True # Subtitles on clips/images
DEBUG_QBC = True # Debug QueryBorderCrop
DEBUG_QLMM = True # Debug QueryLumaMinMax
FPS = (VID) ? 30000.0/1001.0 : 1.0 # Whatever.
OUT_FILE = "FILE.List" # Dont need to change really, just a name.
#--------------------------------------------------------------------------------
GScript("""
picclip = 0 # dummy for now
result=RT_WriteFileList(IN_FILES,OUT_FILE) # Create a listing file of Pic files
Assert((result!=0), "No files found")
FILELIST=RT_ReadTxtFromFile(OUT_FILE) # Get list of files
FILES=RT_TxtQueryLines(FILELIST) # Query Number of lines in String ie number of files.
if(DEBUG){RT_Debug("Found files = " + String(FILES))}
# Prescan to ascertain max wid/hit of all files, also Autocrop & Auto Levels
MAXH=0 MAXW=0 # init max wid and hit
CROPCOORDS=""
AUTOLEVELS=""
AUTOLEVEL=(AUTOLEVEL_STRENGTH>0.0) # Adjust using Levels.
For(i=0,FILES-1) {
FN=RT_TxtGetLine(FILELIST,i) # Filename of avi file i
if(DEBUG){RT_Debug(String(i)+") Getting File dimensions",FN)}
if(VID) {
k=DirectshowSource(FN)
k=k.ConvertToYUY2().Trim(0,0)
} else {
k=ImageReader(FN,end=0).ConvertToRGB32() # With Bmap, some come up as RGB24 bit some RGB32.
}
OrgH=k.height OrgW=k.width
if(DEBUG){RT_Debug ("Orig Width =",String(orgW),"Orig Height =",String(OrgH))}
if (AUTOCROP) {
QBC = k.QueryBorderCrop(samples=24,laced=VID,debug=(DEBUG&&DEBUG_QLMM))
QBC = RT_TxtGetLine(QBC,2) # CropMore
CROPCOORDS=RT_TxtAddStr(CROPCOORDS,QBC) # Avisynth Bugfix, same as CROPCOORDS = CROPCOORDS + QBC + Chr(10)
Eval(QBC)
k=k.Crop(QBCropXM,QBCropYM,QBCropWM,QBCropHM)
}
if (AUTOLEVEL) {
QLMM = k.QueryLumaMinMax(debug=(DEBUG&&DEBUG_QLMM))
Eval(QLMM)
AUTOLEVELS=RT_TxtAddStr(AUTOLEVELS,QLMM) # Avisynth Bugfix, same as AUTOLEVELS = AUTOLEVELS + QLMM + Chr(10)
}
H=k.height W=k.width
If(MAXH<H) {MAXH=H}
If(MAXW<W) {MAXW=W}
if(DEBUG){RT_Debug("MAX Width So Far =",String(MAXW),"MAX Height So Far =",String(MAXH))}
if(DEBUG){RT_Debug(" ----------------------------- ")} # line separator
}
maxFAR = MAXW / MAXH
if(FAR >= maxFAR) {
canvasheight = (MAXH > SzLimit) ? SzLimit : MAXH
canvasheight = ((canvasheight + 1) / 2) * 2
canvaswidth = Int((canvasheight * FAR + 2) / 4) * 4
} else {
canvaswidth = (MAXW > SzLimit) ? SzLimit : MAXW
canvaswidth = ((canvaswidth + 2) / 4) * 4
canvasheight = Int((canvaswidth * FAR + 1) / 2) * 2
}
if(DEBUG){RT_Debug("SETTING CanvasWidth =",String(canvaswidth)," CanvasWeight =",String(canvasheight))}
if(DEBUG){RT_Debug(" ----------------------------- ")} # line separator
For(i=0,FILES-1) {
FN=RT_TxtGetLine(FILELIST,i) # Filename of avi file i
if(DEBUG){RT_Debug(string(i)+ ")","Processing File",FN)}
if(VID) {
k=DirectshowSource(FN).ConvertToYUY2().Trim(0,0)
} else {
k=ImageReader(FN,end=0).ConvertToRGB32() # With Bmap, RGB24 problems with addborders
}
OrgH=k.height OrgW=k.width
if (AUTOLEVEL) {
# We do AUTOLEVELS before crop, because of SHOWAUTOLEVEL
QAT = RT_TxtGetLine(AUTOLEVELS,i)
Eval(QAT)
if(k.IsYUV()) {
CSMin = 16
CSMax = 235
} else {
CSMin = 0
CSMax = 255
}
TMP_K=k
ALMin = Int(CSMin - ((CSMin - QLMMMin) * AUTOLEVEL_STRENGTH) + 0.5) # Round Up
ALMax = Int(CSMax - ((CSMax - QLMMMax) * AUTOLEVEL_STRENGTH)) # Round down
k = k.Levels(ALMin,1.0,ALMax,CSMin,CSMax,Coring=False) # DONT USE Coring
if(SHOWAUTOLEVEL) {
tmpw=int((k.width/4)*2)
Left = TMP_K.Crop(0,0,tmpw,-0)
Right= k.Crop(tmpw,0,-0,-0)
k=StackHorizontal(Left,Right)
}
}
if (AUTOCROP) {
QBC = RT_TxtGetLine(CROPCOORDS,i)
Eval(QBC)
k=k.Crop(QBCropXM,QBCropYM,QBCropWM,QBCropHM)
}
iWidth=k.Width() iHeight=k.Height()
cFAR = float(canvaswidth) / canvasheight
iFar = float(iWidth) / iHeight
Left=0 Top=0 Right=0 Bot=0
if(iFAR >= cFAR) {
oWid = canvaswidth
oHit = (oWid*iHeight/iWidth/4)*4
Top = (canvasheight-oHit) / 2
Bot = canvasheight - oHit - Top
} else {
oHit = canvasheight
oWid = (oHit*iWidth/iHeight/4)*4
Left = (canvaswidth-oWid) / 2
Right = canvaswidth - oWid - Left
}
RT_Debug("Resize","oWid="+String(oWid),"oHit="+String(oHit),"Left="+String(Left),"Top="+String(Top),"Rgt="+String(Right),"Bot="+String(Bot))
k = k.Spline36Resize(oWid,oHit).addborders(Left,Top,Right,Bot,BORDER).assumefps(fps)
if(SUBS) {
k=k.subtitle(String(i) + "/"+String(FILES-1) +" " + FN)
k=k.subtitle("Orig width=" + string(orgW) +" " + "Orig height=" + string(orgH) +" FAR="+String(Float(orgW) /orgH, "%.2f"),20,20)
if (AUTOCROP) {k=k.subtitle("Crop width=" + string(iWidth) +" " + "Crop height=" + string(iHeight) +" FAR="+String(Float(iWidth) /iHeight, "%.2f"),20,40)
} else {k=k.subtitle("NO AUTOCROP",20,40)}
k=k.subtitle("New width=" + string(oWid) +" " + "New height=" + string(oHit)+" FAR="+String(Float(oWid)/oHit,"%.2f"),20,60)
k=k.subtitle("Out width=" + string(k.Width) +" " + "Out height=" + string(k.Height)+" FAR="+String(Float(k.Width)/k.Height,"%.2f"),20,80)
if(AUTOLEVEL) {
k=k.subtitle("Auto Levels(" + String(ALMin) + ",1.0," + String(ALMAx) + "," + String(CSMin) + "," + String(CSMax) + ")",20,100)
k=k.subtitle("QLMMMin = " + String(QLMMMin) + " QLMMMax = " +String(QLMMMAx),20,120)
k=k.subtitle("AUTOLEVEL @ " + String(AUTOLEVEL_STRENGTH*100.0,"%.0f") + "%",20,140)
} else {k=k.subtitle("NO AUTOLEVEL",20,100)}
}
picclip = (IsClip(picclip)) ? picclip ++ k : k # Append to clip so far
if(!VID){k=k.ConvertToRGB24()}
if(DEBUG){RT_Debug(" ")} # line separator
if(DEBUG){RT_Debug(" ----------------------------- ")} # line separator
}
""")
PicClip
EDIT: ImageSplicer_Client_2.avs [for 1st lnk]
import(".\ImageSplicer.avs")
FN=".\*.BMP|JPG|JPE|JPEG|PNG|TGA|TIF|GIF|TIFF"
SZLIMIT = -768 # Height=Abs(SZLIMIT)
FAR = 4.0/3.0
AUTOCROP = TRUE
STRENGTH = 0.66
SHOWAL = False
SHOWBORD = False
SUBS = False
CROPTHRESH = -0.5
FPS = 1.0
WMOD = 4
HMOD = 4
DEBUG = TRUE
IGNORE = 0.4
BORDCOL = $001144 # Added for goorawin
ATM = 0.5 # Added, arg to QueryborderCrop
myName="ImageSplice_Client_2: "
IsAvsPlus=(FindStr(UCase(versionString),"AVISYNTH+")!=0) HasGScript=RT_FunctionExist("GScript")
Assert(IsAvsPlus || HasGScript,RT_String("%sNeed either GScript or AVS+",myName))
Anim = ImageSplicer(FN,SzLimit=SZLIMIT,Far=FAR,AutoCrop=AUTOCROP,AutoLevelStrength=STRENGTH,ShowAutoLevel=SHOWAL,ShowBorder=SHOWBORD,
\ Subs=SUBS,CropThresh=CROPTHRESH,Fps=FPS,Wmod=WMOD,Hmod=HMOD,Debug=DEBUG,IGnore=IGNORE,BorderColor=BORDCOL,ATM=ATM)
OUT_SecondsPerFrame = 5 # How long each frame is diplayed for
OUT_FPS = 25.0 # Final Output FrameRate
FADE_FRAMES = Int(OUT_FPS/2)
T1 = Int(OUT_SecondsPerFrame*OUT_FPS)
T2 = T1+FADE_FRAMES
anim.AssumeFPS(1).ChangeFPS(T2) # repeat each frame for OUT_SecondsPerFrame + ~0.5 sec overlap
Vid = Trim(FADE_FRAMES, -T1) # first chunk
GS="""
for (f=T2, Framecount-T2, T2) {
next = Trim(f, -T2) # next chunk, incl overlap
vid = Dissolve(vid, next, FADE_FRAMES)
}
"""
HasGScript ? GScript(GS) : Eval(GS) # Use GSCript if installed (loaded plugs override builtin)
Return Vid.AssumeFPS(OUT_FPS)
EDIT: And ImageSplicer_Client_1.avs
Import(".\ImageSplicer.avs")
FN=".\*.BMP|JPG|JPE|JPEG|PNG|TGA|TIF|GIF|TIFF"
SzLimit= -768
FAR=1.333
AutoCrop=True
AutoLevelStrength=1.0
ShowBorder=True
ShowAutoLevel = True
Subs = True
CropThresh = -0.5
FPS = 1.0
WMOD = 4
HMOD = 4
IGNORE = 0.4
ScanPerc = 49.0
Baffle = 4
Debug = True
Debug_QBC = False
Resizer = "Spline64resize(_W_,_H_)" # Change to eg "Spline36resize(_W_,_H_)"
Matrix = 2
ATM = 0.5
TimeStart = RT_Timer()
ImageSplicer(FN,SzLimit,FAR=FAR,AutoCrop=AutoCrop,AutoLevelStrength=AutoLevelStrength,ShowBorder=ShowBorder,ShowAutoLevel=ShowAutoLevel,
\ Subs=Subs, CropThresh=CropThresh,Fps=FPS,Wmod=WMOD,Hmod=HMOD,Baffle=Baffle,ScanPerc=ScanPerc,Debug=Debug,Debug_QBC=Debug_QBC,
\ Ignore=IGNORE,Resizer=Resizer,matrix=matrix,ATM=ATM)
#ImageSplicer(FN,SzLimit=1024)
TimeEnd = RT_Timer()
SubTitle("TIME = "+String(TimeEnd-TimeStart,"%6.2f")+" secs",align=9,Y=0)
johnmeyer
5th April 2020, 16:51
Why not just do this in a video editor (NLE)? It would take just a few seconds ...
PerDeConf
5th April 2020, 20:14
Thank you, three of you,
As you can see, this is my second post and I'm very beginner with avisynth.
This post is also an exercise to learn more about avisynth and try to improve an existing script. So I think I should have refrained from mentioning the existence of an already existing script.
FranceBB, I studied the FrostyBorder function but I don't think it's adapted to my needs : this function applies to a video while I'm trying to manipulate an image (then transformed it into a video).
Add vertical or horizontal black borders so that the generated image is the same size as the video clip.
You can even guess in my script that I want to zoom in this picture.
The StainlessS proposal is even more complex for me; I clearly don't yet have the knowledge for it.
Please be indulgent for my perhaps clumsy remarks. The script is almost working but I need your knowledge to make it really usable.
FranceBB
6th April 2020, 08:18
Thank you, three of you,
FranceBB, I studied the FrostyBorder function but I don't think it's adapted to my needs : this function applies to a video while I'm trying to manipulate an image (then transformed it into a video).
Well, once you use an image indexer in Avisynth like ImageSource or FFImageSource etc there's no "image" anymore, as every image is essentially going to be still video inside Avisynth of a certain number of frame, a certain length, a certain framerate and a certain aspect ratio. I noticed you're using AVSPmod, so you should notice that even with a simple "ImageSource" you see something in the bottom right part of the screen about resolution, frame rate, colorspace and bit depth.
I'm trying to manipulate an image (then transformed it into a video).
Add vertical or horizontal black borders so that the generated image is the same size as the video clip.
Yes I know, that's what I was trying to explain, but let's do that step by step. Shall we? :P
Alright, I've collected four randomly chosen pictures from the internet at different aspect ratios and resolutions:
Image 1 - Margot Robbie (https://i.imgur.com/ijwAK0X.jpg)
Image
Format : JPEG
Width : 500 pixels
Height : 500 pixels
Color space : YUV
Chroma subsampling : 4:2:0
Bit depth : 8 bits
Compression mode : Lossy
Stream size : 46.9 KiB (100%)
ColorSpace_ICC : RGB
Image 2 - Taylor Swift (https://i.imgur.com/7Gw5RWn.jpg)
Image
Format : JPEG
Width : 1 778 pixels
Height : 2 667 pixels
Color space : YUV
Chroma subsampling : 4:2:0
Bit depth : 8 bits
Compression mode : Lossy
Stream size : 983 KiB (100%)
Image 3 - Elsa Hosk (https://i.imgur.com/fp8JNd2.jpg)
Image
Format : JPEG
Width : 800 pixels
Height : 1 200 pixels
Color space : YUV
Chroma subsampling : 4:4:4
Bit depth : 8 bits
Compression mode : Lossy
Stream size : 294 KiB (100%)
Image 4 - a cat (https://i.imgur.com/UJK6CZ4.jpg)
Image
Format : JPEG
Width : 960 pixels
Height : 640 pixels
Color space : YUV
Chroma subsampling : 4:2:0
Bit depth : 8 bits
Compression mode : Lossy
Stream size : 70.3 KiB (100%)
Now, first of all let's index them within our script. What I'm gonna use is ImageSource. The reason is that it will allow me to easily specify the number of frames that can be used and I suggest it over FFImageSource also because if you have .png files then FFImageSource will NOT work as it only indexes JPEG files and we don't know whether it will ever support other kind of images or not.
So, let's start:
ImageSource("margot-robbie.jpg", start=0, end=1000, fps=25, pixel_type="RGB32")
https://i.imgur.com/loSsmt7.png
As you can see, Margot ain't a picture anymore, it's now a video which lasts 1000 frames, has 25fps and is in 8bit RGB32.
Now, we have three more images, but to append them they gotta be the same as our final stream has to be consistent which means they gotta be at the same resolution, color space, framerate and bit depth, so how do we do that? Well, let's suppose that our output is 848x480 at 25fps. We can use FrostyBorder to make it happen:
ImageSource("margot-robbie.jpg", start=0, end=1000, fps=25, pixel_type="RGB32")
Converttoyv12()
FrostyBorders(848,480, frosty=true)
https://i.imgur.com/ONLa88S.png
As you can see, we now have a perfectly valid 848x480 4:2:0 planar 8bit (yv12) 16:9 1.77FF stream.
Let's do that with all the other images and then let's add them together, shall we?
clip=ImageSource("taylor-swift.jpg", start=0, end=1000, fps=25, pixel_type="RGB32")
my_initial_width=clip.Width()
my_initial_heigth=clip.Height()
i_width = my_initial_width / 2 * 2
i_height = my_initial_heigth / 2 * 2
Spline64Resize(clip, i_width, i_height)
Converttoyv12()
FrostyBorders(848,480, frosty=true)
In this case we gotta use a clever trick to round the value to the closest even value 'cause the image has an odd resolution which most Avisynth filters can't handle, including TemporalSoften used by FrostyBorder, but for the rest, it's the same.
https://i.imgur.com/vh9MvZn.png
ImageSource("elsa-hosk.jpg", start=0, end=1000, fps=25, pixel_type="RGB32")
Converttoyv12()
FrostyBorders(848,480, frosty=true)
https://i.imgur.com/UutH879.png
and lastly our picture of a cat:
ImageSource("cat.jpg", start=0, end=1000, fps=25, pixel_type="RGB32")
Converttoyv12()
FrostyBorders(848,480, frosty=true)
https://i.imgur.com/6OJ1gjv.png
Ok, so now we're ready to append them together and get a 4000 frames 848x480, 25fps 4:2:0 planar 8bit video out of our images for our fictional actress/singer/model/animal advertising company doomed to fail xD
like so:
ImageSource("margot-robbie.jpg", start=0, end=1000, fps=25, pixel_type="RGB32")
Converttoyv12()
FrostyBorders(848,480, frosty=true)
img1=last
clip=ImageSource("taylor-swift.jpg", start=0, end=1000, fps=25, pixel_type="RGB32")
my_initial_width=clip.Width()
my_initial_heigth=clip.Height()
i_width = my_initial_width / 2 * 2
i_height = my_initial_heigth / 2 * 2
Spline64Resize(clip, i_width, i_height)
Converttoyv12()
FrostyBorders(848,480, frosty=true)
img2=last
ImageSource("elsa-hosk.jpg", start=0, end=1000, fps=25, pixel_type="RGB32")
Converttoyv12()
FrostyBorders(848,480, frosty=true)
img3=last
ImageSource("cat.jpg", start=0, end=1000, fps=25, pixel_type="RGB32")
Converttoyv12()
FrostyBorders(848,480, frosty=true)
img4=last
img1++img2++img3++img4
And the final result is this crappy video: https://we.tl/t-qdWYrfD0E5
I hope this is clear enough to clarify your doubts.
Now I really gotta "go" to work (where "go" means stop scrolling doom9, shut down Windows XP, boot into Windows 10 here at home, and actually train a flipping object/people/setting/speech recognition AI which is exactly what I've been doing for months ever since they re-assigned me from the encoding department and I'm sick of it... T_T)
PerDeConf
7th April 2020, 19:39
Thank you very much FranceBB for such a well-documented explanation.
Your post enlightened me on aspects of Avisynth that I had not assimilated well. It is now much clearer and I thank you for the explicit and very pleasant use of images, I confess.
I took my time and with a little trial and error I managed to get the result I wanted. In the end I preferred to use Imageborders because by zooming in on the edge of the image, Frostyborders let a black border appear, too different from the nice effect I expected (see attachment).
Finally, I want to thank raffriff42 for his simpleZoombox script that made my job so much easier and of course hello_hello for FrostyBorders.
So here's what I came up with:
v = dss2("(2020-04-07 09-00) L'heure des pros [CNEWS].ts", lavs="l3", lavd="l3", preroll=64)
a = DirectShowSource("(2020-04-07 09-00) L'heure des pros [CNEWS].ts", video=False)
AudioDub(v,a)
trim (1458, 1899)
LanczosResize(1280,720)
v=last
pic="D:\pic\screenshot_395.png"
t=5 #[time in sec]
fc=Round(t*FrameRate(v)) #frame count
# Option Zoom
endzoom = 2.0
ImageSource(pic, pixel_type="RGB32")
Converttoyv12()
ImageBorders(Width(v),height(v))
AssumeFPS(v).Loop().Trim(0,fc-1)
ConvertToRGB32()
Animate(last,
\ 1,
\ 119,
\ "SimpleZoomBox",
\ 1.0, 0, 0,
\ endzoom, (endzoom * 0.10), (endzoom * 0.9))
AudioDub(BlankClip(v, length=t))
ConvertToYV12()
v ++ last #joining both clips
#~ last #only for picture clip
https://i.postimg.cc/y88p0JXg/screenshot-398.png
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.