воскресенье, 21 декабря 2014 г.

C#: получить снимки с цифровой камеры

Понадобилось тут поработать с цифровой камерой: скопировать с неё снимки и запихнуть их в базу данных. Если вторая часть задачи решилась хоть и не изящно, но достаточно просто, то с реализацией первой возникли определенные проблемы.

Дело в том, что потребовалось получать и обрабатывать несколько снимков за раз. Я думал обойтись штатным OpenFileDialog. Ведь цифровая камера, будучи подключенной по USB к компьютеру, притворяется обычной папкой. Но, оказалось, "папка" эта не совсем обычная. В частности, например, при попытке доступа к изображению система создает его копию во временных файлах, и позволяет работать уже с этой копией. Если же выбрать несколько изображений, то OpenFileDialog вернёт вместо путей что-то маловразумительное типа "004\Root\IMG_1111" "004\Root\IMG_1112" "004\Root\IMG_1113".

Поэтому возникла мысль использовать обходной путь. В Windows есть нечто под названием Windows Image Acquisition. Статус этой технологии мне остался до конца неясным - то ли она уже устарела, то ли еще нет - но и в XP, и в 7 эта штука присутствует, хоть и двух разных версий.

Сначала попытался использовать WIA первой версии. Думал, обратная совместимость вывезет. Работаю я на WinXP, там WIA 1.0, что называется, "из коробки". Итак, добавляю в проект ссылку на Microsoft Windows Image Acquisition 1.01 Type Library (Add Reference, вкладка COM), получаю пространство имен WIALib и в нем кучку классов, с которыми уже можно работать:
using WIALib;
...

private void button1_Click(object sender, EventArgs e)
{
  object selectUsingUI = System.Reflection.Missing.Value;
  try
  {
    // создаем класс для работы с WIA
    WiaClass wiamanager = new WiaClass();

    // выводим диалог выбора цифровой камеры
    ItemClass wiaRoot = (ItemClass)wiamanager.Create(ref selectUsingUI);
    if (wiaRoot == null)
      return;

    // выводим диалог выбора изображений, хранящихся в камере
    CollectionClass wiaPics = (CollectionClass)wiaRoot.GetItemsFromUI(WiaFlag.UseCommonUI, WiaIntent.ImageTypeColor);

    // каждое выбранное изображение копируем в каталог с нашей программой
    if (wiaPics != null)
      foreach (object wiaObj in wiaPics)
      {
        ItemClass wiaItem = (ItemClass)Marshal.CreateWrapperOfType(wiaObj, typeof(ItemClass));
        string tmpFileName = Path.GetDirectoryName(Application.ExecutablePath) + "\\" + wiaItem.Name;
        wiaItem.Transfer(tmpFileName, false);
      }
  }
  catch (Exception ex)
  {
    MessageBox.Show(ex.Message);
  }
}
Всё работает чудесно. Но обнаружились два момента. Первое: без дополнительной обработки изображения получаются чудовищного размера (без всякого сжатия), и второе, самое главное: этот способ не сработал в Windows 7. А именно, при запуске своей программы на Windows 7 64bit я получил такое сообщение об ошибке: Сбой при получении производства объектов класса COM для компонента с CLSID {4EC4272E-2E6F-4EEB-91D0-EBC4D58E8DEE} в результате следующей ошибки: 80040154. В общем, похоже, в современных виндах WIA 1.0 нет.

Пришлось обратиться к WIA 2.0. Под Windows XP потребовалось её установить дополнительно. Я брал дистрибутив отсюда: http://vbnet.mvps.org/files/updates/wiaautsdk.zip. Дальше добавляю в проект ссылку на Microsoft Windows Image Acquisition Library v2.0 (всё на той же вкладке COM) и получаю пространство имен WIA. Использовал это примерно так:
using WIA;
...

private void button1_Click(object sender, EventArgs e)
{

  // создаем класс для работы с камерой
  WIA.CommonDialogClass dlg = null;
  try
  {
    dlg = new WIA.CommonDialogClass();
  }
  catch(Exception ex)
  {
    MessageBox.Show("Невозможно подключиться к интерфейсу работы с камерой:\n" + ex.Message, "Ошибка",
        MessageBoxButtons.OK, MessageBoxIcon.Error);
  }
  if (dlg == null)
    return;

  // выбираем камеру
  WIA.DeviceClass d = null;
  try
  {
    d = (WIA.DeviceClass)dlg.ShowSelectDevice(WIA.WiaDeviceType.CameraDeviceType, true, false);
  }
  catch (Exception ex)
  {
    MessageBox.Show("Невозможно подключиться к камере:\n" + ex.Message, "Ошибка",
        MessageBoxButtons.OK, MessageBoxIcon.Error);
  }
  if (d == null)
    return;

  // выбираем изображения
  WIA.Items items = dlg.ShowSelectItems(d, WIA.WiaImageIntent.ColorIntent,
      WIA.WiaImageBias.MinimizeSize, false, true, false);

  if (items != null)
    foreach (WIA.Item item in items)
    {
      WIA.ImageFile img = (WIA.ImageFile)item.Transfer(WIA.FormatID.wiaFormatJPEG);

      // выясняем имя и расширение выбранного изображения
      string
        tmpFileName = "",
        tmpFileExt = "";
      foreach (WIA.Property prop in item.Properties)
      {
        if (prop.Name == "Item Name")
          tmpFileName = prop.get_Value().ToString();
        if (prop.Name == "Filename extension")
          tmpFileExt = prop.get_Value().ToString();
      }
      if (tmpFileExt == "")
        tmpFileExt = img.FileExtension;
      if (Path.HasExtension(tmpFileName))
        tmpFileExt = "";

      // копируем выбранное изображение в каталог с нашей программой
      string
        tmpFullName = Path.GetDirectoryName(Application.ExecutablePath) + "\\"
            + tmpFileName + ((tmpFileExt != "") ? "." : "") + tmpFileExt;

      img.SaveFile(tmpFullName);
    }
}
Код получился немного длинее, но это из-за проверок. Основной плюс - помимо того, что эта конструкция сработала и в Windows 7 - возможность сохранять изображения в формате JPEG.

Кстати, во втором примере в свойствах изображения есть много интересного. Я себе на память сделал скриншот:

Комментариев нет:

Отправить комментарий