2015-01-13 3 views
2

Нам было интересно, можно ли сделать что-то вроде прикрепленных фотографий.Определение цвета на изображении GIF или PNG

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

Что за идея?

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

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

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

original image heavy storms highlighted with square boxes

ответ

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 выходит так:

enter image description here

Частичный выход 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 

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

enter image description here

Программа 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; 
} 
+0

** + 1 ** - Хороший! (Не могли бы вы также перенести сам вопрос - он не заслуживает того, чтобы downvote [не компенсировался * моим * upvote]? - И пока вы на это, можете ли вы прочитать [этот вопрос] (http: /stackoverflow.com/q/23495372/359307) и проголосовать за повторное открытие, если вы согласны со мной? - Спасибо.) –

+0

Отличный ответ. Именно то, что нам нужно! – user3408380

+0

Возможно ли это и в Python, с нами было бы намного легче работать! Спасибо :) – user3408380

2

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

Итак, я собирался преобразовать все красные области в белый, а все некрасные области - в черный, затем запустить Анализ блоба и нарисуйте красные ограничивающие прямоугольники вокруг белых капель. Но в пути я подумал о том, чтобы сделать некрасные области изображения полупрозрачными, а затем красные области полностью прозрачными, поэтому основное внимание уделяется красным вещам, а все остальное - по-настоящему бледным. Это может быть сделано в одной команде 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 

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

enter image description here

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

+0

** + 1 ** Также очень хороший подход :-) –

2

Я бы изолировал красные клетки, используя оператор -fx.

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

Detected color

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

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

+0

** + 1 ** Очень элегантный :-) –

1

Я только что обнаружил, что ImageMagick может сделать Connected Components Анализ поэтому теперь я могу обеспечить еще более простое решение, которое не опирается на моем кодирования 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 

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

enter image description here

и здесь меченые объекты из файла baddies.png

enter image description here

Здесь находятся сома e отмечает код ...

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

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

-fill черный -opaque красный - изменяет все не-красные пиксели в черный

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

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

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

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

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

Просто для других людей, чтобы увидеть результаты Connected компонентного анализа 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 
Смежные вопросы