2016-04-25 90 views
2

我對此有模糊的邊緣(上半部)是彩色的斑點的圖像,我想創建一個大綱它製成的直線(下半部):如何做這個圖像轉換?

enter image description here

我不有問題填充形狀,只是添加輪廓會做。必要時可以變成黑色&白色圖像。

任何人都可以指向一個簡單的轉換/過程,可以做到這一點嗎?最好是我可以很容易找到一個代碼示例。

+1

難道我的答案,或任何其他人,理清問題了嗎?如果是這樣,請考慮接受它作爲您的答案 - 通過點擊投票計數旁邊的空心綠色勾號/複選標記。如果沒有,請說出什麼不起作用,以便我或其他人可以進一步幫助您。謝謝。 http://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work/5235#5235 –

回答

1

不是一個完整的答案,但可能足以讓你開始,或足以讓別人評論和添加更多的想法 - 而且沒有人說答案必須完整無論如何。

我只是在命令行中使用ImageMagick將圖像分割爲三個模糊的灰色紅色,如果嘗試將顏色簡化爲三種顏色,會有點痛苦。 ImageMagick安裝在大多數Linux發行版上,可用於OSX和Windows。

首先,我想讓圖像左上角的所有灰色變成黃色。然後,我想使圖像右下方的所有黑色變成另一個,略微不同的黃色。然後我想把所有不是黃色的東西都變成紅色。上面的每個句子對應的下面一行代碼:

convert wave.jpg \ 
    -fuzz 50% -fill "rgb(255,255,0)" -opaque white \ 
    -fuzz 20% -fill "rgb(250,250,0)" -opaque black \ 
    -fuzz 10% -fill red    +opaque yellow result.png 

enter image description here

現在我可以改變的黃色兩個臨時色調回白色和黑色:

convert result.png -fuzz 0 \ 
    -fill white -opaque "rgb(255,255,0)" \ 
    -fill black -opaque "rgb(250,250,0)" result2.png 

enter image description here

然後我可以使用中值濾鏡來平滑鋸齒:

convert result2.png -median 25x25 result3.png 

enter image description here

我現在可以檢測邊緣,使用-edge

convert result3.png -edge 1 result4.png 

enter image description here

現在你看到它是如何工作的,你可以做一切,在一個簡單的命令:

convert wave.jpg \ 
    -fuzz 50% -fill "rgb(255,255,0)" -opaque white \ 
    -fuzz 20% -fill "rgb(250,250,0)" -opaque black \ 
    -fuzz 10% -fill red    +opaque yellow \ 
    -fuzz 0 -fill white -opaque "rgb(255,255,0)" \ 
    -fill black -opaque "rgb(250,250,0)" -median 25x25 -edge 1 result.png 

現在,您可以找到紅色像素接觸白色像素的所有點 - 我建議您在Magick ++(ImageMagick的C++綁定 - 儘管存在Ruby,Python和PHP綁定,如果您願意)中執行此操作,並將這些點在STL列表中,並應用Ramer–Douglas–Peucker算法獲取線段。

然後,對於紅色像素與黑色像素相接觸的所有點獲取下側的線段。

+0

這有幫助,非常感謝! –

2

我覺得在C++中爲你編寫代碼而不是像我的其他答案那樣使用命令行,所以我把它作爲一個不同的答案。最重要的是,它實際上還實現了Douglas-Peucker算法,並且爲了有趣而且很好的測量,對它進行了動畫處理。

//////////////////////////////////////////////////////////////////////////////// 
// main.cpp 
// Mark Setchell 
// To find a blob in an image and generate line segments that describe it, 
// Use ImageMagick Magick++ and Ramer-Douglas-Peucker algorithm. 
// https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker-algorithm 
// 
// function DouglasPeucker(PointList[], epsilon) 
// // Find the point with the maximum distance 
// dmax = 0 
// index = 0 
// end = length(PointList) 
// for i = 2 to (end - 1) { 
//  d = perpendicularDistance(PointList[i], Line(PointList[1], PointList[end])) 
//  if (d > dmax) { 
//   index = i 
//   dmax = d 
//  } 
// } 
// // If max distance is greater than epsilon, recursively simplify 
// if (dmax > epsilon) { 
//  // Recursive call 
//  recResults1[] = DouglasPeucker(PointList[1...index], epsilon) 
//  recResults2[] = DouglasPeucker(PointList[index...end], epsilon) 

//  // Build the result list 
//  ResultList[] = {recResults1[1...length(recResults1)-1], recResults2[1...length(recResults2)]} 
// } else { 
//  ResultList[] = {PointList[1], PointList[end]} 
// } 
// // Return the result 
// return ResultList[] 
// end 
// 
//////////////////////////////////////////////////////////////////////////////// 

#include <Magick++.h> 
#include <iostream> 
#include <fstream> 
#include <sstream> 
#include <string> 
#include <vector> 
#include <cassert> 
#include <cstdio> 
#include <iostream> 
#include <cmath> 

using namespace std; 
using namespace Magick; 

// Global debug image 
    Image DEBUG_IMAGE; 
    int DEBUG_NUM=0; 
    char DEBUG_NAME[64]; 

#define DEBUG(img) {sprintf(DEBUG_NAME,"debug-%04d.png",DEBUG_NUM++);img.write(DEBUG_NAME);} 

// Point class 
class Point { 
private: 
     double px,py; 
public: 
     // Constructor 
     Point(double x = 0.0, double y = 0.0) { 
     px = x; 
     py = y; 
     } 

     // Getters 
     double x() { return px; } 
     double y() { return py; } 
}; 

// Line class 
class Line { 
private: 
     Point start,end; 
public: 
     // Constructor 
     Line(Point a=Point(0,0), Point b=Point(0,0)){ 
      start=a; 
      end=b; 
     } 
     // Getters 
     double startx() { return start.x(); } 
     double starty() { return start.y(); } 
     double endx() { return end.x(); } 
     double endy() { return end.y(); } 
    double DistanceTo(Point p){ 
     double y2my1 = end.y() - start.y(); 
     double x2mx1 = end.x() - start.x(); 
     double numerator = fabs(y2my1*p.x() - x2mx1*p.y() + end.x()*start.y() - end.y()*start.x()); 
     double denominator = sqrt(y2my1*y2my1 + x2mx1*x2mx1); 
     return numerator/denominator; 
     } 
}; 

void DouglasPeucker(vector<Point>& PointList,int startindex,int endindex,double epsilon,vector<Line>& Results){ 
    // Find the point with the maximum distance 
    double d,dmax=0; 
    int i,index; 
    Line line(PointList[startindex],PointList[endindex]); 

    for(i=startindex+1;i<endindex;i++){ 
     d=line.DistanceTo(PointList[i]) ; 
     if(d>dmax){ 
     index=i; 
     dmax=d; 
     } 
    } 

    // If max distance is greater than epsilon, recursively simplify 
    if (dmax > epsilon) { 
     // Recursive call to do left and then right parts 
     DouglasPeucker(PointList,startindex,index,epsilon,Results); 
     DouglasPeucker(PointList,index,endindex,epsilon,Results); 
    } else { 
     Results.push_back(line); 
     // Rest of else statement is just generating debug image 
     std::list<Magick::Drawable> drawList; 
     drawList.push_back(DrawableStrokeColor("blue")); 
     drawList.push_back(DrawableStrokeWidth(1)); 
     drawList.push_back(DrawableLine(line.startx(),line.starty(),line.endx(),line.endy())); 
     DEBUG_IMAGE.draw(drawList); 
     DEBUG(DEBUG_IMAGE); 
    } 
} 


int main(int argc,char **argv) 
{ 
    InitializeMagick(*argv); 

    // Create some colours 
    Color black = Color("rgb(0,0,0)"); 
    Color white = Color("rgb(65535,65535,65535)"); 
    Color red  = Color("rgb(65535,0,0)"); 
    Color green = Color("rgb(0,65535,0)"); 
    Color blue = Color("rgb(0,0,65535)"); 

    // Create a fuzz factor scaling 
    assert(QuantumRange==65535); 
    const double fuzzscale = QuantumRange/100; 

    // Load wave image 
    Image image("wave.jpg"); 
    int w = image.columns(); 
    int h = image.rows(); 
    cout << "Dimensions: " << w << "x" << h << endl; 

    // Copy for debug purposes 
    DEBUG_IMAGE=image; 

    // Fill top-left greyish area of image with green 
    image.colorFuzz(50*fuzzscale); 
    image.opaque(white,green); 
    DEBUG(image); 

    // Fill bottom-right blackish area of image with blue 
    image.colorFuzz(20*fuzzscale); 
    image.opaque(black,blue); 
    DEBUG(image); 

    // Fill rest of image with red 
    image.colorFuzz(81*fuzzscale); 
    image.opaque(red,red); 
    DEBUG(image); 

    // Median filter to remove jaggies 
    image.medianFilter(25); 
    DEBUG(image); 

    // Find red-green edge by cloning, making blue red, then looking for edges 
    std::vector<Point> RGline; 
    Image RGimage=image; 
    RGimage.opaque(blue,red); 
    DEBUG(RGimage); 
    RGimage.type(GrayscaleType); 
    DEBUG(RGimage); 
    RGimage.normalize(); 
    DEBUG(RGimage); 
    RGimage.edge(1); 
    DEBUG(RGimage); 

    // Now pass over the image collecting white pixels (from red-green edge) 
    // Ignore a single row at top & bottom and a single column at left & right edges 
    // Get a "pixel cache" for the entire image 
    PixelPacket *pixels = RGimage.getPixels(0, 0, w, h); 
    int x,y; 

    for(x=1; x<w-2; x++){ 
     for(y=1; y<h-2; y++){ 
     Color color = pixels[w * y + x]; 
     // Collect white "edge" pixels 
     if(color.redQuantum()==65535){ 
      RGline.push_back(Point(x,y)); 
     } 
     } 
    } 
    cout << "RGline has " << RGline.size() << " elements" << endl; 

    // Results - a vector of line segments 
    std::vector<Line> Results; 

    // epsilon = Max allowable deviation from straight line in pixels 
    // Make epsilon smaller for more, shorter, more accurate lines 
    // Make epsilon larger for fewer, more approximate lines 
    double epsilon=18; 
    DouglasPeucker(RGline,0,RGline.size()-1,epsilon,Results); 
    int lines1=Results.size(); 
    cout << "Upper boundary mapped to " << lines1 << " line segments (epsilon=" << epsilon << ")" << endl; 

    // Find red-blue edge by cloning, making green red, then looking for edges 
    std::vector<Point> RBline; 
    Image RBimage=image; 
    RBimage.opaque(green,red); 
    DEBUG(RBimage); 
    RBimage.type(GrayscaleType); 
    DEBUG(RBimage); 
    RBimage.normalize(); 
    DEBUG(RBimage); 
    RBimage.edge(1); 
    DEBUG(RBimage); 

    // Now pass over the image collecting white pixels (from red-green edge) 
    // Ignore a single row at top & bottom and a single column at left & right edges 
    // Get a "pixel cache" for the entire image 
    pixels = RBimage.getPixels(0, 0, w, h); 

    for(x=1; x<w-2; x++){ 
     for(y=1; y<h-2; y++){ 
     Color color = pixels[w * y + x]; 
     // Collect white "edge" pixels 
     if(color.redQuantum()==65535){ 
      RBline.push_back(Point(x,y)); 
     } 
     } 
    } 
    cout << "RBline has " << RBline.size() << " elements" << endl; 

    DouglasPeucker(RBline,0,RBline.size()-1,epsilon,Results); 
    int lines2=Results.size() - lines1; 
    cout << "Lower boundary mapped to " << lines2 << " line segments (epsilon=" << epsilon << ")" << endl; 
} 

enter image description here

Makefile看起來是這樣的:

main: main.cpp 
     clang++ -std=gnu++11 -Wall -pedantic main.cpp -o main $$(Magick++-config --cppflags --cxxflags --ldflags --libs)