Чтение ленивых пикселей F # - PullRequest
0 голосов
/ 06 января 2011

Я хочу сделать ленивую загрузку пикселей изображения в трехмерный массив целых чисел. Например, в простой форме это выглядит так:

   for i=0 to Width 
     for j=0 to Height
       let point=image.GetPixel(i,j)
       pixels.[0,i,j] <- point.R
       pixels.[1,i,j] <- point.G
       pixels.[2,i,j] <- point.B

Как это можно сделать ленивым образом?

Ответы [ 3 ]

5 голосов
/ 06 января 2011

Что будет медленным, так это вызов GetPixel. Если вы хотите вызывать его только по мере необходимости, вы можете использовать что-то вроде этого:

open System.Drawing

let lazyPixels (image:Bitmap) =
    let Width = image.Width
    let Height = image.Height
    let pixels : Lazy<byte>[,,] = Array3D.zeroCreate 3 Width Height
    for i = 0 to Width-1 do
        for j = 0 to Height-1 do
            let point = lazy image.GetPixel(i,j)
            pixels.[0,i,j] <- lazy point.Value.R
            pixels.[1,i,j] <- lazy point.Value.G
            pixels.[2,i,j] <- lazy point.Value.B
    pixels

GetPixel будет вызываться не более одного раза для каждого пикселя, а затем повторно использоваться для других компонентов.

Другим способом решения этой проблемы может быть массовая загрузка всего изображения. Это будет намного быстрее, чем звонить GetPixel снова и снова.

open System.Drawing
open System.Drawing.Imaging

let pixels (image:Bitmap) =
    let Width = image.Width
    let Height = image.Height
    let rect = new Rectangle(0,0,Width,Height)

    // Lock the image for access
    let data = image.LockBits(rect, ImageLockMode.ReadOnly, image.PixelFormat)

    // Copy the data
    let ptr = data.Scan0
    let stride = data.Stride
    let bytes = stride * data.Height
    let values : byte[] = Array.zeroCreate bytes
    System.Runtime.InteropServices.Marshal.Copy(ptr,values,0,bytes)

    // Unlock the image
    image.UnlockBits(data)

    let pixelSize = 4 // <-- calculate this from the PixelFormat

    // Create and return a 3D-array with the copied data
    Array3D.init 3 Width Height (fun i x y ->
        values.[stride * y + x * pixelSize + i])

(взято из образца C # на Bitmap.LockBits)

2 голосов
/ 08 января 2011

Как уже упоминалось в других комментариях, вы можете использовать ленивую загрузку пикселей в 3D-массиве, но это просто сделает операцию GetPixel ленивой, а не выделение памяти для 3D-массива, поскольку массив уже выделяется при вызове метода createArray3D.

Если вы хотите сделать выделение памяти так же, как GetPixel, то можете использовать последовательности, как показано ниже кодом:

let getPixels (bmp:Bitmap) =
  seq {
        for i = 0 to bmp.Height-1 do
            yield seq {
                            for j = 0 to bmp.Width-1 do
                                let pixel = bmp.GetPixel(j,i)
                                yield (pixel.R,pixel.G,pixel.B)
                       }
  }
2 голосов
/ 06 января 2011

Что вы подразумеваете под ленивым?

Массив не является ленивым типом данных, что означает, что если вы хотите использовать массивы, вам нужно загрузить все пиксели во время инициализации.Если бы мы использовали одномерный массив, альтернативой было бы использовать seq<_>, что лениво (но вы можете обращаться к элементам только последовательно).Для многомерных массивов нет ничего подобного seq<_>, поэтому вам нужно будет использовать что-то еще.

Вероятно, самым близким вариантом будет использование трехмерного массива отложенных значений (Lazy<int>[,,]).Это массив задержанных групповых сигналов, которые обращаются к пикселям и оцениваются только тогда, когда вы действительно читаете значение в местоположении.Вы можете инициализировать его следующим образом:

for i=0 to Width 
  for j=0 to Height 
    let point = lazy image.GetPixel(i,j) 
    pixels.[0,i,j] <- lazy point.Value.R 
    pixels.[1,i,j] <- lazy point.Value.G 
    pixels.[2,i,j] <- lazy point.Value.B

Фрагмент создает ленивое значение, которое читает пиксель (point), а затем три ленивых значения, чтобы получить отдельные цветовые компоненты.При доступе к компоненту цвета оценивается значение point (путем доступа к Value).

Единственное отличие в остальной части вашего кода состоит в том, что вам нужно будет вызвать Value (например, pixels.[0,10,10].Value чтобы получить фактический компонент цвета пикселя.

Вы можете определить более сложные структуры данных (например, ваш собственный тип, который поддерживает индексирование и ленив), но я думаю, что массив ленивых значений должен быть хорошимотправная точка.

...