Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Delphi программирование > Мультимедиа в Delphi
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 30.07.2013, 19:51   #1
AnTe
Форумчанин
 
Регистрация: 25.09.2008
Сообщений: 209
По умолчанию "Подрисовать" в TIFF-файле. GDI+

Здравствуйте! Возникла следующая задача.

Имеется куча TIFF-файлов, нужно в каждый подрисовать "печать" - небольшое изображение.

TIFF файлы - отсканированные чертежи и текстовые документы в ч/б, многие многостраничные. Средний размер страницы в пикселах 4000*2516, dpi колеблется от 300 до 600 (причём в одном файле могут находиться страницы, отсканированные в разное время, с разным dpi)

Задача фактически разовая, т.е. будет периодически открываться одним пользователем, чтобы "проштамповать" пару тысяч файлов. Сторонний софт был исключён, т.к. попадать нужно примерно в одно место в основной надписи документа, т.е. для того, чтобы определить, куда проставлять штамп документ нужно будет анализировать.


После недолгих мытарств с различными библиотеками (то некоторые файлы не открывались, то ещё что), остановился на библиотеке GDI+ - всё-таки как никак microsoft....

Однако, опыта работы с графическими форматами нет. Как-то писал программку, открывал bmp, jpeg, png, исследовал canvas.pixels - распознавал нужные места, дорисовывал, что нужно методами canvas и в pixels напрямую. В GDI+, насколько понял, принцип немного другой.


С налёту не могу разобраться в некоторых моментах.
1. Вот как я просто пересохраняю одностраничный файл.
Код:
var
    Image: TGPImage;
    encoderClsid: TGUID;
    i: integer;
begin
 Image := TGPImage.Create(ExtractFilePath(Application.ExeName) + 'Document_1Page.tif');

  GetEncoderClsid('image/tiff', encoderClsid); // не знаю, что это
  Image.Save(ExtractFilePath(Application.ExeName) + 'Result_1Page.tif', encoderClsid, nil);
end;
Страница пересохраняется, размеры в пикселах файлов одинаковы, с виду всё один-в-один, однако, размер результирующего файла в несколько раз превышает исходный размер.

2. Теперь точно так же пытаюсь сохранить все страницы многостраничного файла:
Код:
var
  Image: TGPImage;
  encoderClsid: TGUID;
  i: integer;
begin
  Image := TGPImage.Create(ExtractFilePath(Application.ExeName) + 'Document_1Page.tif');

  GetEncoderClsid('image/tiff', encoderClsid);
  Image.Save(ExtractFilePath(Application.ExeName) + 'Result_1Page.tif', encoderClsid, nil);

  for i := 1 to Image.GetFrameCount(FrameDimensionPage) - 1 do
  begin
    Image.SelectActiveFrame(FrameDimensionPage, i);
    Image.SaveAdd(nil) // понимаю, что передавать nil наверное глупо, но идей больше не возникло
  end;
end;
В файле последняя страничка....

3. Теперь пытаюсь редактировать. На форме разместил imgOut: TImage
Код:
var
  Image: TGPImage;
  encoderClsid: TGUID;
  i: integer;

  tmp: TBitmap;
  Graphics: TGPGraphics;
begin
  Image := TGPImage.Create(ExtractFilePath(Application.ExeName) + 'Document_4Pages.tif');

  tmp := TBitmap.Create;
  tmp.Width := Image.GetWidth;
  tmp.Height := Image.GetHeight;
  Graphics := TGPGraphics.Create(tmp.Canvas.Handle);
  Graphics.DrawImage(Image, 0, 0, Image.GetWidth, Image.GetHeight);
  imgOut.Picture.Assign(tmp);
end;
Текущая страничка замечательно отображается.

Однако, как работать с изображением? Как в него дорисовать? В принципе, Graphics.DrawImage, вызванный повторно, рисует изображение, однако, на исходный Image это не влияет, и при сохранении файла ничего не изменяется.

Нельзя ли решить мою задачу, изучив какой-нибудь минимум информации, или без дебрей не обойтись?

Посоветуйте, пожалуйста, что можно почитать по GDI+, кроме msdn-а, хотелось бы что-нибудь покороче, сроки очень сжатые.

UPD во вложении прикладываю два исходных используемых TIFF
Вложения
Тип файла: rar TIFF_Source.rar (268.5 Кб, 18 просмотров)

Последний раз редактировалось AnTe; 30.07.2013 в 19:55.
AnTe вне форума Ответить с цитированием
Старый 30.07.2013, 23:21   #2
raxp
Старожил
 
Регистрация: 29.09.2009
Сообщений: 9,713
По умолчанию

...для пакетной обработки пользуюсь консолью ImageMagic с ключом -composite ...впрочем, BD-Sizer и FastStone Image Viewer в визуальном режиме тоже умеют.
Разработки и научно-технические публикации :: Видеоблог :: Твиттер
Radar systems engineer & Software developer of industrial automation
raxp вне форума Ответить с цитированием
Старый 31.07.2013, 18:16   #3
AnTe
Форумчанин
 
Регистрация: 25.09.2008
Сообщений: 209
По умолчанию

Спасибо! Но всё же, пожалуй, от использования сторонних программ приходится отказываться окончательно: в перспективе простановка литеры, т.о. необходимо будет находить (распознавать) точные координаты "квадратика", в который ставится символ.

А страницы отсканированы далеко не идеально: многие даже с поворотом на небольшой градус. Всё же нужно свою программу писать.

Собственно, с первыми двумя пунктами в GDI+ с горем пополам разобрался. Вот как теперь выглядит кнопочка "сохранить":

Код:
procedure TfmMain.btnSaveClick(Sender: TObject);
var encoderClsid: TGUID;
  i: integer;
  EncoderMultiFrame, encoderFrameDimensionPage, EncoderFlush: TEncoderParameters;
  ValMultiFrame, ValFrameDimensionPage, ValFlush: EncoderValue;
begin

  ValMultiFrame := EncoderValueMultiFrame;
//  ValCompression := EncoderValueCompressionCCITT4;
  ValFrameDimensionPage := EncoderValueFrameDimensionPage;
  ValFlush := EncoderValueFlush;

  EncoderMultiFrame.Count := 1;
  EncoderMultiFrame.Parameter[0].NumberOfValues := 1;
  EncoderMultiFrame.Parameter[0].Guid := EncoderSaveFlag;
  EncoderMultiFrame.Parameter[0].Type_ := EncoderParameterValueTypeLong;
  EncoderMultiFrame.Parameter[0].Value := @ValMultiFrame;

  encoderFrameDimensionPage.Count := 1;
  encoderFrameDimensionPage.Parameter[0].NumberOfValues := 1;
  encoderFrameDimensionPage.Parameter[0].Guid := EncoderSaveFlag;
  encoderFrameDimensionPage.Parameter[0].Type_ := EncoderParameterValueTypeLong;
  encoderFrameDimensionPage.Parameter[0].Value := @ValFrameDimensionPage;

  EncoderFlush.Count := 1;
  EncoderFlush.Parameter[0].NumberOfValues := 1;
  EncoderFlush.Parameter[0].Guid := EncoderSaveFlag;
  EncoderFlush.Parameter[0].Type_ := EncoderParameterValueTypeLong;
  EncoderFlush.Parameter[0].Value := @ValFlush;

  GetEncoderClsid('image/tiff', encoderClsid);
  Image.Save(edtResultFN.Text, encoderClsid, @EncoderMultiFrame);

  if cbSaveAllPages.Checked then
    for i := 1 to Image.GetFrameCount(FrameDimensionPage) - 1 do
    begin
      Image.SelectActiveFrame(FrameDimensionPage, i);
      Image.SaveAdd(@encoderFrameDimensionPage)
    end;
  Image.SaveAdd(@EncoderFlush);
end;
Размер получается другой (раза в три больше), потому что исходный файл упакован в "CCITT Group 4 Fax Encoding", а выходной по умолчанию пакуется с LZW.

Не разобрался, как изменить тип компрессии. Но главное - файл сохраняется.

Остался третий пункт: - "дорисовать" что-нибудь на объекте GDI++ Image, точнее, его Frame (странице). Предположительных вариантов три:

1. Либо существуют методы Image, которые позволяют рисовать прямиком в этом объекте (на его Frame - странице), непосредственно перед сохранением.
2. Либо нужно использовать какие-либо другие объекты, возможно, наследников Image в GDI+, которые сразу загрузят картинку "особым" образом,и свойства (а-ля canvas) будут доступны.
3. Либо нужно каждую страничку выгружать в штатный TBitmap и каким-то образом вставлять её обратно в Image

Выгрузить страничку файла в TBitmap проблем не составило, и даже отобразить её на форме (в последней строчке):
Код:
var
 Image: TGPImage
 Graphics: TGPGraphics;
 tmp: TBitmap;
begin
  {загрузка файла из четырёх страниц}
  Image := TGPImage.Create(ExtractFilePath(Application.ExeName) + 'Document_4Pages.tif');

  {например, вторую страницу}
  Image.SelectActiveFrame(FrameDimensionPage, 2); 

  tmp := TBitmap.Create;
  tmp.Height := Image.GetHeight;
  tmp.Width := Image.GetWidth;

  Graphics := TGPGraphics.Create(tmp.Canvas.Handle);

  imgOut.Picture.Assign(tmp);
Соответственно, читать битмап и рисовать на нём можно всё, что угодно.

Вопрос, если идти таким путём - как залить битмап обратно в Image?
AnTe вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Создать класс "Фигура", от него наследованием создать 3 класса ("треугольник", "четырехугольник", "окружность") funnyy Помощь студентам 3 17.10.2012 17:40
Организовать текстовый файл f, состоящий из N строк. Организовать замену символов в файле. "Старый" символ и "новый" символ запраш Richik123 Паскаль, Turbo Pascal, PascalABC.NET 1 14.06.2012 16:28
Организовать текстовый файл f, состоящий из N строк. Организовать замену символов в файле. "Старый" символ и "новый" символ запра Richik123 Паскаль, Turbo Pascal, PascalABC.NET 0 31.05.2012 17:32
при вводе на листе "магазин"- код товара появлялось "описание" товара из "склада" с "продажной ценой" aleksei78 Microsoft Office Excel 13 25.08.2009 12:04