如何进行图像转换?

时间:2016-04-25 15:21:12

标签: image transformation

我有一个带有彩色斑点的图像,边缘模糊(上半部分),我想为它创建一个由直线构成的轮廓(下半部分):

enter image description here

我没有填写形状的问题,只需添加轮廓即可。可以变成黑&如有必要,可以使用白色图像。

有人能指出我可以做到的简单转换/程序吗?我最好能找到一些代码示例。

2 个答案:

答案 0 :(得分:3)

我觉得要用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)

答案 1 :(得分:2)

不是一个完整的答案,但也许足以让你开始,或者足以让其他人发表评论并添加一些更多的想法 - 并且没有人说答案必须完整。

我只是从命令行使用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算法来获取线段。

然后对红色像素接触黑色像素以获得下方的线段的所有点也这样做。