python : แตกไฟล์ JPG จากไฟล์ภาพ Canon

มีโจทย์อยู่คือ
เวลาไปเที่ยวหรือมีงานที่ต้องถ่ายภาพเป็นจำนวนมาก เกินการ์ดหน่วยความจำที่มีอยู่
เวลาการ์ดเต็ม ก็ต้องถ่ายออกมาเก็บไว้ในโน๊ตบุ๊ก

ปัญหาคือเวลาจะดูภาพจากโน๊ตบุ๊ก ซึ่งสเปคเครื่องต่ำมาก โหลดไฟล์ภาพใหญ่ ๆ ไม่ไหว มันจะดูได้ช้ามาก ๆ ดูภาพ 10 ภาพ ใช้เวลาไป 15 นาที

ทางแก้คือคัดลอกไฟล์ภาพมาแปลงเป็นไฟล์เล็ก (อาจจะแปลงด้วย gimp หรือ imagemagick ก็ได้) แต่เนื่องจากสเปคเครื่องต่ำมาก แปลงไฟล์แต่ละครั้งกินเวลาเป็นชั่วโมง ไม่ทันต่อเหตุการณ์

ทางออกอีกทางคือไปแตกเอาไฟล์ JPG อันเล็ก ที่ซ่อนอยู่ภายใต้ไฟล์ตัวจริงซึ่งใหญ่มาก เอาออกมาแทน วิธีนี้จะทำงานได้รวดเร็วกว่ามาก

เคยเขียน C ไว้เป็นไฟล์เล็ก ๆ บนวินโดวส์ แต่เที่ยวนี้ผมลองเอามาคอมไพล์บนลินุกส์ ปรากฎว่าคอมไพล์ไม่ผ่าน และภาษา C ก็ลืมสิ้นแล้ว อย่ากระนั้นเลย พึ่งไพธอนดีกว่า

ผมใช้กล้อง Canon และเราจะแตกไฟล์ JPG อันเล็ก ซึ่งเป็นไฟล์ลำดับที่ 3 ที่ซ่อนอยู่ในไฟล์ใหญ่ เลยตั้งชื่อโปรแกรมว่า canon3.py

เวลาใช้งานก็สั่ง
$ ./canon3.py FILENAME.JPG
ก็จะแตกไฟล์ FILENAME.JPG ไปเป็น canon3/FILENAME.JPG

หรือถ้าสั่ง
$ ./canon3.py เฉย ๆ
ก็จะควานหาทุกไฟล์ในไดเรกทอรี่ที่เป็น JPG หรือ CRW และแตกไฟล์ย่อยออกมาใส่ในไดเรกทอรี่ย่อย canon3

โดยโปรแกรมจะคัดลอกเอาข้อมูล Exif ของกล้องติดไปด้วย (แต่ข้อมูลขนาดภาพใน Exif จะผิดจากความเป็นจริง ขี้เกียจแก้แล้วอ่ะ)

ทดลองแล้วความเร็วใสการแตกไฟล์ดีมาก (ไพธอนนี่ดีกว่าที่คิดเยอะเลย)

โค๊ดมีดังนี้

#!/usr/bin/env python
# EXTRACT THIRD jpg FILE IN Canon CAMERA
#
# jpg file format
# start with: FF D8 FF E1 NN NN ...  (contain exif data to byte NN NN)
# first JPG : FF D8 ... FF D9 (thumbnail)
# second JPG: FF D8 ... FF D9 (real JPG)
# third JPG : FF D8 ... FF D9 (hidden small JPG)
# SKIP TO 2/3 OF FILE THEN SEARCH FOR THIRD #FFD8 TO #FFD9
#

import sys, os, string

# VAR
tag_beg="\xff\xd8"
tag_end="\xff\xd9"
subdir="canon3"
file_skel=[".JPG",".jpg",".jpeg"]
dir_skel=["DCIM","CANON"]

# PROCEDURE
def process_file(filename):
  for fskel in file_skel:
    if fskel in filename:
      #OPEN FILE IN BINARY MODE
      try:
        f = open(filename, 'rb')
      except:
        print 'Could not open file to read !', filename
        sys.exit(3)
      if f is None:
        print 'Error opening file ', filename
        sys.exit(3)

      # COPY EXIF HEADING TO NEW FILE IN SUBDIR canon3
      basename=os.path.basename(filename)
      print "%s -> %s/%s" % (basename,subdir,basename)

      if not os.path.exists(subdir):
        os.mkdir(subdir)
      #
      f_new=open(os.path.join(subdir,basename),"wb")

      # SEEK FF D8 FF E1 NN NN
      f.seek(4)
      offset_low=f.read(1)
      offset_hi=f.read(1)
      no_of_byte=ord(offset_low)*256+ord(offset_hi)-4

      ## print "no_of_byte=%i" % no_of_byte

      # WRITE TO NEW FILE
      f_new.write("\xff\xd8\xff\xe1"+offset_low+offset_hi+f.read(no_of_byte))

      # SEEK FOR THE REST 3RD JPG FILE
      # INCREASE SPEED BY SKIP TO 2/3 OF FILE
      f.seek(os.path.getsize(filename) * 2/3)
      is_found=False
      for i in f:
        if tag_beg in i:
          is_found=True
          # WRITE THIRD JPG TO NEW FILE
          f_new.write(tag_beg+i.split(tag_beg)[1])

          for j in f:
            if tag_end in j:
              f_new.write(j.split(tag_end)[0]+tag_end)
              break
              f_new.close()
            else:
              f_new.write(j)
            #
          #
          break
        #
      #
      f.close()
      if not is_found:
        print "third JPG file not found."
      #
    #
  #

def process_dir(dirname):
  ## print "dirname=%s" % dirname
  for dskel in dir_skel:
    if dskel in dirname:
      print "enter %s" % dirname
      os.chdir(dirname)
      filelist=os.listdir(".")
      for i in filelist:
        if os.path.isdir(i):
          process_dir(i)
        else:
          process_file(i)
        #
      #
      print "exit %s" % dirname
      os.chdir("..")
    #
  #


# MAIN PROG
def main():
  if len(sys.argv) < 2:
    # PROCESS ALL FILE&DIR
    for filelist in os.listdir("."):
      if os.path.isdir(filelist):
        process_dir(filelist)
      else:
        process_file(filelist)
      #
    #
  else:
    filename = os.path.abspath(sys.argv[1])
    if os.path.isdir(filename):
      process_dir(filename)
    else:
      process_file(filename)
    #
  #
  print "finished"

if __name__=="__main__":
        main()

แถมอีกนิด
ถ้าจะให้หมุนภาพอัตโนมัติด้วย ต้องใช้แพคเกจ jhead
$ sudo apt-get install jhead
$ jhead -autorot *

ลดขนาดรูป

ลดขนาดรูปพร้อมกับหมุนภาพอัตโนมัติ ทำลึกลงไปทุกไดเรคทอรี่

$ sudo apt-get install imagemagick jhead
$ sudo touch /usr/local/bin/d.canon5.py
$ sudo chmod 0755 /usr/local/bin/d.canon5.py
$ sudo vi /usr/local/bin/d.canon5.py

#!/usr/bin/env python

import sys, os, string

# VAR
subdir="canon3"
file_skel=[".JPG",".jpg",".jpeg"]
dir_skel=["DCIM","CANON","100EOS5D"]

# PROCEDURE
def process_file(filename):
  for fskel in file_skel:
    if fskel in filename:
      if not os.path.exists(subdir):
        os.mkdir(subdir)
      #
      print "convert %s -> %s" % (filename, os.path.join(subdir,filename))
      os.system('convert -resize 40%'+" %s %s" % (filename,os.path.join(subdir,filename)))
      os.system("jhead -autorot %s" % (os.path.join(subdir,filename)))
    #
  #


def process_dir(dirname):
  ## print "dirname=%s" % dirname
  for dskel in dir_skel:
    if dskel in dirname:
      print "enter %s" % dirname
      os.chdir(dirname)
      filelist=os.listdir(".")
      for i in filelist:
        if os.path.isdir(i):
          process_dir(i)
        else:
          process_file(i)
        #
      #
      print "exit %s" % dirname
      os.chdir("..")
    #
  #

# MAIN PROG
def main():
  if len(sys.argv) < 2:
    # PROCESS ALL FILE&DIR
    for filelist in os.listdir("."):
      if os.path.isdir(filelist):
        process_dir(filelist)
      else:
        process_file(filelist)
      #
    #
  else:
    filename = os.path.abspath(sys.argv[1])
    if os.path.isdir(filename):
      process_dir(filename)
    else:
      process_file(filename)
    #
  #
  print "finished"


if __name__=="__main__":
        main()
Topic: 

debian: บันทึก Raw Image 2

ได้ลองใช้ ufraw แล้วรู้สึกว่าสีสวยกว่า dcraw

ติดตั้งด้วย
$ sudo aptitude install ufraw

ทำงานแบบบรรทัดคำสั่ง
$ cd /PATH/TO/IMAGE
$ mkdir jpg
$ for i in *cr2; do
ufraw-batch \
--wb=camera \
--exposure=auto \
--out-type=jpeg \
--compression=96 \
--out-path=./jpg \
$i
done

เป็นการทำงานกับไฟล์ภาพนามสกุล cr2 โดยให้ผลิตไฟล์ jpg ไปอยู่ในโฟลเดอร์ชื่อ jpg

เอามาจาก
Linux Photography : Workflow (3) - quick RAW converting batch

debian: บันทึกการทำงานกับไฟล์ภาพ cr2

เพิ่งได้เริ่มหัดถ่ายภาพแบบ Raw ของกล้อง Canon มีนามสกุลเป็น .CR2

ถ้าต้องการแบบบรรจง
$ sudo aptitude install gimp gimp-ufraw
เวลาเราใช้ Gimp เปิดไฟล์สกุล CR2 เขาจะขึ้นไดอะล๊อกมาให้ปรับค่าต่าง ๆ
ส่วนใหญ่ก็จะปรับแค่ exposure-ความสว่าง กับ temperature-อุณหภูมิสี
ของกล้อง canon 5d ลองดูแล้ว ค่าอุณหภูมิสีที่ออกมาดูดี อยู่ระหว่าง CameraWB กับ Daylight โดยอยู่ค่อนไปทาง CameraWB
ค่าสีเขียวก็เหมือนกัน ปรับให้อยู่ระหว่างนี้
ถ้าต้องการให้ออกมาในโทนเย็น ก็ปรับให้ค่อนไปทาง CameraWB
แต่ถ้าต้องการให้ออกมาในโทนอุ่น ก็ปรับให้ค่อนไปทาง Daylight
(AutoWB ออกมาไม่ดีเลย)
ถ้าต้องการแบบบรรทัดคำสั่ง
$ sudo aptitude install dcraw
$ for i in *.CR2; do dcraw -c -q 3 -B 2 4 -w $i | cjpeg -quality 100 > `basename $i .CR2`.jpg; done

เอามาจาก How to convert raw cr2 pictures with linux, and merge pictures by date and Exif data with jhead