Как программно подсчитать количество пузырей на фото, Фото внутри.
[COLOR="Red"]от Модератора: Ты мне весь раздел уже запузырил...[/COLOR]
Навскидку - алгоритм с двумя настроечными коэффициентами + ...дополнительно небольшая настройка.
Вот с этой дополнительной настройки и начнем:
1. Надо привести цветное изображение к полутоновому (серому). Какие подобрать коэффициенты при цветовых составляющих - одна возможность настройки.
2. После того, как изображение приведено к серому, необходимо выбрать уровень (1-й настроечный параметр), ниже которого все считать черным, а выше - белым.
3. Привести изображение к монохромному (черно-белому).
4. Пусть у нас основная масса черная, а блики, которые нужно считать - белые.
5. Проходим последовательно все строки изображения.
6. Если точка черная, ничего не делаем - переходмим к следующей.
7. Если точка белая - начинаем процедуру закраски.
8. Закрашиваем все белые точки смежные с найденной. Т.е. закрашиваем черным все белое пятно. Подсчитываем точки в этом пятне.
9. Если количество точек в пятне больше определенного количества (второй настроечный параметр), то учитываем это пятно при подсчете.
10. После закраски пятна переходим к следующей точке по очереди после той, с которой мы начинали закраску.
Количество учтенных пятен и должно равняться количеству пузырей.
Примечание: на шаге 8 у нас есть еще одна возможность настройки - считать либо не считать смежными точки, находящиеся по диагонали.
З.Ы. Гы, блин... Галактики какие-то получились... :D
З.Ы. Гы, блин... Галактики какие-то получились... :D
А можешь рассказать или кинуть ссылку на то как это сделать, а то облазил нет так ничего и не нашёл. Кстати это фильтр фотошопа Stamp?
Собственно, именно этому посвящены первые 4 пункта моего предыдущего сообщения.
Нет, это не Photoshop (из него в png конвертил). Какая-то моя наработка (вроде как для heightmap) но исходники потерялись... :(
Да не сложно там, при желании - статей в сети уйма...
Значит - не дано. Пост №2 смотри, и ищи как написано (по пунктам).
А зачем?
За хлебом: так преобразовать цвета в градации серого проще.
Можно, в принципе, вааще ничего не преобразовывать, и использовать для получения картины бликов (вектор(точка.цвет)).длину, посчитанную по Пифу
Можно, в принципе, вааще ничего не преобразовывать, и использовать для получения картины бликов (вектор(точка.цвет)).длину, посчитанную по Пифу
"Проще", говоришь?
Во-первых, преобразование к градациям серого осуществляется по формуле:
Y = Wr*R + Wg*G + Wb*B
где Wr, Wg и Wb - весовые коэффициенты равные 0.30, 0.59 и 0.11 соответственно.
А ну-ка приведи код, который делает это "проще" через преобразование в HSV.
Во-вторых, в данной задаче преобразовывать в градации серого не требуется Требуется подобрать такие коэффициенты, при которых обеспечивается наиболее адекватный вариант подсчета.
Могу предложить, например, такме: Wr=0, Wg=0.5 и Wb=0.5.
Никакого отношения к градациям серого это иметь не будет, а результат может дать лучше.
Другими словами ты предлагаешь крайне неэкономичный способ решения совсем другой задачи, нежели требуется.
var
i, j : integer;
line : PRGBTriple;
r, g, b : byte;
begin
for i := rect.Top to rect.Bottom - 1 do
begin
line := bmp.ScanLine;
for j := rect.Left to rect.Right - 1 do
begin
r := line.rgbtRed;
g := line.rgbtGreen;
b := line.rgbtBlue;
histogramInfo[j, i] := r shr 16 + g shr 8 + b;
if (histogramInfo[j, i] >= level) then
binaryImage[j, i] := 1
else
binaryImage[j, i] := 0;
Inc(line);
end;
end;
level - порог, выше которого белое, ниже чёрное.
Теперь я столкнулся с такой проблемой: как подсчитать количество фигур на бинарном изображении. Попробовал написать - для простейших фигур работает нормально (рисунок test), для остальных - не правильно считает. Вот мой код, пока это единственный алгоритм который правильно определяет хотя бы на простых изображениях (я их прикрепил к посту):
var
i, j, k, l : integer;
numberOfBubbles : integer;
hasNumberTwo : boolean;
begin
numberOfBubbles := 0;
for i := 0 to BINAR_Y_SIZE do
begin
for j := 0 to BINAR_X_SIZE do
begin
if (binaryImage[i, j] = 1) then
begin
hasNumberTwo := false;
for k := i - 1 downto 0 do
begin
for l := j to BINAR_X_SIZE do
begin
if (binaryImage[k, l] = 0) then
begin
break;
end
else if (binaryImage[k, l] = 2) then
begin
hasNumberTwo := true;
end;
end;
end;
for k := i to BINAR_Y_SIZE do
begin
for l := j to BINAR_X_SIZE do
begin
if (binaryImage[k, l] = 0) then
begin
break;
end;
if (binaryImage[k, l] = 2) then
begin
hasNumberTwo := true;
end;
binaryImage[k, l] := 2;
end;
for l := j - 1 downto 0 do
begin
if (binaryImage[k, l] = 0) then
begin
break;
end;
if (binaryImage[k, l] = 2) then
begin
hasNumberTwo := true;
end;
binaryImage[k, l] := 2;
end;
end;
if (not hasNumberTwo) then
begin
Inc(numberOfBubbles);
end;
end;
end;
end;
result := numberOfBubbles;
end;
Чем он не устраивает?
Чего ж тебя на каждом шаге пинать надо, а?
Тут смотри.
1. Заносишь первую точку в очередь.
2. Извлекаешь одну за другой точки из очереди, закрашивая их и занося в очередь их незакрашенных соседей.
2. Извлекаешь одну за другой точки из очереди, закрашивая их и занося в очередь их незакрашенных соседей.
Это любому идиоту понятно будет. А с мало-мальским примером или ссылочкой можно? Или только воздух сотрясать своими неземными познаниями?
Ну, когда все понятно, дополнительных вопросов не задают.
Вот мне, кстати, непонятно, чем алгоритм, находящийся по ссылке, лучше того же алгоритма, помещенного непосредственно в тело сообщения?
Кроме того, в тексте твоего сообщения чувствуется неприязнь. Хотелось бы знать, чем она обусловлена?
Отнюдь. Только содержательности в твоих постах мало. Если так будет продолжаться, тему закрою. С раздачей слонов, разумеется...
2. Извлекаешь одну за другой точки из очереди, закрашивая их и занося в очередь их незакрашенных соседей.
Я не совсем понимаю, можешь привести пример кода.
Lerkin, спасибо за ссылку, я прочитал, но не очень понял как это реализовать.
Lerkin, спасибо за ссылку, я прочитал, но не очень понял как это реализовать.
Ты, вообще, хоть немного программить умеешь?
X, Y : integer;
end;
function TBubbleFinder.CountBubbles: integer;
var
i, j : integer;
numberOfBubbles : integer;
begin
numberOfBubbles := 0;
for i := 0 to BINAR_Y_SIZE do
begin
for j := 0 to BINAR_X_SIZE do
begin
if (binaryImage[j, i] = 1) then
begin
if (OverflawFigure(j, i) >= minBubbleSquare) then
begin
Inc(numberOfBubbles);
end;
end;
end;
end;
result := numberOfBubbles;
end;
function TBubbleFinder.OverflawFigure(a, b : integer) : integer;
var
i, j : integer;
figureSquare : integer;
begin
PixelsArrayDownToZero;
figureSquare := 1;
Inc(queueOfPixelsLength);
pixelsQueue[queueOfPixelsLength].X := a;
pixelsQueue[queueOfPixelsLength].Y := b;
while (queueOfPixelsLength >= 0) do
begin
i := pixelsQueue[queueOfPixelsLength].Y;
j := pixelsQueue[queueOfPixelsLength].X;
selectedPixels[j, i] := true;
binaryImage[j, i] := 0;
Dec(queueOfPixelsLength);
if (binaryImage[j + 1, i] = 1) then
begin
figureSquare := MarkSelectedPixel(j + 1, i, figureSquare);
end;
if (binaryImage[j - 1, i] = 1) then
begin
figureSquare := MarkSelectedPixel(j - 1, i, figureSquare);
end;
if (binaryImage[j, i - 1] = 1) then
begin
figureSquare := MarkSelectedPixel(j, i - 1, figureSquare);
end;
if (binaryImage[j, i + 1] = 1) then
begin
figureSquare := MarkSelectedPixel(j, i + 1, figureSquare);
end;
end;
result := figureSquare;
end;
function TBubbleFinder.MarkSelectedPixel(j, i,
figureSquare: integer): integer;
begin
if (not selectedPixels[j, i]) then
begin
Inc(figureSquare);
selectedPixels[j, i] := true;
Inc(queueOfPixelsLength);
pixelsQueue[queueOfPixelsLength].X := j;
pixelsQueue[queueOfPixelsLength].Y := i;
end;
result := figureSquare;
end;
Maximillian_Cavalera, если создашь еще одну тему для своих пузырей - наказание последует незамедлительно.
[CENTER] [SIZE="5"][COLOR="Red"]Пузырим тут![/COLOR][/SIZE][/CENTER]
2. Если от одной картинки до другой есть сильные перепады яркости, советую перед обработкой строить для каждой картинки диаграмму, после чего привязывать границу раздела между черным и белым к определенной точка диаграммы.
Можно ручками в фотошопе.
А если имеется в виду "программно", то надо придумать алгоритм такого действа.
Но прежде, чем родится алгоритм, надо бы сформулировать критерий, как отличать границу от всего остального.
В прнинципе, можно попытаться выделить области с максимальным градиентом, а потом провести скелетизацию, после чего избавиться от отдельных точек (в которые должны превратиться блики). Но что из этого получится, сказать нельзя - надо пробовать.
Не могу.
Нет у меня такой ссылки.
Да и непонятно из твоего сообщения, какая именно ссылка тебе нужна.
Поэтому могу предложить сначала тщательно сформулировать, что именно тебе нужно, а затем скормить свою формулировку Гуглу. У него точно есть ссылки.
Могу предложить и альтернативный вариант (сам обычно пользуюсь именно им).
1. Попытаться выполнить поставленную задачу ручками.
2. Тщательно проанализировать и задокументировать свои действия.
3. На основании этих записей составить алгоритм.
4. Написать реализацию этого алгоритма на любом доступном ЯВУ и радоваться жизни.
andriano, ты же это не от балды взял. Можешь скинуть ссылку на это или хотя бы сказать где ты такое узнал.
[COLOR="Red"]
from moderator: самое последнее предупреждение...[/COLOR]
Гм...
Если "балдой" ты называешь <свою> голову, то это твое право.
Я же не говорил ни о чем, до чего нельзя было бы додуматься своей головой. По крайней мере, я обычно так и делаю.
Единственное, что я "позаимствовал", это сам термин "скелетизация", на что и готов дать ссылку: http://forum.sources.ru/index.php?showtopic=231182
PS. В оригинале термин звучит "скелетонизация", но, думаю, более адекватный русский перевод именно "скелетизация". (skeleton - скелет).
Графические фильтры, позволяющие выделять контуры. От простого градиентного, до алгоритма Кэнни.
Не уверен, подойдет ли тебе это. Но я для своих задач, тоже как то писал, простейший фильтр выделения границ, хочешь, глянь код (BCB 6++): При желании, могу на Дельфи привести.
Test->LoadFromFile(opLeft->FileName);
int i, j, sum_previos, sum_current;
float k;
k = StrToFloat(Edit1->Text);
Byte *Line;
for (i=0; i<Test->Height;i++ )
{
Line = (Byte *)Test->ScanLine;
for (j=0; j<3*Test->Width-3; j=j+3)
{
sum_previos = Line[j ] + Line[j+1] + Line[j+2];
sum_current = Line[j+3] + Line[j+4] + Line[j+5];
if ((sum_current<(1-k)*sum_previos)||
(sum_current>(1+k)*sum_previos))
{
Line[j]=0; Line[j+1]=0; Line[j+2]=0;
}
else
{
Line[j]=255; Line[j+1]=255; Line[j+2]=255;
}
}
}
imLeft->Canvas->Draw(0,0, Test);
Test->Free();
Принцип действия такой, сравниваем два соседних пикселя, если различаются больше чем установленная величина - граница, если меньше. то нет.
А кто нидь может сказать алгоритм Кэнни, желательно с исходником.
Все зависит от тех задач, которые нужно реализовать. Для меня даже такой способ подходил. Алгоритм Кенни искал в инете (реализацию), но к сожалению безрезультатно. Посмотри градиентный метод в ссылке, которую тебе дал, вроде не такой сложный в исполнении.
Тьфу блин, самое главное забыл!!! Эта штука (код, который я выложил) работает тока с 24 битным BMP...
Тут же идет последовательный доступ к каждому байту, а такое возможно тока в модели BMP 24 бита.... Может поэтому испорченый телевизор был?
А у тебя нету написанного алгоритма градиентного метода?
{
IplImage *image = TBitmapToIplImage(&bmp);
IplImage* laplace = 0;
IplImage* colorlaplace = 0;
IplImage* planes[3] = { 0, 0, 0 };
// Laplacian
if( !laplace )
{
for(int i = 0; i < 3; i++ )
{
planes = cvCreateImage( cvSize(image->width,image->height), 8, 1 );
}
laplace = cvCreateImage( cvSize(image->width,image->height), IPL_DEPTH_16S, 1 );
colorlaplace = cvCreateImage( cvSize(image->width,image->height), 8, 3 );
}
cvCvtPixToPlane( image, planes[0], planes[1], planes[2], 0 );
for(int i = 0; i < 3; i++ )
{
cvLaplace( planes, laplace, 3 );
cvConvertScaleAbs( laplace, planes, 1, 0 );
}
cvCvtPlaneToPix( planes[0], planes[1], planes[2], 0, colorlaplace );
colorlaplace->origin = image->origin;
// Contours
IplImage *edge = cvCreateImage( cvSize(image->width,image->height), IPL_DEPTH_8U, 3 );
IplImage *convertedImage = cvCreateImage( cvSize(image->width,image->height), IPL_DEPTH_8U, 3 );
cvCanny(convertedImage, edge, 50, 200, 3 ); //вот здесь выдаёт ошибку
IplImageToTBitmap(edge, &outputBmp);
}
вот что получается при использовании лапласа(на картинке)