Установите пиксель в случайное положение в зависимости от яркости в базовом изображении - PullRequest
2 голосов
/ 30 ноября 2011

Надеюсь, это интересный вопрос для некоторых людей.

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

Давайте возьмем это изображение, например:

Clouds

Я хочу создать функцию SetRandomizedPixel, которая получит растровое изображение и установит пиксель в случайной позиции.Вероятность того, что пиксель будет создан в позиции 1, должна быть высокой, в позиции 2 средней и в позиции 3 низкой.

Clouds with numbers

Я работаю с C # и растровым изображением / графикой, но это не должно иметь значения.

Единственное, что мне нужно, это предложение, как начать, потому что я не могу найти хороший способ архивировать это.

Может быть, прочитайте яркость и позициивсех пикселей изображения и сортируйте список по яркости - но как я могу сделать рандомизацию, которая тогда предпочтителен для более ярких областей?1025 * Следующий код приводит к таким изображениям:

Randomized image

Но если мы посмотрим поближе, то с левой стороны будет много пикселей:

Randomized image zoomed

Это код (C # / MVC3):

public ActionResult RandomTest()
{

    const int points = 500;

    Bitmap bitmap = new Bitmap(Server.MapPath("~/Files/random-test.jpg"));
    Random random = new Random();

    int imageWidth = bitmap.Width;
    int imageHeight = bitmap.Height;

    float[][] weightedPixels = ConvertImageToGrayScale(bitmap);
    float totalValue = weightedPixels.Sum(i => i.Sum());

    for (var y = 0; y < imageHeight - 1; y++)
    {
        for (var x = 0; x < imageWidth - 1; x++)
        {
            weightedPixels[y][x] /= totalValue;
        }
    }

    for (var i = 0; i < points; i++)
    {
        double randomNumber = random.NextDouble();
        double currentSum = 0;
        for (var y = 0; y < imageHeight - 1; y++)
        {
            for (var x = 0; x < imageWidth - 1; x++)                    
            {
                currentSum += weightedPixels[y][x];
                if (currentSum >= randomNumber)
                {
                    bitmap.SetPixel(x, y, Color.Red);
                    break; 
                }
            }
        }
    }

    // output
    var stream = new MemoryStream();
    bitmap.Save(stream, ImageFormat.Png);
    return File(stream.ToArray(), "image/png");
}


public float[][] ConvertImageToGrayScale(Bitmap bm)
{
    var b = new Bitmap(bm);
    var data = new List<float[]>();
    for (var i = 0; i < b.Width; i++)
    {
        var row = new List<float>();
        for (int x = 0; x < b.Height; x++)
        {
            var oc = b.GetPixel(i, x);
            var grayScale = (int)((oc.R * 0.3) + (oc.G * 0.59) + (oc.B * 0.11));
            row.Add(grayScale);
        }
        data.Add(row.ToArray());
    }
    return data.ToArray();
}

Ответы [ 3 ]

4 голосов
/ 30 ноября 2011

Вы можете сделать изображение в градациях серого и затем суммировать общее значение пикселей.Затем, если вы разделите каждый пиксель на эту сумму, вы получите вес.Затем вы выбираете число от 0 до 1 и начинаете с первого взвешенного пикселя и продолжаете суммировать значения, пока не доберетесь до первого пикселя, который делает сумму больше, чем ваше случайное число.Затем раскрасьте этот пиксель на исходном изображении так, как вы хотите.

Редактировать: Psuedo Code

int imageWidth = ...
int imageHeight = ...
int[][] grayScale = ConvertImageToGrayScale(yourImage);
int totalValue = grayScale.Sum();

float[][] weightedPixels = grayScale.AsType(float[][]);
for (int y in 0...imageHeight-1)
     for (int x in 0...imageWidth-1)
         weightedPixels[y][x] /= totalValue;

float randomNumber = RandomReal();
float currentSum = 0;
for (int y in 0...imageHeight-1)
     for (int x in 0...imageWidth-1)
         currentSum += weightedPixels[y][x];
         if (currentSum >= randomNumber)
             break 2; // Here is your random pixel at (x, y)

Редактировать: Теория

Теория, лежащая в основе этого, заключается в том, что мы хотим преобразовать изображение в распределение вероятностей на основе яркости каждого пикселя.Таким образом, более яркие пиксели будут выбраны с большей вероятностью, чем тусклые.Преобразование в оттенки серого помогает нам генерировать PDF или weightedMatrix, уменьшая каждый пиксель до одного числа вместо тройной RGB.Мы берем сумму матрицы оттенков серого, чтобы получить общее значение, необходимое для создания взвешенной матрицы.Взвешенная матрица инициализируется матрицей оттенков серого, но затем каждый элемент делится на общий вес.Это означает, что сумма взвешенной матрицы равна 1, что является требованием для ее представления в формате PDF.Теперь мы можем выбрать случайную вероятность, которая находится между 0 и 1. Используя это, мы выбираем пиксель, суммируя по взвешенной матрице, пока сумма не станет больше, чем наша вероятность.Пиксель, на котором это происходит, является нашим случайно выбранным пикселем.Это стандартный способ случайного выбора элемента из списка с вероятностями, связанными с каждым элементом.

Редактировать: Исправление левой боковой линии

Проблема, которую принес ОПup имеет отношение к граничным условиям из цикла по массиву в Y, X строк сканирования.Если код переработан для использования линейного массива, а затем преобразовать индекс в X, пара Y, сплошная линия слева удаляется.Если средний блок основного метода изменяется на следующий, он работает как задумано:

float[] weightedPixels = ConvertImageToGrayScale(bitmap).SelectMany(r => r).ToArray();
float totalValue = weightedPixels.Sum();

for ( int i = 0; i < weightedPixels.Length; i++) {
        weightedPixels[i] /= totalValue;
}

for (int pIdx = 0; pIdx < points; pIdx++)
{
    double randomNumber = random.NextDouble();
    double currentSum = 0;
    for (int i = 0; i < weightedPixels.Length; i++)
    {
        currentSum += weightedPixels[i];
        if (currentSum >= randomNumber)
        {
            int y = i / imageWidth;
            int x = i % imageWidth;
            bitmap.SetPixel(x, y, Color.Red);
            break;
        }
    }
}
1 голос
/ 01 декабря 2011

Мне нравятся решения для взвешенных случайных чисел как для troutinator, так и для Matthias Odisio, но я думаю, что для большого изображения это может потребовать значительных ресурсов процессораОсобенно, если нужно подобрать несколько пикселей.Я бы предпочел метод Монте-Карло .Вот некоторый псевдокод:

Normalize(I)                //in [0,1] range, with 1=bright
n=0
While n < number_of_pixels_to_pick
  x = random(image_size(1)) //random pixel position
  y = random(image_size(2))
  p = random()              // in [0,1] range
  If p < I(x,y) do
    select pixel
    n=n+1
  Endif
Endwhile

Редактировать: Против этого решения, если изображение очень темное, оно может повторяться дольше, чем детерминированный подход.

1 голос
/ 30 ноября 2011

Вы ищете взвешенные случайные пики.

Ответ на этот вопрос должен помочь вам начать программирование низкого уровня: Взвешенные случайные числа

Вот как это выглядит в Mathematica, используя встроенную функцию RandomChoice:

{w, h} = ImageDimensions@img;
brightness = ImageData@ColorConvert[img, "Grayscale"];

(* number of pixels to change *)
npix = 150;
(* new value for changed pixels *) 
newrgb = {1, 1, 1};   

Image[ ReplacePart[
    ImageData@img, 
    RandomChoice[
        Flatten@brightness -> Tuples[{Range[1, h], Range[1, w]}],
        npix] -> newrgb]]

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...