Обнаружение цвета на GIF или PNG изображении

Нам было интересно, можно ли было сделать что-то вроде прикрепленных картинок.

У нас есть живой метеорологический радар на нашем веб-сайте, который отображается на странице карт Google с циклом обновления 5 минут.

В чем идея?

Мы хотим обнаружить «тяжелые» штормы для наших посетителей и выделить их квадратной коробкой или чем-то еще. Если это возможно, мы хотим сделать эту систему на PHP. Я думаю, что лучший способ — это определить цвета или что-то еще?

Прикрепленные изображения в качестве примера мы нарисовали с помощью Photoshop:

Мы надеемся, что кто-то может помочь нам, чтобы мы могли начать с чего-то!

исходное изображение
сильные штормы выделены квадратными коробками

2

Решение

У меня была еще одна попытка, используя некоторые Connected Component Analysis Программное обеспечение, которое я написал на C. Оно легко компилируется на любом компьютере с OS X / Linux / Windows.

Итак, вот сценарий:

#!/bin/bash

# Make red areas white and all else black for blob analysis
convert http://i.stack.imgur.com/qqein.png \
-fuzz 50%                               \
-fill white +opaque red                 \
-fill black -opaque red -colorspace gray -negate -depth 16 weather.pgm

# Run Connected Component Analysis to find white blobs and their areas and bounding boxes
./cca < weather.pgm > /dev/null 2> info.txt

# Find blobs with more than 100 pixels
while read a b ;do
draw="$draw -draw \"rectangle $a $b\" "done < <(awk '/Area/{area=$5+0;if(area>100)print $7,$8}' info.txt)

# Now draw the rectangles on top of the source image
eval convert http://i.stack.imgur.com/qqein.png -strokewidth 2 -stroke red -fill none "$draw" result.png

Файл weather.pgm выходит так:

введите описание изображения здесь

Частичный вывод cca программа

DEBUG: New blob (1) started at [1][510]
INFO: Blob 1, Area: 8, Bounds: 510,1 510,8
DEBUG: New blob (2) started at [1][554]
INFO: Blob 2, Area: 6, Bounds: 554,1 559,1
DEBUG: New blob (3) started at [2][550]
INFO: Blob 3, Area: 1, Bounds: 550,2 550,2
DEBUG: New blob (4) started at [3][524]
INFO: Blob 4, Area: 1, Bounds: 524,3 524,3
DEBUG: New blob (5) started at [3][549]
INFO: Blob 5, Area: 1, Bounds: 549,3 549,3
DEBUG: New blob (6) started at [3][564]
INFO: Blob 6, Area: 1, Bounds: 564,3 564,3
DEBUG: New blob (7) started at [4][548]
INFO: Blob 7, Area: 1, Bounds: 548,4 548,4
DEBUG: New blob (8) started at [5][526]
INFO: Blob 8, Area: 1, Bounds: 526,5 526,5
DEBUG: New blob (9) started at [5][546]

Финал convert Команда в скрипте вызывается так:

convert http://i.stack.imgur.com/qqein.png -strokewidth 2 -stroke red -fill none    \
-draw 'rectangle 930,125 958,142' -draw 'rectangle 898,138 924,168'              \
-draw 'rectangle 822,143 846,172' -draw 'rectangle 753,167 772,175'              \
-draw 'rectangle 658,181 758,215' -draw 'rectangle 759,186 803,197'              \
-draw 'rectangle 340,223 372,267' -draw 'rectangle 377,259 429,294'              \
-draw 'rectangle 977,281 988,357' -draw 'rectangle 705,321 751,351'              \
-draw 'rectangle 624,376 658,412' -draw 'rectangle 357,485 380,499' result.png

И результат такой:

введите описание изображения здесь

cca.c Программа выглядит так:

/*******************************************************************************
File: cca.c
Author: Mark Setchell

Description:
Connected Components Analyser and Labeller - see algorithm at
http://en.m.wikipedia.org/wiki/Connected-component_labeling#One-pass_version

Algorithm
=========

1. Start from the first pixel in the image. Set "curlab" (short for "current label") to 1. Go to (2).
2. If this pixel is a foreground pixel and it is not already labelled, then give it the label "curlab" and add it as the first element in a queue, then go to (3). If it is a background pixel, then repeat (2) for the next pixel in the image.

3. Pop out an element from the queue, and look at its neighbours (based on any type of connectivity). If a neighbour is a foreground pixel and is not already labelled, give it the "curlab" label and add it to the queue. Repeat (3) until there are no more elements in the queue.
4. Go to (2) for the next pixel in the image and increment "curlab" by 1.

CurrentLabel=1
for all pixels in image
if this is a foreground pixel
if this pixel is not already labelled
label this pixel with Currentlabel
add this pixel to queue
while there are items in the queue
pop item from queue
for all 4-connected or 8-connected neighbours of this item
if neighbour is foreground and is not already labelled
label this neighbour with Currentlabel
add this neighbour to the queue
endif
endfor
endwhile
increment Currentlabel
endif
else
label as background in output image
endif
endfor

Usage
=====

Usage: cca [-c 4|8] < Binarized16BitPGMFile > Binarized16BitPGMFile

where "-c" specifies whether pixels must be 4- or 8-connected to be considered
as parts of same object. By default 4-connectivity is assumed.

Files can be prepared for this program with ImageMagick as follows:

convert YourImage.[jpg|bmp|png|tif] \
-colorspace gray            \
-threshold 50%              \
-depth 16                   \
[-negate]                   \
FileForAnalysis.pgm

This program expects the background pixels to be black and the objects to be
white. If your image is inverted relative to this, use the "-negate" option.

On OSX, run and view results with ImageMagick like this:

cca < test1.pgm | convert PGM:- -auto-level a.jpg && open a.jpg

*******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>

#define DEFAULT_CONNECTIVITY 4

void Usage() {
printf("Usage: cca [-c 4|8] < InputImage.pgm > OutputImage.pgm\n");
exit(EXIT_FAILURE);

}

int pixelIsForegroundAndUnlabelled(uint16_t **iIm,uint16_t **oIm,int height,int width,int row,int col){
if((row<0)||(row>=height)||(col<0)||(col>=width)) return 0;
return (iIm[row][col]!=0) && (oIm[row][col]==0);
}

// Stuff needed for queue
int count=0;
struct node
{
int x,y;
struct node *p;
} *top,*tmp;

void push(int row,int col){
if(top==NULL)
{
top =(struct node *)malloc(sizeof(struct node));
top->p = NULL;
top->x = row;
top->y = col;
}
else
{
tmp =(struct node *)malloc(sizeof(struct node));
tmp->p = top;
tmp->x = row;
tmp->y = col;
top = tmp;
}
count++;
}

void pop(int *x,int *y){
tmp = top;
tmp = tmp->p;
*x = top->x;
*y = top->y;
free(top);
top = tmp;
count--;
}

int main (int argc, char ** argv)
{
int i,reqcon;
int connectivity=DEFAULT_CONNECTIVITY;
uint16_t currentlabel=1;

while (1) {
char c;

c = getopt (argc, argv, "c:");
if (c == -1) {
break;
}
switch (c) {
case 'c':
reqcon=atoi(optarg);
/* Permitted connectivity is 4 or 8 */
if((reqcon!=4)&&(reqcon!=8)){
Usage();
}
connectivity=reqcon;
break;
case '?':
default:
Usage();
}
}

int width,height,max;
int row,col;

/* Check it is P5 type */
char type[128];
fscanf(stdin,"%s",type);
if (strncmp(type,"P5",2)!=0) {
fprintf(stderr, "ERROR: The input data is not binary PGM, i.e. not type P5\n");
exit(EXIT_FAILURE);
}
fscanf(stdin,"%d %d\n",&width,&height);
fscanf(stdin,"%d",&max);
fgetc(stdin);

/* Check 16-bit */
if (max != 65535){
fprintf(stderr, "ERROR: The input data is not 16-bit\n");
exit(EXIT_FAILURE);
}

// Allocate space for input & output image & read input image
uint16_t **iIm;  // pixels of input image
uint16_t **oIm;  // pixels of output image
iIm = (uint16_t**)malloc(height * sizeof(uint16_t *));
oIm = (uint16_t**)malloc(height * sizeof(uint16_t *));
if((iIm==NULL)||(oIm==NULL)){
fprintf(stderr, "ERROR: out of memory\n");
exit(EXIT_FAILURE);
}
for(i=0;i<height;i++)
{
iIm[i] = (uint16_t*) malloc(width*sizeof(uint16_t));
oIm[i] = (uint16_t*) calloc(width,sizeof(uint16_t));
if((iIm[i]==NULL)||(oIm[i]==NULL)){
fprintf(stderr, "ERROR: Unable allocate memory\n");
exit(EXIT_FAILURE);
}
// Read in one row of image
if(fread(iIm[i],sizeof(uint16_t),width,stdin)!=width){
fprintf(stderr,"ERROR: Reading input file\n");
exit(EXIT_FAILURE);
}
}

// Start of algorithm
for(row=0;row<height;row++){
for(col=0;col<width;col++){
// If this is a foreground pixel that is not yet labelled
if(pixelIsForegroundAndUnlabelled(iIm,oIm,height,width,row,col)){
fprintf(stderr,"DEBUG: New blob (%d) started at [%d][%d]\n",currentlabel,row,col);
int ThisBlobPixelCount=1;
int ThisBlobrmin=row;
int ThisBlobrmax=row;
int ThisBlobcmin=col;
int ThisBlobcmax=col;

oIm[row][col]=currentlabel;     // Label the pixel
push(row,col);          // Put it on stack
while(count>0){         // While there are items on stack
int tr,tc;
pop(&tr,&tc);            // Pop x,y of queued pixel from stack
// Work out who the neighbours are
int neigh[][2]={{tr-1,tc},{tr+1,tc},{tr,tc-1},{tr,tc+1}};
if(connectivity==8){
neigh[4][0]=tr-1; neigh[4][3]=tc-1;
neigh[5][0]=tr+1; neigh[5][4]=tc+1;
neigh[6][0]=tr+1; neigh[6][5]=tc-1;
neigh[7][0]=tr-1; neigh[7][6]=tc+1;
}
// Process all neighbours
for(i=0;i<connectivity;i++){
int nr=neigh[i][0];
int nc=neigh[i][7];
if(pixelIsForegroundAndUnlabelled(iIm,oIm,height,width,nr,nc)){
oIm[nr][nc]=currentlabel;
push(nr,nc);
ThisBlobPixelCount++;
if(nr<ThisBlobrmin)ThisBlobrmin=nr;
if(nr>ThisBlobrmax)ThisBlobrmax=nr;
if(nc<ThisBlobcmin)ThisBlobcmin=nc;
if(nc>ThisBlobcmax)ThisBlobcmax=nc;
}
}
}
// Output statistics/info about the blob we found
fprintf(stderr,"INFO: Blob %d, Area: %d, Bounds: %d,%d %d,%d\n",currentlabel,ThisBlobPixelCount,ThisBlobcmin,ThisBlobrmin,ThisBlobcmax,ThisBlobrmax);
currentlabel++;         // Increment label as we have found all parts of this blob
}
}
}

// Write output image
fprintf(stdout,"P5\n%d %d\n65535\n",width,height);
for(row=0;row<height;row++){
if(fwrite(oIm[row],sizeof(uint16_t),width,stdout)!=width){
fprintf(stderr,"ERROR: Writing output file\n");
exit(EXIT_FAILURE);
}
}
return EXIT_SUCCESS;
}
2

Другие решения

Правильный способ сделать это, вероятно, будет использовать какой-то BLOB-анализ чтобы извлечь красные области и сделать ограничивающие рамки вокруг них. Это не так сложно, но, начав этот подход, я могу сделать что-то гораздо более простое, но довольно эффективное, с помощью одной строчки ImageMagick. Это бесплатно и доступно из командной строки и с PHP, Perl, Python и другими привязками.

Итак, я собирался преобразовать все красные области в белые, а все не красные области в черные, а затем запустить BLOB-анализ и нарисуйте красные ограничительные рамки вокруг белых капель. Но по дороге я подумала о том, чтобы сделать не красные области изображения полупрозрачными, а затем красные области полностью прозрачными, поэтому внимание сосредоточено на красном, а все остальное действительно бледно. Это можно сделать с помощью одной команды ImageMagick, например:

convert http://i.stack.imgur.com/qqein.png               \
\( +clone                                            \
-fuzz 30%                                         \
-fill "#222222" +opaque red                       \
-fill "#ffffff" -opaque red -colorspace gray \)   \
-compose copy-opacity -composite out.png

Результат таков:

введите описание изображения здесь

Числа, очевидно, могут быть изменены, если вам нравится подход …

2

Я бы выделил эритроциты с помощью -FX оператор.

convert source.png -fx '(p.r > p.b && p.r > 0.9) ? p : 0' a_RED.png

Обнаруженный цвет

p.r > p.b удалить белые цвета, а p.r > 0.9 проверяет текущий пиксель по порогу 0.9,

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

2

Я только что обнаружил, что ImageMagick сможет сделать Анализ связанных компонентов так что теперь я могу предоставить еще более простое решение, которое не зависит от моего C-кодирования.

Вот:

#!/bin/bash

draw=$(convert http://i.stack.imgur.com/qqein.png  \
-fuzz 50%                                       \
-fill white +opaque red                         \
-fill black -opaque red                         \
-colorspace gray                                \
-define connected-components:verbose=true       \
-define connected-components:area-threshold=100 \
-connected-components 8                         \
-auto-level baddies.png | \
awk 'BEGIN{command=""}
/\+0\+0/||/id:/{next}
{
geom=$2
gsub(/x/," ",geom)
gsub(/+/," ",geom)
split(geom,a," ")
d=sprintf("-draw \x27rectangle %d,%d %d,%d\x27 ",a[3],a[4],a[3]+a[1],a[4]+a[2])
command = command d
#printf "%d,%d %d,%d\n",a[3],a[4],a[3]+a[1],a[4]+a[2]
}
END{print command}')

eval convert http://i.stack.imgur.com/qqein.png -fill none -strokewidth 2 -stroke red $draw out.png

Вот результирующее изображение:

введите описание изображения здесь

и вот помеченные объекты из файла baddies.png

введите описание изображения здесь

Вот некоторые примечания по коду …

-пух 50% допускает некоторую степень изменения обнаруженных оттенков красного

-заливка белый + непрозрачный красный — меняет все красные пиксели на белые

-заливка черного цвета — непрозрачный красный — меняет все не красные пиксели на черные

-определить подключенные компоненты: verbose = true — вызывает диагностический вывод, так что я могу получить ограничивающие рамки, которые он находит

-определить связанные компоненты: area-threshold = 100 — говорит, что меня интересуют только красные области размером 100 пикселей или больше

-связанные компоненты 8 — говорит, что красные точки могут быть соединены с их 8-соседями (то есть соединены по диагонали, а не соединены квадратно)

-автоматический уровень baddies.png — Контраст растягивает помеченные штормовые объекты и сохраняет их в файле с именем baddies.png

awk материал так же, как awk вещи в моем другом ответе.

Просто для того, чтобы другие люди увидели результаты анализа подключенных компонентов ImageMagick на первом этапе, они выглядят так:

Objects (id: bounding-box centroid area mean-color):
0: 1020x563+0+0 507.6,281.2 567516 gray(253)
495: 53x36+377+259 405.3,273.3 1040 gray(0)
391: 101x35+658+181 699.9,195.6 984 gray(0)
515: 13x77+976+281 982.5,321.4 863 gray(0)
581: 35x37+624+376 641.9,397.1 740 gray(0)
439: 33x45+340+223 352.0,249.2 643 gray(1)
558: 47x32+705+320 727.2,334.8 641 gray(1)
353: 25x30+822+143 834.3,156.1 422 gray(0)
350: 27x31+898+138 911.4,152.7 402 gray(0)
343: 29x18+930+125 944.6,132.2 283 gray(0)
392: 45x12+759+186 783.0,193.0 276 gray(0)
663: 24x15+357+485 367.3,493.4 192 gray(0)
531: 98x58+169+297 209.4,336.2 152 gray(0)
377: 20x9+753+167 762.6,170.6 106 gray(0)

Параметры к финалу convert Команда выглядит так:

convert http://i.stack.imgur.com/qqein.png -fill none -strokewidth 2 -stroke red \
-draw 'rectangle 377,259 430,295' \
-draw 'rectangle 658,181 759,216' \
-draw 'rectangle 976,281 989,358' \
-draw 'rectangle 624,376 659,413' \
-draw 'rectangle 340,223 373,268' \
-draw 'rectangle 705,320 752,352' \
-draw 'rectangle 822,143 847,173' \
-draw 'rectangle 898,138 925,169' \
-draw 'rectangle 930,125 959,143' \
-draw 'rectangle 759,186 804,198' \
-draw 'rectangle 357,485 381,500' \
-draw 'rectangle 169,297 267,355' \
-draw 'rectangle 753,167 773,176' out.png
1
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector