Форум Flasher.ru

Форум Flasher.ru (http://www.flasher.ru/forum/index.php)
-   ActionScript 3.0 (http://www.flasher.ru/forum/forumdisplay.php?f=83)
-   -   Усредненный цвет области изображения (http://www.flasher.ru/forum/showthread.php?t=123478)

Mur4ik 03.04.2009 01:56

Усредненный цвет области изображения
 
Подскажите, пожалуйста, как получить некое среднее значение цвета в Битмапдате или загруженном изображении?

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

Может кто алгоритмом поможет, может есть уже готовое решение.

VVall 03.04.2009 02:06

Можно посчитать именно среднее - сложить RGB-значения всех точек в заданной области и поделить на количество точек. До этого можно уменьшить изображение с интерполяцией во временный буфер, чтобы перебирать меньше точек.

serenkiy 03.04.2009 03:55

Можно использовать метод
Код AS3:

public function histogram(hRect:Rectangle = null):Vector.<Vector>

у BitmapData. Он представляет распространение отдельных значений цветовых компонентов изображения (красный, зеленый, синий, альфа). Исходя из гистограммы, находим наиболее распространенное значение компонента (например, красный - AA, зеленый - BB, синий - CC...альфа не трогаем). Совмещаем шестнадцатеричные значения и получаем усредненный цвет - 0xAABBCC. Далее или используем его, или находим его контрастный цвет.
Нахождение контрастного цвета - отнимаем от 0xFFFFFF(белый цвет) найденное значение (в нашем случае 0xAABBCC), итоговый цвет - контрастный (у нас он равен 0x554433).

Добавлено через 4 минуты
http://f.imagehost.org/0373/Colors.png

Mur4ik 03.04.2009 07:32

вот что у меня получилось методом "тупого" нахождения средних значений цветовых компонент
Код AS3:

public function findFrameColour( $source:BitmapData ):uint
{
        var r:Number = 0;
        var g:Number = 0;
        var b:Number = 0;
 
        var pCount:Number = $source.width * $source.height;
        var pixel:Number;
 
        for (var px:int = 0; px < $source.width; px++)
        {
                for (var py:int = 0; py < $source.height; py++)
                {
                        pixel = $source.getPixel(px, py);
 
                        r += pixel >> 16 & 0xFF;
                        g += pixel >> 8 & 0xFF;
                        b += pixel & 0xFF;
                }
        }
 
        r /= pCount;
        g /= pCount;
        b /= pCount;
 
        return r << 16 | g << 8 | b;
}

Но не всегда он выдает нормальный результат.
Может есть более гармоничный (в плане результирующего цвета) алгоритм?

С histogram() идея понятна, но это все таки под 10-ку, пока не очень хочется на нее ориентироваться....

VVall 03.04.2009 07:49

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

_dm 03.04.2009 13:51

Можно рисовать битмап в другой, который в 2 раза меньше, потом полученный битмап опять рисовать в уменьшенный в два раза, и т.д. пока не получишь изображение в 1 пиксел =)

mre 03.04.2009 14:37

А можно вообще уменьшить до 1 пикселя и взять его цвет :)

silin 03.04.2009 14:46

>>и т.д. пока не получишь изображение в 1 пиксел =)
а чего не сразу-то
Код AS3:

public function findFrameColour( source:BitmapData ):uint
{
        var mtrx:Matrix = new Matrix();
        mtrx.scale(1 / source.width, 1 / source.height);
        var resBmd:BitmapData = new BitmapData(1, 1);
        resBmd.draw(source, mtrx);
        var res:uint = resBmd.getPixel(0, 0);
        resBmd.dispose();
        return res;
}


_dm 03.04.2009 14:47

mre, silin не, так не сработает, надо постепенно в два раза уменьшать :)
Кстати, в играх так расчитывается средняя освещенность сцены, когда используется HDR с адаптацией

Вобще, этот метод имеет смысл использовать если рисование картинки происходит при помощи видяхи, а не процессора. Вроде как в 9 и 10 плеере хардварное рисование, поэтому пусть ГПУ сам считает средний цвет =)

etc 03.04.2009 14:53

Цитата:

Сообщение от _dm (Сообщение 810542)
Вроде как в 9 и 10 плеере хардварное рисование

«Вроде как», но действительности не соответствует.

silin 03.04.2009 15:23

_dm, не понятно тебя: в чем нюанс, что значит "не сработает" ?, у меня почему-то работает
причем тут хардварное рисование тоже не совсем ясно, нативный draw по-любому будет быстрее скриптового счета, а реализуется он программно или на видеокарте - вопрос десятый

VVall 03.04.2009 15:26

Не сработает, потому что при уменьшении не усредняются значения всех точек.

silin 03.04.2009 15:59

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

_dm 03.04.2009 16:11

>>_dm, не понятно тебя: в чем нюанс, что значит "не сработает" ?, у меня почему-то работает
Если честно, я не пробовал рисовать сразу в один пиксель, просто если уменьшать в 2 раза, так будут усреднятся именно соседние пиксели, а если сразу в надцать раз уменьшить - некоторые точки не будут учтены... но если точность не важна, то можно сразу рисовать в 1 пиксель =)

З.Ы. погуглил о хардварном ускорении - в адобовском блоге написано "Starting in version 9.0.115.0, the Flash Player was able to display _fullscreen_ content with GPU assistance."... эх, а я думал оно и в окне ускоряет :(

serenkiy 03.04.2009 16:17

Как я и предлагал, вот пример работы с гистограммой.

Класс "подборщика" цвета:
Код AS3:

package  
{
        import flash.display.BitmapData;
 
        public final class ColorPickup
        {
 
                public function ColorPickup(){}
 
                // Функция нахождения контрастного цвета исходя из цвета
                public static function pickedForColor(color:uint):uint
                {
                        return (0xFFFFFF - color);
                }
 
                // Функция нахождения цвета исходя из изображения
                public static function pickedForBitmap(bitmapData:BitmapData,contrast:Boolean):uint
                {
                        var histogram:Vector.<Vector.<Number>> = bitmapData.histogram(),
                                rmax:uint=0,
                                gmax:uint=0,
                                bmax:uint=0,
                                r:uint=0,
                                g:uint=0,
                                b:uint=0,
                                color:uint;
 
                        for (var i:uint = 0; i < 256; i++) {
                                if (rmax < histogram[0][i]) {
                                        rmax = histogram[0][i];
                                        r = i;
                                }
 
                                if (gmax < histogram[1][i]) {
                                        gmax = histogram[1][i];
                                        g = i;
                                }
 
                                if (bmax < histogram[2][i]) {
                                        bmax = histogram[2][i];
                                        b = i;
                                }
                        }
                        color = ((r << 16) | (g << 8) | b);
                        if(contrast) color=pickedForColor(color);
                        return color;
                }
        }
}

Тестируем:

Код AS3:

package  
{
        import flash.display.Bitmap;
        import flash.display.BitmapData;
        import flash.display.Sprite;
 
        public class project extends Sprite
        {
                [Embed(source = 'testimage.jpg')] private var TestImage:Class;
                private var bitmap:Bitmap = new TestImage();
 
                public function project()
                {
                        addChild(bitmap);
                        bitmap.x = (stage.stageWidth - bitmap.width)/2;
                        bitmap.y = (stage.stageHeight - bitmap.height) / 2;
 
                        var color:uint = ColorPickup.pickedForBitmap(bitmap.bitmapData, false);
 
                        graphics.beginFill(color);
                        graphics.drawRect(bitmap.x - 10, bitmap.y - 10, bitmap.width + 20, bitmap.height + 20);
                        graphics.endFill();
                }       
        }
 
}

Если в функции pickedForBitmap второй параметр (contrast) будет false, то вернется "усредненный" цвет.

пример:
http://g.imagehost.org/0553/contrast.jpg

Mur4ik 03.04.2009 19:39

Всем спасибо.
Метод silin'a тоже выдает не всегда приемлемые результаты (видимо это похоже просто на случайный выбор цвета одного пикселя), например на вечерних фотках, цвет всегда почти черный.
Метод serenkiy с хистограммой, наверное, самый правильный, но он под 10 плеер, "мне ща так низя" :)
Поэтому решил все таки искать средний цвет перебором чуток уменьшенного изображения.
Либо найти 4-8 средних цветов изображения и из них уже выбрать случайно один.

Вот еще осталось непонятно как сделать цвет чуть-чуть темнее?
Т.е не накладывать его с прозрачностью на темный фон или сверху него класть прозрачный темный фон, а именно сам цвет (т.е значение сделать темнее).

BlooDHounD 03.04.2009 19:55

станет потемнее :)
Код AS3:

color = ( ( color       ) & 0xFF - 0x33 ) |
        ( ( color >> 8  ) & 0xFF - 0x33 ) |
        ( ( color >> 16 ) & 0xFF - 0x33 );


serenkiy 04.04.2009 00:35

Ну, раз используется 9я версия, переписал класс для 9й версии:
Код AS3:

package  
{
        import flash.display.BitmapData;
        import flash.utils.ByteArray;
 
        public final class ColorPickup
        {
 
                public function ColorPickup(){}
 
                // Функция нахождения контрастного цвета исходя из цвета
                public static function pickedForColor(color:uint):uint
                {
                        return (0xFFFFFF - color);
                }
 
                // Функция нахождения цвета исходя из изображения
                public static function pickedForBitmap(bitmapData:BitmapData,contrast:Boolean):uint
                {
                        var histogram:Array = getHistogram(bitmapData),
                                rmax:uint = 0,
                                gmax:uint = 0,
                                bmax:uint = 0,
                                r:uint = 0,
                                g:uint = 0,
                                b:uint = 0,
                                color:uint;
 
                        for (var i:uint = 0; i < 256; i++) {
                                if (rmax < histogram[0][i]) {
                                        rmax = histogram[0][i];
                                        r = i;
                                }
 
                                if (gmax < histogram[1][i]) {
                                        gmax = histogram[1][i];
                                        g = i;
                                }
 
                                if (bmax < histogram[2][i]) {
                                        bmax = histogram[2][i];
                                        b = i;
                                }
                        }
                        color = ((r << 16) | (g << 8) | b);
                        if (contrast) color = pickedForColor(color);
                        return color;
                }
 
                //        Функция нахождения гистограммы изображения
                public static function getHistogram(bitmapData:BitmapData):Array
                {
                        var bytes:ByteArray = bitmapData.getPixels(bitmapData.rect),
                                array:Array = new Array(3),
                                currentByte:int,
                                r:int,
                                g:int,
                                b:int;
                        array[0] = new Array(256);
                        array[1] = new Array(256);
                        array[2] = new Array(256);
                        for (var i:uint = 0; i < 256; i++) {
                                array[0][i] = array[1][i] = array[2][i] = 0;
                        }
                        bytes.position = 0;
                        while (bytes.bytesAvailable != 0) {
                                currentByte = bytes.readUnsignedInt();
                                r = (currentByte & 0x00FF0000) >> 16;
                                g = (currentByte & 0x0000FF00) >> 8;
                                b = (currentByte & 0x000000FF);
                                array[0][r] += 1;
                                array[1][g] += 1;
                                array[2][b] += 1;
                        }
                        return array;
 
                }
        }
}


Mur4ik 04.04.2009 03:41

serenkiy, спасибо за класс, пригодится ;)

Вообщем методом проб и ошибок пришел к выводу, что наилучшим является метод с вычислением среднего значения цвета всех точек, а метод гистограмм чаще него выдает результат не подходящий к фотке.
Метод silin'a берет некий цвет прямо из центральной точки фотки (остальные не учитываются), наверное этот способ равносилен просто выборке цвета этой точки :).

Всем спасибо.

Цитата:

Сообщение от BlooDHounD (Сообщение 810687)
станет потемнее :)
Код AS3:

color = ( ( color       ) & 0xFF - 0x33 ) |
        ( ( color >> 8  ) & 0xFF - 0x33 ) |
        ( ( color >> 16 ) & 0xFF - 0x33 );


чет не работает так :)

Но как же все таки сделать цвет потемнее?

Наверное BlooDHounD имел ввиду что то типа такого:

Код AS3:

frameColor = ( ( frameColor >> 16 ) & 0xFF - 0x33 ) << 16 |
                ( ( frameColor >> 8  ) & 0xFF - 0x33 ) << 8 |
                ( ( frameColor      ) & 0xFF - 0x33 );

так вроде работает, правильно ли это?

VVall 04.04.2009 03:54

Надо умножить значения RGB на число от 0 до 1 (1 полная яркость).

Mur4ik 04.04.2009 04:42

Вот что по итогу получилось, может кому пригодится
Код AS3:

 
                /**
                * Возвращает цвет темнее заданного на указанную величину
                * @param        $color Исходный цвет
                * @param        $koeff На сколько делать темнее
                * @return Результирующий цвет
                */

                public static function darker( $color:uint, $koeff:Number = .7 ):uint
                {
                        var r:Number = 0;
                        var g:Number = 0;
                        var b:Number = 0;
 
                        r = ($color >> 16 & 0xFF) * $koeff;
                        g = ($color >> 8 & 0xFF) * $koeff;
                        b = ($color & 0xFF) * $koeff;
 
                        return r << 16 | g << 8 | b;
                }
 
                /**
                * Находит среднее значение цвета всего изображения
                * @param        $source Исходное изображение
                * @param        $scale масштаб изображения для подсчета цвета
                * @return результирующий цвет
                */

                public static function findMedianColour( $source:BitmapData, $scale:Number = .25 ):uint
                {
                        var xScale:uint = Math.round( $source.width * $scale );
                        var yScale:uint = Math.round( $source.height * $scale );
 
                        var mtrx:Matrix = new Matrix();
                        var tempBmd:BitmapData = new BitmapData(xScale, yScale);
 
                        mtrx.scale($scale,$ scale);
                        tempBmd.draw($source, mtrx);
 
                        var r:uint = 0;
                        var g:uint = 0;
                        var b:uint = 0;
 
                        var pCount:uint = xScale * yScale;
                        var pixel:uint;
 
                        for (var px:int = 0; px < xScale; px++)
                        {
                                for (var py:int = 0; py < yScale; py++)
                                {
                                        pixel = tempBmd.getPixel(px, py);
 
                                        r += pixel >> 16 & 0xFF;
                                        g += pixel >> 8 & 0xFF;
                                        b += pixel & 0xFF;
                                }
                        }
 
                        tempBmd.dispose();
 
                        r /= pCount;
                        g /= pCount;
                        b /= pCount;
 
                        return r << 16 | g << 8 | b;
                }

Поправил функцию darker, c умножением каждой компоненты затемнение выглядит более правильно.

BlooDHounD 04.04.2009 15:48

Mur4ik, правильно правильно. забыл обратно преобразовать.


Часовой пояс GMT +4, время: 23:16.

Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.