Convert produces different result than original command
I'm currently using Arc inside a project but I a strange issue with a convert command, here is my transform method:
@versions [
:original, :"1920x652"
]
def transform(:original, _), do: :noaction
def transform(version, _) do
size = Atom.to_string(version)
{:convert, "-strip -interlace Plane -quality 85% -gaussian-blur 0.05 -resize #{size} -format jpg", :jpg}
end
It produce the desired result but it differ from what I get by running the command myself:
- From the transform
- original image 1.6 Mo
- generated image 662,5 Ko
- From the command line
convert in.png -strip -interlace Plane -quality 85% -gaussian-blur 0.05 -resize 1920x652 -format jpg out.jpg- original image 1.6 Mo
- generated image 120,2 Ko
I'm getting very similar behavior. I'm trying to keep it as basic as possible using this transform/2 in Phoenix 1.3.
def transform(:jpg, _) do
{:convert, "-resize x400,", :jpg}
end
I get a jpg, but the quality is so terrible you can barely make out the image.
But if I run the command myself using the exact same scope, I get a perfect jpg.
System.cmd("convert",["-resize","x400", "somedir","test.jpg”])
Sorry this issue has not been solved yet, but i seem to have figured out the problem..
The problem lies in a mixture of convert and the following code:
First, the processor prepares the transformation:
def process(definition, version, {file, scope}) do
transform = definition.transform(version, {file, scope})
apply_transformation(file, transform)
end
# [...]
defp apply_transformation(file, {cmd, conversion, _}) do
apply_transformation(file, {cmd, conversion})
end
defp apply_transformation(file, {cmd, conversion}) do
Arc.Transformations.Convert.apply(cmd, Arc.File.ensure_path(file), conversion)
end
As you see above, your 3-tuple gets turned into a 2-tuple, where the last part gets dropped. I think there are some reasons this is not used in the transformation step, so as to not make assumptions about the underlying conversion program, and the filetype it expects.
In the next step, before the transformation is applied, a tmpfile is aquired:
defmodule Arc.Transformations.Convert do
def apply(cmd, file, args) do
new_path = Arc.File.generate_temporary_path(file)
def generate_temporary_path(file \\ nil) do
extension = Path.extname((file && file.path) || "") # <-- here, the file extension of the original file is used
file_name =
:crypto.strong_rand_bytes(20)
|> Base.encode32()
|> Kernel.<>(extension) # <--
Path.join(System.tmp_dir, file_name)
end
and then lastly, the command is run.
System.cmd(program, args_list(args), stderr_to_stdout: true)
# yada yada
As you might have noticed, the command you (as well as i) have assumed was run convert input.png -blah -blah output.jpg, has in fact had output.png on the end. This causes issues, since imagemagick (and convert) does not actually let your format your image by specifying -format jpg.
It turns out the -format option is used for something completely different..
So convert sees and output file with .png, and assumes you want to keep the image in the png format. Once it has applied as many of the conversions as relevant for png, the conversion is completed, and the file now gets moved to its final location.
Here, the code respects the 3-tuple with its ext definition, and renames the file to whatever was specified.
This results in a png, with a jpg extension. Most browsers don't care, since they run on magick anyways. If you want to check the actual filetype, you can run the file command (found in most *nix) on your file.
More on imagemagick's magick filetype detection and how to override https://www.imagemagick.org/script/command-line-processing.php (Explicit image format section)
What the best fix for this issue would be, im not sure, but i think the current behavior is correct. Maybe adding some sort of helper / configuration option, letting us not only specify the final filetype, but also the intermediary filetype would be a solid all-round option.
In addition, its probably also a good idea to correct the docs to clarify that -format jpg does not change the filetype of your image.
maybe @stavro has an opinion on this?
TLDR: We are using imagemagick wrong
In the mean time, to provide a quick and easy fix, you can use the lambda based conversions to force the filetype:
def transform(_, _) do
{:convert, fn(input, output) -> "#{input} -some conversion -here jpg:#{output}" end, :jpg}
end
Add filetype: in front of the output filename, no spaces, where filetype is the type you want.
Thanks @pedep . I also ran into this problem today