Save Selection As JPEG - Quality Options?

Started by Dave9, February 28, 2025, 01:57:45 AM

Previous topic - Next topic

Dave9

The few times I used the menu option, "Save Selection as JPEG", since it was JPEG I had just assumed it was a very high quality preset, but today I discovered that it seems to be closer to an amount of compression some apps call quality # 85?

I can't find any menu settings to change this.  Is there a workaround, or if not, could the option be put into a newer version of  Avidemux to save at a higher quality level or PNG instead of JPG? 

I'm aware that there is at least one way to do that, with ffmpeg command line, but that's quite clunky, cumbersome to do if Avidemux could just allow PNG?

eumagga0x2a

Avidemux uses the libavcodec default for (M)JPEG which is the highest quality.

Exporting selection as PNG images wastes a huge amount of disk space, can be accomplished via a script

adm = Avidemux()
ed = Editor()
ui = Gui()

if not ed.nbSegments():
    ui.displayError("No video", "Please load a video prior to running this script")
    return 0

start = adm.markerA
end = adm.markerB
if start > end:
    swap = end
    end = start
    start = swap

if not adm.setCurrentPts(start):
    ui.displayError("Seek error", "Cannot seek to the start of the selection")
    return 0

outfile = ui.fileWriteSelectEx("Select output filename", "png")
if outfile is None:
    return 0

def getTimeStampString(time):
    ct = int(time / 1000)
    mss = int(ct)
    mss -= int(mss / 1000) * 1000
    msecondsString = str(mss)

    if mss < 100:
        msecondsString = "0" + msecondsString

    if mss < 10:
        msecondsString = "0" + msecondsString

    ct /= 1000
    ss = int(ct)
    ss -= int(ss / 60) * 60
    secondsString = str(ss)
    if ss < 10:
        secondsString = "0" + secondsString

    ct /= 60
    mm = int(ct)
    mm -= int(mm / 60) * 60
    minutesString = str(mm)
    if mm < 10:
        minutesString = "0" + minutesString

    ct /= 60
    hh = int(ct)
    hoursString = str(hh)
    if hh < 10:
        hoursString = "0" + hoursString

    timestamp = hoursString + "h-" + minutesString + "m-" + secondsString + "s-" + msecondsString + "ms"
    return timestamp

count = 0
fileNameNoExtension = (splitext(outfile))[0]

while True:
    pts = ed.getCurrentPts()
    if pts >= end:
        break
    if not adm.savePng(fileNameNoExtension + "-" + getTimeStampString(pts) + ".png"):
        break
    count += 1
    if not adm.seekFrame(1):
        break

if not count:
    ui.displayError("Error", "No PNG images saved")
    return 0

ui.displayInfo("Finished", str(count) + " PNG images saved")
return count

Put it with .py as filename extension into the "custom" subfolder of Avidemux profile directory to make it accessible via menu.

Dave9

#2
Thank you for the script!

One question though, I am trying to later load these back into Avidemux in sequence, and if I understand correctly, they need to be in a single digit numerical sequence to be able to just pick the first frame and have Avidemux load successive files as frames, for  example 1.png, 2.png, 3.png? 

With the output of that script, it is not doing that.  Is there some other code I can put on this line (or elsewhere) to name the images in consecutive order?

if not adm.savePng(fileNameNoExtension + "-" + getTimeStampString(pts) + ".png"):

I'm not good at python and tried adding these lines and altering the adm.savePng line as shown below, but that just created an error message.  Obviously I don't know what I'm doing.  :)

def next_file_name():
    num = 1
    while True:
        file_name = '%d' % num
        if not os.path.exists(file_name):
            return file_name
        num += 1


while True:
    pts = ed.getCurrentPts()
    if pts >= end:
        break
    if not adm.savePng(next_file_name + ".png"):
        break
    count += 1
    if not adm.seekFrame(1):
        break

A bit of  background on what I'm  trying to achieve is to make video play in reverse, so after I have the sequential file names, I run a batch job on them to rename in the opposite order, then can load the first sequence of images into Avidemux, then append the reverse named sequence of images to achieve that.  I can already accomplish that with the Save Selection As JPG file save method built into Avidemux, because it saves them consecutively named as (filename)-00001.jpg, (filename)-00002.jpg, etc.

If there is an easier way to do this, that would be interesting as well.

Dave9

#3
While I am still interested in what is wrong with the code, I have found a workaround, which is to just run the batch job I use to create new, renamed files in reverse numerical order, and run that a second time on the reverse named files, to create a second set of files in the original order again, with sequential file names so Avidemux will load all in sequence.  Having to run the batch twice, does take twice as long, and twice as much storage space, but it's not a big problem for short time period video segments.

This is the batch file I use to do that:

@echo off
if not exist "reverse" mkdir reverse
del /Q "reverse\*"
SETLOCAL EnableDelayedExpansion
set /A var=10000
for /f "tokens=*" %%s in ('dir /b *.png ^| sort') do (
echo f | xcopy /f /y %%s reverse/!var!.png
set /A var-=1
)

del "reverse\10000.png"
pause

Dave9

#4
I would like to share my observations that started all this.

I loaded a video in Avidemux, use the Save Selection As JPEG on a frame.  215KB file size.

Next I ran this script to make a PNG on the same frame. 1.68MB

Next I loaded this PNG into an image editor and saved it at highest quality JPEG. 557KB

Next I loaded this PNG into a smartsaver app that lets you pick parameters as well.  781KB

In order to get that 1.68MB PNG down to a 215KB file size JPG, I have to set the smartsaver app, all the way down to quality 86 out of 100, to get about the same JPEG file size.

I am possibly being picky, but see the results where I have  zoomed in 20X to show the difference (and used pixel resizing, not bicubic resampling, to preserve the detail.  The default 215K JPG saved, has more artifacts than the larger filesize JPGs saved by the other apps, which have more than the PNG.

This is why I'd as soon just use PNG, temporary storage space is cheap/free.

You cannot view this attachment.

eumagga0x2a

There is no os class in tinyPy, if I am not mistaken.

Loading sequentially named images in Avidemux in reverse order: Edit -> Preferences -> Import -> Pictures -> [v] Load sequentially named pictures in reverse order

(I cannot use "x" for a checkmark as the forum software interprets it as a list)

I'll post a ready-to-use script with the desired output filename pattern later.

Avidemux invokes the MJPEG encoder in libavformat at default quality which is 2. It is possible to ask for 1, which generates output roughly 50% larger in size with almost no perceptible quality gain. I am not yet convinced this needs to be made tunable, the default is good enough for maybe 99% of users. However, extending the quality range for the MJPEG encoder definitely makes sense, thank you.


Dave9

#6
I had overlooked that setting for load sequentially named pictures in reverse order, thanks for pointing it out.

I don't feel like larger file size is an issue when dealing with short lengths of video, where the videos take up orders of magnitude more storage than a few thousands of pictures, temporarily stored then deleted once made back into a video.

eumagga0x2a

Here comes a version of the script which saves up to 99999 sequentially named PNG files from a selection:

adm = Avidemux()
ed = Editor()
ui = Gui()
mx = 99999
pad = "00000"

def zeropad(nb):
    c = int(nb)
    if c < 0 or c > mx:
        return "invalid"
    if not c:
        return pad
    a = 0
    while c > 0:
        a += 1
        c = int(c/10)
    return pad[a:] + str(nb)

if not ed.nbSegments():
    ui.displayError("No video", "Please load a video prior to running this script")
    return 0

start = adm.markerA
end = adm.markerB
if start > end:
    swap = end
    end = start
    start = swap

if not adm.setCurrentPts(start):
    ui.displayError("Seek error", "Cannot seek to the start of the selection")
    return 0

outfile = ui.fileWriteSelectEx("Select output filename", "png")
if outfile is None:
    return 0

count = 0
fileNameNoExtension = (splitext(outfile))[0]

while True:
    if ed.getCurrentPts() >= end:
        break
    if not adm.savePng(fileNameNoExtension + "-" + zeropad(count) + ".png"):
        break
    count += 1
    if count > mx:
        break
    if not adm.seekFrame(1):
        break

if not count:
    ui.displayError("Error", "No PNG images saved")
    return 0

ui.displayInfo("Finished", str(count) + " PNG images saved")
return count

eumagga0x2a

With future nightly builds, you should be able to save a selection to sequentially named JPG files with higher quality using the MJPEG encoder and the "Video only" RAW muxer: muxerRaw: add options to output a file per frame, to override extension.

Dave9

You must have the ultimate passion for this because your support level is outstanding.

Thank you.