Имя: Пароль:
IT
 
Масштабирование матрицы
0 queit
 
14.11.11
12:06
Всем привет.
Суть задачки следующая: есть большое изображение, из которого я плучаю часть (прямоугольную область). В этой области имеется изображение цифры. Далее я пределяю контуры цифры и загоняю это все дело в двумерный массив. Тем самым плучается некоторый отпечаток цифры. При этом размерность двумерного массива будет варьироваться в зависимисти от первоначальных размеров облассти (например, 9х19 - ширина и высота соответственно). Для примера, двумерный массив изображения цифры 1:

000011000
001111100
111111100
001111000
001111000
001111000
001111000
001111000
001111000
001111000
001111000
001111000
001111000
001111000
001111000
001111000
001111000
001111100
111111111

Далее есть дикое желание :-) загнать этот массив в массив 16х16, но таким образом, чтобы не потерялись данные.
Нашел в иинете небольшой пссевдокод и перевел его на с++:
   int    nSymbol;    // кол-во значимых пикселей
   double percent; // процент заполнения Percent
   Points XY[17];  // массив относительных координат анализируемых ячеек
   int newH, newW;    // новые высота и ширина
   int ki, kj;        // вспомогательные координаты при масштабировании
   int Mas16x16[16][16]; // массив масштабированного изображения

   fill(Mas16x16[0], Mas16x16[0] + 16*16, 0);

   //----------------------------------------------------------------------------
   // получаем процент заполнения как отношение кол-ва значимых пикселей к общему
   // кол-ву пикселей в границах образа
   // Percent будет необходим при анализе каждой ячейки в разбитом на 16х16 образе
   nSymbol = 0;
   for(int j = 0; j < cur_H; j++)
       for(int i = last_x; i < cur_x; i++)
           if(ArreyPix[j][i] == 1)
               nSymbol++;
   percent = (double) nSymbol / (cur_H*(cur_x - last_x));
   percent = 0.99*percent; // коэф-т влияет на формирование матрицы 16х16
                               // > 1 - учитывается меньше значимых пикселей
                               // < 1 - учитывается больше значимых пикселей

   //----------------------------------------------------------------------------
   // разбиваем прямоугольник образа на 16 равных частей путем деления сторон на 2
   // и получаем относительные координаты каждой ячейки

   newW = cur_x - last_x;
   XY[0].x  = 0;
   XY[16].x = newW;
   XY[8].x  = Divides(XY[16].x);
   XY[4].x  = Divides(XY[8].x);
   XY[2].x  = Divides(XY[4].x);
   XY[1].x  = Divides(XY[2].x);
   XY[3].x  = Divides((XY[4].x + XY[2].x));
   XY[6].x  = Divides((XY[8].x + XY[4].x));
   XY[5].x  = Divides((XY[6].x + XY[4].x));
   XY[7].x  = Divides((XY[8].x + XY[6].x));
   XY[12].x = Divides((XY[16].x + XY[8].x));
   XY[10].x = Divides((XY[12].x + XY[8].x));
   XY[14].x = Divides((XY[16].x + XY[12].x));
   XY[9].x  = Divides((XY[10].x + XY[8].x));
   XY[11].x = Divides((XY[12].x + XY[10].x));
   XY[13].x = Divides((XY[14].x + XY[12].x));
   XY[15].x = Divides((XY[16].x + XY[14].x));

   newH = cur_H;
   XY[0].y  = 0;
   XY[16].y = newH;
   XY[8].y  = Divides(XY[16].y);
   XY[4].y  = Divides(XY[8].y);
   XY[2].y  = Divides(XY[4].y);
   XY[1].y  = Divides(XY[2].y);
   XY[3].y  = Divides((XY[4].y + XY[2].y));
   XY[6].y  = Divides((XY[8].y + XY[4].y));
   XY[5].y  = Divides((XY[6].y + XY[4].y));
   XY[7].y  = Divides((XY[8].y + XY[6].y));
   XY[12].y = Divides((XY[16].y + XY[8].y));
   XY[10].y = Divides((XY[12].y + XY[8].y));
   XY[14].y = Divides((XY[16].y + XY[12].y));
   XY[9].y  = Divides((XY[10].y + XY[8].y));
   XY[11].y = Divides((XY[12].y + XY[10].y));
   XY[13].y = Divides((XY[14].y + XY[12].y));
   XY[15].y = Divides((XY[16].y + XY[14].y));

   //----------------------------------------------------------------------------
   // анализируем каждую полученную ячейку в разбитом прямоугольнике образа
   // и создаем приведенную матрицу 16x16

   for(kj = 0; kj < 15; kj++)
       for(ki = 0; ki < 15; ki++){
           nSymbol = 0;
           for(int j = XY[kj].y; j < XY[kj+1].y; j++)                          // пробегаемся по ячейкам уже
               for(int i = last_x + XY[ki].x; i < last_x + XY[ki+1].x; i++)  // в абсолютных координатах

               if(ArreyPix[j][i] == 1)
                   nSymbol++;    // считаем кол-во значимых пикселей (=1 -> черный цвет)

                   // если отношение кол-ва знач. пикселей к общему кол-ву в ящейке > характерного процента заполнения то = 1 иначе = 0
               if(nSymbol / max(1, ((XY[ki+1].x - XY[ki].x) * (XY[kj+1].y - XY[kj].y))) > percent)
                   Mas16x16[kj][ki] = 1;
               else Mas16x16[kj][ki] = 0;                          // результат - приведенная матрица 16х16
       }

В результате пполучается:
000000000101000
000001010101010
010101010101010
000001010101000
000001010101000
000001010101000
000001010101000
000001010101000
000001010101000
000001010101000
000001010101000
000001010101000
000001010101000
000001010101000
000001010101000

Т.е. есть разрывы и неточности в результате.
Подскажите, как сделать чтобы алгоритм сработал правильно (возможно где-то я ошибся)? Или возможно уже есть какое-то решение?
1 Axel2009
 
15.11.11
11:16
матрица получается 15/15 судя по ноликам.
плюс ко всему по моему проще и лучше сначала в зависимости от масштаба увеличить до нужных размеров там где меньше. а после увеличения - уменьшать там где больше.
2 dk
 
15.11.11
11:20
вроде стандартная задачка на масштабирование изображения, только вместо точки цифра
поищи работу с изображением - там несколько формул для 2д / 3д обработки изображений - масштабирование / поворот / зеркалирование / ...
давно в институте проходили, но помню, что формулы были простые
3 queit
 
15.11.11
14:27
(1) благодарю, верно подмечено.
должно быть так:
       for(kj = 0; kj <= 15; kj++)
               for(ki = 0; ki <= 15; ki++){
                       nSymbol = 0;
                       for(int j = XY[kj].y; j < XY[kj+1].y; j++)                                        // пробегаемся по ячейкам уже
                               for(int i = last_x + XY[ki].x; i < last_x + XY[ki+1].x; i++)  // в абсолютных координатах

                                       if(ArreyPix[j][i] == 1)
                                               nSymbol++;      // считаем кол-во значимых пикселей (=1 -> черный цвет)

                                       // если отношение кол-ва знач. пикселей к общему кол-ву в ящейке > характерного процента заполнения то = 1 иначе = 0
                               if((double)nSymbol / max(1, ((XY[ki+1].x - XY[ki].x) * (XY[kj+1].y - XY[kj].y))) > percent)
                                       Mas16x16[kj][ki] = 1;
                               else Mas16x16[kj][ki] = 0;                          // результат - приведенная матрица 16х16
               }

Результат полуается:
0000000001010000
0000010101010100
0101010101010100
0000010101010000
0000010101010000
0000010101010000
0000010101010000
0000010101010000
0000010101010000
0000010101010000
0000010101010000
0000010101010000
0000010101010000
0000010101010000
0000010101010000
0101010101010111
4 queit
 
15.11.11
14:27
но все равно есть пропуски
5 queit
 
15.11.11
14:30
(2) да, есть Аффинное преобразование
вот только там есть некий коэффициент масштабирования по осям ах и ау
если взять реальную задачку, то коэффициенты получаются дробными, а у меня один элемент матрицы - это один пиксель, аа он неделимый.
Получается, что нужно как-то рассматривать соседние элементы.
что-то я совсем не соображу... :-(
6 acsent
 
15.11.11
14:33
(5) Линейный масштабированием изменяешь размер?
7 Axel2009
 
15.11.11
14:37
(3) реально единичка правильная подставилась только в нижней строке праве 3 единички. в остальных всех случаях возвращается 0 между 1цами
8 queit
 
15.11.11
14:38
(6) не совсем понял что такое линейный, но попробую ответить.
о сути получаетя что да, т.к. у меня исходная матрица и масштабированная состоят из неделимых элементов
9 queit
 
15.11.11
14:39
(7) получается, что просто увеличился масштаб путем добавления нулевых столбцов
10 queit
 
15.11.11
14:41
(6) какой еще можно взять масшаб?
11 Axel2009
 
15.11.11
14:42
(9) ну а нужно я так понимаю если между 2 соседние единички, тогда в результат вставлять единичку а не нолик
12 queit
 
15.11.11
14:45
(11) меня тоже первоначально такая идея посещала.
После него пребежаться по результитующей матрице и доставить единицы, по правилу:
если левый и правый сосед единицы, то ставим единицу. только пока не знаю как быть с граничными элементами.
Вот, напиример, цифра 2 выглядит так:
0000000101110000
0000011101111000
0001011101111100
0011011101111110
0011000000111110
0000000000011110
0000000000011100
0000000000011100
0000000000011000
0000000000110000
0000000001110000
0000000101100000
0000001100000011
0000011101111110
0001011101111110
0011011101111110

Справа в верху там не нужно сставить 1, хотя от того что я их туда поставлю суть не поменяется
13 acsent
 
15.11.11
14:47
Тебе нужно просто изменить размер рисунка?
14 acsent
 
15.11.11
14:47
Для начала погугли какие алгоритмя изменения размера есть
15 queit
 
15.11.11
14:47
но меня вот все равно любопытство распирает - этот же код есть на delphi:

 nSymbol := 0;
 for j := yTop to yBottom do
 for i := xLeft to xRight do
   if MasY[j][i] = 0 then inc(nSymbol);
 Percent := nSymbol / ((yBottom - yTop)*(xRight - xLeft));
 Percent := 0.99*Percent;     // коэф-т влияет на формирование матрицы 16х16
                              // > 1 - учитывается меньше значимых пикселей
                              // < 1 - учитывается больше значимых пикселей
 //----------------------------------------------------------------------------


 //----------------------------------------------------------------------------
 // разбиваем прямоугольник образа на 16 равных частей путем деления сторон на 2
 // и получаем относительные координаты каждой ячейки

 W := xRight - xLeft;;
 XY[0].x  := 0;
 XY[16].x := W;
 XY[8].x  :=   XY[16].x div 2;
 XY[4].x  :=   XY[8].x div 2;
 XY[2].x  :=   XY[4].x div 2;
 XY[1].x  :=   XY[2].x div 2;
 XY[3].x  :=  (XY[4].x + XY[2].x) div 2;
 XY[6].x  :=  (XY[8].x + XY[4].x) div 2;
 XY[5].x  :=  (XY[6].x + XY[4].x) div 2;
 XY[7].x  :=  (XY[8].x + XY[6].x) div 2;
 XY[12].x :=  (XY[16].x + XY[8].x) div 2;
 XY[10].x :=  (XY[12].x + XY[8].x) div 2;
 XY[14].x :=  (XY[16].x + XY[12].x) div 2;
 XY[9].x  :=  (XY[10].x + XY[8].x) div 2;
 XY[11].x :=  (XY[12].x + XY[10].x) div 2;
 XY[13].x :=  (XY[14].x + XY[12].x) div 2;
 XY[15].x :=  (XY[16].x + XY[14].x) div 2;
 H := yBottom - yTop;
 XY[0].y  := 0;
 XY[16].y := H;
 XY[8].y  :=   XY[16].y div 2;
 XY[4].y  :=   XY[8].y div 2;
 XY[2].y  :=   XY[4].y div 2;
 XY[1].y  :=   XY[2].y div 2;
 XY[3].y  :=  (XY[4].y + XY[2].y) div 2;
 XY[6].y  :=  (XY[8].y + XY[4].y) div 2;
 XY[5].y  :=  (XY[6].y + XY[4].y) div 2;
 XY[7].y  :=  (XY[8].y + XY[6].y) div 2;
 XY[12].y :=  (XY[16].y + XY[8].y) div 2;
 XY[10].y :=  (XY[12].y + XY[8].y) div 2;
 XY[14].y :=  (XY[16].y + XY[12].y) div 2;
 XY[9].y  :=  (XY[10].y + XY[8].y) div 2;
 XY[11].y :=  (XY[12].y + XY[10].y) div 2;
 XY[13].y :=  (XY[14].y + XY[12].y) div 2;
 XY[15].y :=  (XY[16].y + XY[14].y) div 2;
 //----------------------------------------------------------------------------


 //----------------------------------------------------------------------------
 // анализируем каждую полученную ячейку в разбитом прямоугольнике образа
 // и создаем приведенную матрицу 16x16

 for kj := 0 to 15 do
 for ki := 0 to 15 do
 begin
   nSymbol := 0;
   for j := yTop + XY[kj].y to yTop + XY[kj+1].y do    // пробегаемся по ячейкам уже
   for i := xLeft + XY[ki].x to xLeft + XY[ki+1].x do  // в абсолютных координатах
     if MasY[j][i] = 0 then inc(nSymbol);              // считаем кол-во значимых пикселей (=0 -> черный цвет)
   // если отношение кол-ва знач. пикселей к общему кол-ву в ящейке > характерного процента заполнения то = 1 иначе = 0
   if nSymbol / MAX(1, ((XY[ki+1].x - XY[ki].x) * (XY[kj+1].y - XY[kj].y))) > Percent
     then Result[kj][ki] := 1 else Result[kj][ki] := 0; // результат - приведенная матрица 16х16
 end;

если ему подссовывать тоже самое изображение, то он все правильно делает, а вот на c++ ниига не хочет :-)
тут либо я где-то ошибся либо нужно сделать какую-то доработку
16 Axel2009
 
15.11.11
14:48
считаем кол-во значимых пикселей (=0 -> черный цвет)
17 queit
 
15.11.11
14:50
(14) конечно погуглил :-)
(13) по сути дела да, но я там если изменять изображения другими методами, то могут возникнуть градации серого в ненужных местах + нужно применять фильтры. в общем как-то сложно все
18 queit
 
15.11.11
14:51
(16) там считается что значимый пиксель 0, а у меня 1 - суть от этого не меняется
19 aleks-id
 
15.11.11
14:51
SetStretchBltMode, StretchDIBits, StretchBlt
сначала колдуй с битмапом, потом заколдованый в 16х16 преобразуй в двоичные данные.
20 queit
 
15.11.11
14:52
(19) ок, спасибо. ща попробую что там делается
21 queit
 
15.11.11
15:47
м-да, функции SetStretchBltMode, StretchDIBits, StretchBlt хороша вещь, но не совсем понятно, во-первых, как преобразовать bitmap в DC, а потом обратно, во-вторых, скорее всего произойдет искажение (судя по форумам) и придется применять какие-то фильтры.
Проще скорее всего разобраться что ни так в моём коде, но все равно покопаю указанные функции.
22 queit
 
15.11.11
17:35
всё! разрешилась проблема :-)
вот такой код должен быть:
for(int j = XY[kj].y; j < XY[kj+1].y; j++)
for(int i = last_x + XY[ki].x; i <= last_x + XY[ki+1].x; i++)

а то я уже пост обработку зафигачил:
int last_value;
       for(kj = 0; kj <= 16; kj++)
               for(ki = 0; ki <= 16; ki++){
                       if((Mas16x16[kj][ki-1])&&(Mas16x16[kj][ki+1]))
                               Mas16x16[kj][ki] = 1;

               }


Благодарю всех откликнувшихся! )))
Компьютеры — это как велосипед. Только для нашего сознания. Стив Джобс