Add text failing. Trying to match existing file.

Started by whelton, November 11, 2022, 06:22:20 PM

Previous topic - Next topic

whelton

I'm trying to split the tiles in a montage image into separate images and carry a subset of the exif data over into each tile image.

The montage has an exif field with a big chunk of json which includes a dictionary entry (containing more json) for each tiled image.

When I try to add the field "sd-metadata" to the new tile's exif data, I'm getting:

    0 image files updated
    1 image files unchanged

(btw The field name "sd-metadata" is not something I control. I have to use that field name (or "Sd-metadata") to make the image compatible with the tool in question.)

I gather that I need a special config file set up. So I put a config file named .ExifTool_config in my home directory. I pared down what was in the file to what I guess would be needed, and here is the resulting config file contents:

$ cat ~/.ExifTool_config
%Image::ExifTool::UserDefined = (
    'Image::ExifTool::PNG::TextualData' => {
        "sd-metadata" => { iTXt => 1 }
    }
);
%Image::ExifTool::UserDefined::Options = (
    CoordFormat => '%.6f',  # change default GPS coordinate format
    Duplicates => 1,        # make -a default for the exiftool app
    GeoMaxHDOP => 4,        # ignore GPS fixes with HDOP > 4
    RequestAll => 3,        # request additional tags not normally generated
);
1;

[EDIT: removing the second half of the above, as it looks unrelated. But maybe I need my own entries there? I haven't been able to figure out a syntax that works though. Does the config look OK disregarding the bottom half?]

My command for trying to add the exif data (inside a shell script) is this:

exiftool -PNG:sd-metadata="$tile_exif" $tile_dir/$tile_file

I'm also echoing the value of $tile_exif out as the script runs:

echo "[sd-metadata=$tile_exif]"

so we can see the value of that here, along with the sd-metadata= prefix added in the echo line:

[sd-metadata={"app_id":"lstein/stable-diffusion","app_version":"v1.15","model":"stable diffusion","model_hash":"e1441589a6f3c5a53f5f54d0975a18a7feb7cdf0b0dee276dfc3331ae376a053","model_id":"stable-diffusion-1.5","image": {"steps":50,"height":256,"threshold":0,"init_mask":null,"width":256,"prompt":[{"prompt":"painting of a flower,","weight":1}],"perlin":0,"init_img":null,"seed":2647603606,"cfg_scale":10,"postprocessing":null,"sampler":"k_lms","variations":[],"type":"txt2img"}}]

I can use the original tool that made the montage to instead make a single image. That single image has metadata in the format I am going for, so it serves as a good example of where I am trying to get to with these tile images.

Here is the field 'Sd-metadata' (not sure why the case is different, but believe me, I have tried different combinations of upper and lower case S) from a single-tile, non-montage image from the same tool. This is my reference target exif field and format I want. I don't care field order in json dictionaries (I don't think this post is about json, I hope we can think of it as just text). I've checked the json in a json validator and it validates. I also don't care about spaces unless exiftool does:

$ exiftool flower-001241.383747576.png
ExifTool Version Number         : 12.42
...
Sd-metadata                     : {"model": "stable diffusion", "model_id": "stable-diffusion-1.5", "model_hash": "e1441589a6f3c5a53f5f54d0975a18a7feb7cdf0b0dee276dfc3331ae376a053", "app_id": "lstein/stable-diffusion", "app_version": "v1.15", "image": {"steps": 50, "height": 256, "threshold": 0.0, "init_mask": null, "width": 256, "prompt": [{"prompt": "painting of a flower,", "weight": 1.0}], "perlin": 0.0, "init_img": null, "seed": 383747576, "cfg_scale": 10.0, "postprocessing": null, "sampler": "k_lms", "variations": [], "type": "txt2img"}}

In the exiftool command invocation quoted a few steps above you'll notice there are no quotes around sd-metadata (my custom field name I'm trying to use). I have tried every combination of quotes. Single, double, backslash-escaped single, backslash-escaped double, and no quotes, and no quotes but with a backslash-escaped hyphen. The hyphen is especially suspicious because in the config file, if I omit quotes, exiftool tries to do arithmetic on the hyphen-separated parts of the field name(!). Could it be that the hyphen is the problem? Or am I overlooking something else? What is missing here? Thank you for looking!


Phil Harvey

I've quickly read over your long post and I'll answer a few questions, but I haven't quite arrived at the exact issue, and you haven't provided a sample file with this metadata so I can't answer anything for certain.

Quote from: whelton on November 11, 2022, 06:22:20 PM[EDIT: removing the second half of the above, as it looks unrelated.

Yes, it is unrelated.

QuoteHere is the field 'Sd-metadata' (not sure why the case is different, but believe me, I have tried different combinations of upper and lower case S)

ExifTool capitalizes the first letter of a tag name if it generates it automatically from the tag ID.  You can provide a different "Name" in the tag definition if you want.

QuoteIn the exiftool command invocation quoted a few steps above you'll notice there are no quotes around sd-metadata (my custom field name I'm trying to use).

It doesn't matter for the command line since there are no special characters in the tag name from the point of view of the command interpreter (which is what, btw?  I don't know what shell you are using).

QuoteThe hyphen is especially suspicious because in the config file

Correct.  The hyphen is a minus operator in Perl, and must be quoted if used in a tag ID.

- Phil
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

StarGeek

Quote from: whelton on November 11, 2022, 06:22:20 PMHere is the field 'Sd-metadata' (not sure why the case is different, but believe me, I have tried different combinations of upper and lower case S)

I believe this is what your tag would look like with the changed name, though I haven't tested it.  Phil may correct me if I'm wrong.
%Image::ExifTool::UserDefined = (
    'Image::ExifTool::PNG::TextualData' => {
        "sd-metadata" => { iTXt => 1, Name => 'sd-metadata'}
    }
);
* Did you read FAQ #3 and use the command listed there?
* Please use the Code button for exiftool code/output.
 
* Please include your OS, Exiftool version, and type of file you're processing (MP4, JPG, etc).

whelton

#3
Quote from: Phil Harvey on November 13, 2022, 03:52:27 PMI've quickly read over your long post and I'll answer a few questions, but I haven't quite arrived at the exact issue, and you haven't provided a sample file with this metadata so I can't answer anything for certain.

Thank you for the reply! Now that I realize the config file is code, the evaluation of the '-' in the tag ID makes complete sense.

The shell I'm using is bash.

Here's an example of a single image that has the EXIF data (at least the syntax, values vary) I'm aiming for:



Here's an example of a montage image that has EXIF data all in one field for the 9 images it contains, in an "images" field which is an array of dictionaries of per-image data. This can be thought of as the source image, even though I have an intermediate step (already handled, out of scope for my question) where I parse out just the relevant JSON for a single image from the larger JSON in the montage image:



I hope imgur doesn't strip EXIF data.

Edit: I don't see my images, but I see the URLs when I edit. I assume that's because I'm in some sandbox as a new user on this forum. Hopefully you can see them; if not please let me know if I need to post in some other way.


whelton

Quote from: StarGeek on November 13, 2022, 04:27:19 PM
Quote from: whelton on November 11, 2022, 06:22:20 PMHere is the field 'Sd-metadata' (not sure why the case is different, but believe me, I have tried different combinations of upper and lower case S)

I believe this is what your tag would look like with the changed name, though I haven't tested it.  Phil may correct me if I'm wrong.
%Image::ExifTool::UserDefined = (
    'Image::ExifTool::PNG::TextualData' => {
        "sd-metadata" => { iTXt => 1, Name => 'sd-metadata'}
    }
);


Now I am getting at least that there is a distinction between the ID and the name. I expect this to help down the line when reading or re-reading some of the docs. Thank you!

StarGeek

Fixed your links, not that it's going to help

Quote from: whelton on November 14, 2022, 02:33:14 AMI hope imgur doesn't strip EXIF data.

Yes, imgur strips away all metadata from an image.

QuoteEdit: I don't see my images, but I see the URLs when I edit.

Quick imgur tutorial.  What you want to do is click your name in the upper right and select images


Then click on the image and copy the url under "Direct link"


QuoteHopefully you can see them; if not please let me know if I need to post in some other way.

To see the metadata, you'll have to post to a site like dropbox or google drive.  Imgur and social media sites like Instagram strip away all metadata.  What is needed is a file sharing site, though some image sites, such as Flickr and Smugmug, do not strip away metadata.
* Did you read FAQ #3 and use the command listed there?
* Please use the Code button for exiftool code/output.
 
* Please include your OS, Exiftool version, and type of file you're processing (MP4, JPG, etc).

whelton

#6
Quote from: StarGeek on November 15, 2022, 11:41:18 AMFixed your links, not that it's going to help

Thank you for that and for the tips.

Here is a link to a folder on Google Drive, with the two images. I hope the EXIF data is intact.

https://drive.google.com/drive/folders/1hmBhrFQiGrwAevBhyu08awtJTwTrvr21?usp=sharing


StarGeek

So is there still a problems somewhere?  Using the config file, I was able to copy the Sd-metadata from one of your PNGs into another.

Off topic question, is the Dream and sd-meTadata tags added by a Stable Diffusion front end?  If so, which one?  We've already come across a different SD metadata tag and a all in one config file is probably worth creating.
* Did you read FAQ #3 and use the command listed there?
* Please use the Code button for exiftool code/output.
 
* Please include your OS, Exiftool version, and type of file you're processing (MP4, JPG, etc).

whelton

Quote from: StarGeek on November 17, 2022, 11:06:49 AMSo is there still a problems somewhere?  Using the config file, I was able to copy the Sd-metadata from one of your PNGs into another.

Thanks again for your continued replies.

Looking back at it, I see the way I asked the question wasn't clear enough.

In short, I need the value of the sd-metadata field munged before it is put into my output image.
I can do the munging. What I need help with is how to stuff the munged value back into the new image.

The montage image (as seen in Google Drive) has JSON in its exif data (excerpt below). Note the "images": [] array. It contains 9 entries (3 shown here). One entry for each of the 9 tiles in the montage image.

{
  "model": "stable diffusion",
  "model_id": "stable-diffusion-1.5",
  "model_hash": "e1441589a6f3c5a53f5f54d0975a18a7feb7cdf0b0dee276dfc3331ae376a053",
  "app_id": "lstein/stable-diffusion",
  "app_version": "v1.15",
  "images": [
    {
      "steps": 50,
      "height": 256,
      "threshold": 0,
      "init_mask": null,
      "width": 256,
      "prompt": [
        {
          "prompt": "painting of a flower,",
          "weight": 1
        }
      ],
      "perlin": 0,
      "init_img": null,
      "seed": 1275322858,
      "cfg_scale": 10,
      "postprocessing": null,
      "sampler": "k_lms",
      "variations": [],
      "type": "txt2img"
    },
    {
      "steps": 50,
      "height": 256,
      "threshold": 0,
      "init_mask": null,
      "width": 256,
      "prompt": [
        {
          "prompt": "painting of a flower,",
          "weight": 1
        }
      ],
      "perlin": 0,
      "init_img": null,
      "seed": 363762986,
      "cfg_scale": 10,
      "postprocessing": null,
      "sampler": "k_lms",
      "variations": [],
      "type": "txt2img"
    },
    {
      "steps": 50,
      "height": 256,
      "threshold": 0,
      "init_mask": null,
      "width": 256,
      "prompt": [
        {
          "prompt": "painting of a flower,",
          "weight": 1
        }
      ],
      "perlin": 0,
      "init_img": null,
      "seed": 3633463394,
      "cfg_scale": 10,
      "postprocessing": null,
      "sampler": "k_lms",
      "variations": [],
      "type": "txt2img"
    },
    ... truncated ...
  ]
}

For each tile image I'm extracting from the montage image, I need to get just one of these entries, put it in the right format along with other common fields, to create a well formatted string of JSON (that is the munging part) that will be the value for the sd-metadata field for that single output image.

So I end up with 9 different strings of JSON, one for each image, and I need to add each of these strings as the value for the sd-metadata field (a new field that does not exist in the fresh output image) for each respective output image.

After munging one entry, it will be formatted like below. This will be the value (sans formatting) that will be added to the output file as the value of the sd-metadata field.

This (below) is just showing the formatting. This is from a single non-montage image generated by the tool. It's a reference for the format I'm munging the JSON into.

{
  "model": "stable diffusion",
  "model_id": "stable-diffusion-1.5",
  "model_hash": "e1441589a6f3c5a53f5f54d0975a18a7feb7cdf0b0dee276dfc3331ae376a053",
  "app_id": "lstein/stable-diffusion",
  "app_version": "v1.15",
  "image": {
    "steps": 50,
    "height": 256,
    "threshold": 0,
    "init_mask": null,
    "width": 256,
    "prompt": [
      {
        "prompt": "painting of a flower,",
        "weight": 1
      }
    ],
    "perlin": 0,
    "init_img": null,
    "seed": 383747576,
    "cfg_scale": 10,
    "postprocessing": null,
    "sampler": "k_lms",
    "variations": [],
    "type": "txt2img"
  }
}

The gap I am trying to close is how to take that string (again, sans formatting; I can handle getting rid of the formatting) and store it into the new image file, under the key sd-metadata. So, the question is, how to do this? I've tried a few things and no workie so far. I've escaped the quotes in the JSON, trying a couple/few different ways for that... I'm on macOS but also have Linux available. Do I need to escape the angle brackets or square brackets too?

fwiw my target format shown above can be seen in another form in the (excerpted) output of Imagemagick's identify -verbose command:

  Properties:
    date:create: 2022-11-14T07:16:15+00:00
    date:modify: 2022-11-11T22:46:26+00:00
    date:timestamp: 2022-11-18T08:57:47+00:00
    Dream: "painting of a flower," -s 50 -S 383747576 -W 256 -H 256 -C 10.0 -A k_lms
    png:IHDR.bit-depth-orig: 8
    png:IHDR.bit_depth: 8
    png:IHDR.color-type-orig: 2
    png:IHDR.color_type: 2 (Truecolor)
    png:IHDR.interlace_method: 0 (Not interlaced)
    png:IHDR.width,height: 256, 256
    png:sRGB: intent=0 (Perceptual Intent)
    png:text: 2 tEXt/zTXt/iTXt chunks were found
    sd-metadata: {"model": "stable diffusion", "model_id": "stable-diffusion-1.5", "model_hash": "e1441589a6f3c5a53f5f54d0975a18a7feb7cdf0b0dee276dfc3331ae376a053", "app_id": "lstein/stable-diffusion", "app_version": "v1.15", "image": {"steps": 50, "height": 256, "threshold": 0.0, "init_mask": null, "width": 256, "prompt": [{"prompt": "painting of a flower,", "weight": 1.0}], "perlin": 0.0, "init_img": null, "seed": 383747576, "cfg_scale": 10.0, "postprocessing": null, "sampler": "k_lms", "variations": [], "type": "txt2img"}}

QuoteOff topic question, is the Dream and sd-meTadata tags added by a Stable Diffusion front end?  If so, which one?  We've already come across a different SD metadata tag and a all in one config file is probably worth creating.

This is generated by the InvokeAI Toolkit.

Speaking of Dream I want to add that too, but figured I would just ask about the "harder" one with a hyphen in the name, sd-metadata, first and once I figure that out, Dream should be easy.

StarGeek

Quote from: whelton on November 18, 2022, 04:16:36 AMThe gap I am trying to close is how to take that string (again, sans formatting; I can handle getting rid of the formatting) and store it into the new image file, under the key sd-metadata. So, the question is, how to do this? I've tried a few things and no workie so far. I've escaped the quotes in the JSON, trying a couple/few different ways for that... I'm on macOS but also have Linux available. Do I need to escape the angle brackets or square brackets too?

Sorry for being so dense but I'm still not sure I understand. Especially about escaping brackets. It seems to me to be a simple 3 step process. Assuming a bash script, extract the json with exiftool into a variable, edit the json, and re-embed it with exiftool.

The obvious solution to me would be to list just the sd-metadata.  For example, in a bash script, you could use the backtick to save the data into a variable, using either the -s (-short) option or the -b (-binary) option
myJson = `exiftool -sd-metadata -s3 file.png`
You could pipe the json directly into whatever you're using to change the data and then into the new file using the -TAG<=DATFILE option and a hyphen for stdin.  For example, using JQ (had to look up a jq pipe example)
exiftool -sd-metadata -s3 file.png | jq -options '.' | exiftool -sd-metadata<=- newfile.png

There's always doing everything in Perl using the exiftool Perl library, which would be the most powerful and flexible, but harder if you don't know enough Perl.

You could also use the -W (-tagOut) option.  You would use that to extract all the json into basically a json sidecar file for the big image for all your images.  Then you could run your programs on that and embed the new data into each smaller image with exiftool.
exiftool -W %d%f.json -sd-metadata /path/to/files/
* Did you read FAQ #3 and use the command listed there?
* Please use the Code button for exiftool code/output.
 
* Please include your OS, Exiftool version, and type of file you're processing (MP4, JPG, etc).

whelton

Quote from: StarGeek on November 18, 2022, 11:01:00 AM... edit the json, and re-embed it with exiftool.

Thanks. It's the re-embedding that's giving me trouble.

Trying something based on your example. Created a new image newfile.png and ran these:

bash:

$ echo "hello" | exiftool -sd-metadata<=- newfile.png
-bash: =-: No such file or directory

tcsh:

% echo "hello" | exiftool -sd-metadata<=- newfile.png
Ambiguous input redirect.

zsh:

% echo "hello" | exiftool -sd-metadata<=- newfile.png
zsh: - not found

Or, closer to your example but taking munging and jq out the picture, and back to bash:

$ exiftool -sd-metadata -s3 flower-001241.383747576.png | exiftool -sd-metadata<=- newfile.png
-bash: =-: No such file or directory

Feels like figuring out this command will be the shortest path, but I'm not enough of a shell redirection expert to see what is going wrong.

Phil Harvey

You must quote the ExifTool arguments containing "<":

echo "hello" | exiftool "-sd-metadata<=-" newfile.png

("<=" is an ExifTool operator, not a shell redirect)

- Phil
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

whelton

Our messages crossed.. yours was posting as I was previewing. Here's mine:

I couldn't for the life of me find anything on the internet describing what '<=-' does in a unix command line piping / redirection context. Less than, hyphen, I get those, but equal sign?

I thought maybe the forum was rewriting some characters. But then I realized hey what if <= is an exiftool thing, not a bash thing...

So... I tried putting all but the hyphen in quotes after the first hyphen:

exiftool -sd-metadata -s3 flower-001241.383747576.png | exiftool -"sd-metadata<=" - newfile.png

and, nope.

But.. this (below) works!!! Thank you!!

exiftool -sd-metadata -s3 flower-001241.383747576.png | exiftool -"sd-metadata<=-" newfile.png


whelton

Interesting our quote placements are slightly different but both work I guess. I'll prefer yours, of course, for obvious reasons :-).

Phil Harvey

The quotes just have to go around the offending character, so even this works:

echo "hello" | exiftool -sd-metadata"<"=- newfile.png

(that's the way most shells handle quotes)

- Phil
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).