none
Обработка изображения по частям (C#, WPF) RRS feed

  • Вопрос

  • Здравствуйте.

    Есть такой код:

    /****************************
    "status"    -   Label
    "progress"  -   ProgressBar
    ****************************/
    
    //  16.156 * 11.041 pixels - 96 dpi - 24 Bit - 26.3 Mb
    string imageFile = "D:\\Pictures\\Картинки\\Карта Мира.jpg";
    string outDir = "D:\\work\\test\\";
    
    async void Button_Click(object sender, RoutedEventArgs e)
    {
    	// Вызываем метод обработки изображения
    	if (await Test_Method_1())
    		// Закончили...
    		MessageBox.Show("Complete");
    }
    
    /// <summary>
    /// Обработка изображения
    /// </summary>
    /// <returns>True - Если обработка завершена; False - в обратном случае</returns>
    async Task<bool> Test_Method_1()
    {
    	// Запускаем новый поток
    	if (await Task.Run<bool>(() =>
    	{
    		// Получаю Image рабочего изображения
    		Image image = Image.FromFile(imageFile);
    		/*
    		Max size part image: 1.000 * 1.000 pixels
    		(в общем какой-то оптимальный размер для части изображения)
    		(сейчас стоит 1.000 * 1.000 пикселей)
    		*/
    		double maxSide = 1000.0;
    		// Переменная "количество частей изображения"
    		int imagePartsOnSide;
    		// Получение количества частей изображения на стороне
    		// По ширине - если она больше высоты
    		if (image.Width > image.Height)
    			imagePartsOnSide = (int)Math.Ceiling(image.Width / maxSide);
    		// По высоте - если высота больше ширины
    		else
    			imagePartsOnSide = (int)Math.Ceiling(image.Height / maxSide);
    		// Ширина части изображения
    		int partWidth = image.Width / imagePartsOnSide;
    		// Высота части изображения
    		int partHeight = image.Height / imagePartsOnSide;
    		// Номер части изображения
    		int partIndex = 1;
    		#region Всякие данные и показ процесса пользователю
    		Dispatcher.BeginInvoke(new Action(() =>
    		{
    			// Задание начальной позиции прогресса
    			progress.Value = 0;
    			// Задание максимального значение прогресса
    			progress.Maximum = imagePartsOnSide * imagePartsOnSide;
    			// Задание начального статуса
    			status.Content = string.Format("Part {0} of {1}", progress.Value, progress.Maximum);
    		}));
    		#endregion
    		// Цикл по оси X - Width
    		for (int x = 0; x < imagePartsOnSide; x++)
    		{
    			// Цикл по оси Y - Height
    			for (int y = 0; y < imagePartsOnSide; y++)
    			{
    				// Создаю Bitmap размера с нужную часть
    				using (Bitmap tempBitmap = new Bitmap(partWidth, partHeight))
    				{
    					// Создаю Graphics для рисования
    					using (Graphics graphics = Graphics.FromImage(tempBitmap))
    					{
    						// Отрисовываю в Bitmap необходимую часть изображения
    						graphics.DrawImage(image, -(x * partWidth), -(y * partHeight));
    						// Сохраняю эту часть изображения
    						tempBitmap.Save(string.Format("{0}{1}{2}{3}", outDir, "part_", partIndex.ToString(), ".png"), ImageFormat.Png);
    					}
    				}
    				// Увеличиваем номер части изображения
    				partIndex++;
    				#region Всякие данные и показ процесса пользователю
    				Dispatcher.BeginInvoke(new Action(() =>
    				{
    					// Задание позиции прогресса
    					progress.Value = partIndex - 1;
    					// Задание статуса
    					status.Content = string.Format("Part {0} of {1}", progress.Value, progress.Maximum);
    				}));
    				#endregion
    			}
    		}
    		return true;
    	}))
    		return true;
    	else
    		return false;
    }

    Проблема такая: для разбивки изображения на части, приходится загружать его целиком, после чего уже делить на эти самые части и с ними работать!

    Изображение большое! И даже в Photoshop'е и в Windows при открытии занимает около 1 Гб памяти!!! Я не вижу смысла выделять под этот метод такой объем памяти (ну и на компе может не быть такого свободного объема ОЗУ) если все равно обрабатывается лишь его малая часть, и встает вопрос:

    Мне не нужно все изображение! В теле цикла (где происходит основная работа) я обрабатываю лишь часть картинки!

    Так как мне загружать для обработки не все изображение, а лишь его определенную часть!?


    • Изменено Hovanskiy 13 ноября 2015 г. 9:31 Не правильное отображение кода
    13 ноября 2015 г. 9:29

Ответы

  • На сколько я помню WIC поддерживает такой режим. Создав IWICBitmapSource (без фактического декодирования) можно затем вызвать метод CopyPixels что декодирует нужную часть изображения и выдаст декодированные пиксели для указанной части изображения.

    Использование WIC из управляемого кода возможно через COM interop. Конечно это несколько сложнее чем использовать управляемые API из устаревшего GDI.


    This posting is provided "AS IS" with no warranties, and confers no rights.

    14 ноября 2015 г. 18:07
    Модератор

Все ответы

  • Добрый день.

    Теоретически, вы можете сделать свой парсер jpg, который будет парсить заголовок файла, потом часть файла пропускать, загружать только те байты которые относятся к нужной части, ну и дальше конвертнув их в jpg (т.е. добавив заголовок) использовать как картинку.Если у вас картинка не меняется, может вам имеет смысл нужную вам часть/части сохранить отдельно? Или разбить картинку на более мелкие и в программу грузить их по мере надобности?

    13 ноября 2015 г. 10:09
    Отвечающий
  • Тут ситуация такая:

    Загружается некая, указанная пользователем картинка, если она больше определенных размеров (здесь, например, 1000*1000 пикселей), она разбивается на несколько маленьких картинок, затем каждая ее часть обрабатывается (изменение размера, цвета, эффекты какие-нибудь и т.д.) - затем все части картинки объединяются в одну и на выходе получаем исходное изображение, но с необходимыми изменениями.

    Просто проблема как раз в том, что например вот эта картинка в памяти занимает около 1 Гб. Я, со своим объемом ОЗУ, могу себе позволить обрабатывать даже большие по размеру картинки, особенно если кроме этой программы ничего не запущенно - но зачастую это не так! И потому хотелось бы максимально сократить требуемый объем памяти.

    14 ноября 2015 г. 8:33
  • Думаю, что вам нужно в корне менять концепцию. Такие задачи не решаются средствами готовых API. Вам нужно грузить картинку побайтово и выполнять преобразования на уровне байт. Тут затраты только во времени. Ну и само собой нужно знать все алгоритмы работы с изображением на уровне байт.

    VB.Net - WPF, UWP


    14 ноября 2015 г. 9:13
    Отвечающий
  • На сколько я помню WIC поддерживает такой режим. Создав IWICBitmapSource (без фактического декодирования) можно затем вызвать метод CopyPixels что декодирует нужную часть изображения и выдаст декодированные пиксели для указанной части изображения.

    Использование WIC из управляемого кода возможно через COM interop. Конечно это несколько сложнее чем использовать управляемые API из устаревшего GDI.


    This posting is provided "AS IS" with no warranties, and confers no rights.

    14 ноября 2015 г. 18:07
    Модератор