Hatena::Grouprubyist

Going My Ruby Way このページをアンテナに追加 RSSフィード

Ruby ロゴ (C) Ruby Association LLC

2012年04月08日(日)

内容が同じ画像ファイルなら同じ名前を返すスクリプト

| 13:41 | 内容が同じ画像ファイルなら同じ名前を返すスクリプト - Going My Ruby Way を含むブックマーク はてなブックマーク - 内容が同じ画像ファイルなら同じ名前を返すスクリプト - Going My Ruby Way 内容が同じ画像ファイルなら同じ名前を返すスクリプト - Going My Ruby Way のブックマークコメント

前に扱った image_size を使って標題のスクリプトを作ってみます。

出力する名前は、以下の形式です。

 幅 'x' 高さ '-' SHA256ハッシュ '.' 拡張子

幅と高さは 4桁(空いた桁は '_' 埋め)、拡張子は JPEGGIFPNG = {.jpg, .gif, .png) です。

img_uniqname.rb

#!/usr/bin/env ruby
# -*- coding : UTF-8 -*-

require 'open-uri'
require 'openssl'
require 'image_size'

def img_uniqname(file)
  uniqname = lambda do |f|
    img  = f.read
    hash = OpenSSL::Digest::SHA256.hexdigest img
    is   = ImageSize.new img
    type = is.get_type
    ext  = { "JPEG" => 'jpg',
             "PNG"  => 'png',
             "GIF"  => 'gif',
            }[type] || 'unknown'

    ("%4dx%4d-#{hash}.#{ext}" % is.size).gsub(/\s/, '_')
  end

  file.respond_to?(:read) ? uniqname[file] : open("#{file}", 'rb', &uniqname)
end

filename = ARGV[0] or raise "usage: #{$0} image-filename"

puts img_uniqname(filename)

# vi:set ts=2 sw=2 et:

試す

この日記の右上に貼ってある Rubyロゴ画像(幅70、高さ81、GIF)で試してみます。

$ ruby img_uniqname.rb 20110722133229.gif
__70x__81-140e89c7a34b9585d6ad88dcbd04e1d2f7f3e8bed80ae844439742cc5f30671e.gif

画像を別の名前でコピーして、試してみます。

名前が違っても同じ内容の画像なので同じ名前を返すはずです。

$ cp 20110722133229.gif hoge
$ img_uniqname.rb hoge
__70x__81-140e89c7a34b9585d6ad88dcbd04e1d2f7f3e8bed80ae844439742cc5f30671e.gif

同じ名前を返しました。

スクリプトの引数は(画像がダウンロードできるなら)URIを指定しても構いません。

$ ruby img_uniqname.rb http://cdn-ak.f.st-hatena.com/images/fotolife/l/lnznt/20110722/20110722133229.gif
__70x__81-140e89c7a34b9585d6ad88dcbd04e1d2f7f3e8bed80ae844439742cc5f30671e.gif

やはり、同じ名前を返します。

その他

----

このスクリプトでは SHA256 を使い実装しましたが、MD5 など他のハッシュ関数でも構いません。

----

gpg コマンドを使った SHA256 の求め方。

$ gpg --print-md SHA256 20110722133229.gif 
20110722133229.gif: 140E89C7 A34B9585 D6AD88DC BD04E1D2 F7F3E8BE D80AE844
                    439742CC 5F30671E

----

内容が同じなら同じ名前を返しますが、同じ名前だから同じ内容だとは限りません。

SHA256 は 16進数64桁 のダイジェストを返します。これは256進数32桁です。

1バイト(が8ビットとして)を256進数とみなすと、33バイト以上のファイルが表現できるパターンはダイジェストのそれを超えます。(鳩の巣原理)

画像形式として妥当で、かつ、形式と幅と高さが同じという制限があるので確率は低くなりますが、同じ名前だから絶対に同じ内容とはいえません。(...と思います)