ギザギザ画像を滑らかにする「画質向上ソフト」 ”Image quality improvement software” that smooth jagged images

画質が低い画像を拡大すると、画像がドット絵のようにギザギザに見えます。その画像を滑らかにするソフトを”Processing”で作りました。

When you enlarge an image with low image quality, the image appears jagged like a dot picture. I made software that makes the image smoother with “Processing”.

左が、低画質のギザギザのオリーブの木。これをプロセッシングで作ったソフトで滑らかにしたのが、右の画像です。

Left is a low-quality jagged olive tree. It is the image on the right that made this smooth with software made by ”processing”.

使い方を説明します。

まず、Processsingの実行ボタン(緑の▶)を押します。

すると、「PIC 2X」という画像が出てきます。

ここに、低画質の画像を入れれば、その画像と同じフォルダ内に、滑らかになった画像が現れます。

I will explain how to use it.

First, press the Execute Processing button (green ▶).

Then, the image “PIC 2X” appears.

If you insert a low-quality image here, a smoothed image appears in the same folder as the image.

 

↓on YouTube

ソースコード詳細は、こちら↓Source code details, here ↓

boolean uwagaki = true;
boolean denoise = true;
float s = 1;

/*
import gab.opencv.*;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.highgui.Highgui;
import org.opencv.imgproc.Imgproc;
import java.io.OutputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.awt.image.BufferedImage;
import java.io.FileOutputStream;
*/

void windowsize(int w, int h) {
  frame.setSize( w + frame.getInsets().left + frame.getInsets().right, h + frame.getInsets().top + frame.getInsets().bottom );
  size(w, h);
}
void setup() {
  dragDropFile();
  //System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
  size(256, 256);
  image(loadImage("bg.png"), 0, 0);
}
String passs;
boolean hikaku(color a, color b) {
  boolean out = false;
  float r0 = red(a);
  float g0 = green(a);
  float b0 = blue(a);
  float r1 = red(b);
  float g1 = green(b);
  float b1 = blue(b);
  if (r0 > r1 && g0 > g1 && b0 > b1)out = true;
  return out;
}
PImage med(PImage in) {
  in.loadPixels();
  color[] data = new color[in.pixels.length];
  for (int i = 0; i < in.pixels.length; i++) {
    data[i] = in.pixels[i];
  }
  PImage out = in.get();
  for (int y = 0; y < in.height; y++) {
    for (int x = 0; x < in.width; x++) {
      color[] d = new color[512];
      float[] dd = new float[512];
      int c = 0;
      for (int i = -4; i < 5; i++) {
        for (int f = -4; f < 5; f++) {
          if (x+i >= 0 && x+i < in.width && y+f >= 0 && y+f < in.height) {
            if ((x+i)+((y+f)*in.width) < in.pixels.length) {
              d[c] = data[(x+i)+((y+f)*in.width)];
              dd[c] = red(data[(x+i)+((y+f)*in.width)])+green(data[(x+i)+((y+f)*in.width)])+blue(data[(x+i)+((y+f)*in.width)]);
            }
            c++;
            if (c > 511)c = 511;
          }
        }
      }
      for (int j=0; j<c; j++) {//0~8までのループ
        for (int i=0; i<c-1; i++) {//0~8までのループ
          if (dd[i]<dd[i+1]) {
            //配列の入れ替える処理
            float tmp=dd[i+1];
            color tmp2=d[i+1];
            dd[i+1]=dd[i];
            d[i+1]=d[i];
            dd[i]=tmp;
            d[i]=tmp2;
          }
        }
      }
      out.set(x, y, d[(c+0)/2+1]);
    }
    if (y%10 == 0)println(y+"/"+in.height);
  }
  return out;
}
PImage sett(PImage in, int x, int y, color c) {
  in.loadPixels();
  int a = (y*in.width)+x;
  if (in.pixels.length > a)in.pixels[a] = c;
  in.updatePixels();
  return in;
}
float gosa = 32;
boolean equal(color o, color t) {
  boolean a = false;
  float g = 0;
  g += red(o)-red(t);
  g += green(o)-green(t);
  g += blue(o)-blue(t);
  g /= 3;
  if (g < 0)g = -g;
  if (g < gosa)a = true;
  return a;
}
PImage denoise(PImage data) {
  PImage out = createImage(data.width, data.height, ARGB);
  out.loadPixels();
  int e0 =  data.get(0, 0);
  int e1 = color(0);
  int e2 = color(0);
  int e3 = color(0);
  int e4 = color(0);
  int b0 = color(0);
  int b1 = color(0);
  int b2 = color(0);
  int b3 = color(0);
  int b4 = color(0);
  for (int y = 0; y < data.height; y++) {
    for (int x = 0; x < data.width; x++) {
      e0 = data.get(x, y);
      e1 = data.get(x-1, y);
      e2 = data.get(x, y-1);
      e3 = data.get(x-1, y-1);
      b0 = data.get(x, y);
      b1 = data.get(x-1, y);
      b2 = data.get(x+1, y);
      b3 = data.get(x, y-1);
      b4 = data.get(x, y+1);
      if (!equal(e0, e1) && !equal(e0, e3) && !equal(e0, e4)) {
        e4 = e0;
        //if (x != 0 && y != 0)
        sett(out, x, y, e4);
      } else {
        sett(out, x, y, mix(mix(mix(mix(b0, b1), b2), b3), b4));
      }
    }
  }
  out.updatePixels();
  return out;
}
PImage x2(PImage data) {
  PImage out = createImage(data.width*2, data.height*2, ARGB);
  int e0, e1, e2, e3;
  int sa, sb, sc, sd, se, sf, sg, sh, si;

  out.loadPixels();
  for (int x = 0; x < data.width; x++) {
    for (int y = 0; y < data.height; y++) {
      sa = data.get(x, y);
      sett(out, x * 2, y * 2, sa);
      sett(out, (x * 2) + 1, y * 2, sa);
      sett(out, x * 2, (y * 2) + 1, sa);
      sett(out, (x * 2) + 1, (y * 2) + 1, sa);
    }
  }
  out.updatePixels();
  return out;
}
PImage x3(PImage data) {
  PImage out = createImage(data.width*3, data.height*3, ARGB);
  int e0, e1, e2, e3;
  int sa, sb, sc, sd, se, sf, sg, sh, si;

  out.loadPixels();
  for (int x = 0; x < data.width; x++) {
    for (int y = 0; y < data.height; y++) {
      sa = data.get(x, y);
      sett(out, (x * 3)+1, (y * 3)+0, sa);
      sett(out, (x * 3)+2, (y * 3)+0, sa);
      sett(out, (x * 3)+3, (y * 3)+0, sa);
      sett(out, (x * 3)+1, (y * 3)+1, sa);
      sett(out, (x * 3)+2, (y * 3)+1, sa);
      sett(out, (x * 3)+3, (y * 3)+1, sa);
      sett(out, (x * 3)+1, (y * 3)+2, sa);
      sett(out, (x * 3)+2, (y * 3)+2, sa);
      sett(out, (x * 3)+3, (y * 3)+2, sa);
    }
  }
  out.updatePixels();
  return out;
}
color mix(color a, color b) {
  color out = a;
  out = color((red(a)+red(b))/2, (green(a)+green(b))/2, (blue(a)+blue(b))/2, (alpha(a)+alpha(b))/2);
  return out;
}
PImage h2(PImage data) {
  PImage out = createImage(data.width/2, data.height/2, ARGB);
  out.loadPixels();
  for (int x = 0; x < data.width/2; x++) {
    for (int y = 0; y < data.height/2; y++) {
      color outc = data.get(x*2, y*2);
      outc = mix(outc, data.get((x*2)+1, (y*2)+0));
      outc = mix(outc, data.get((x*2)+2, (y*2)+0));
      outc = mix(outc, data.get((x*2)+1, (y*2)+1));
      outc = mix(outc, data.get((x*2)+2, (y*2)+1));
      outc = mix(outc, data.get((x*2)+3, (y*2)+1));
      outc = mix(outc, data.get((x*2)+1, (y*2)+2));
      outc = mix(outc, data.get((x*2)+2, (y*2)+2));
      outc = mix(outc, data.get((x*2)+3, (y*2)+2));
      sett(out, x, y, outc);
    }
  }
  out.updatePixels();
  return out;
}
PImage h3(PImage data) {
  PImage out = createImage(data.width/3, data.height/3, ARGB);
  out.loadPixels();
  for (int x = 0; x < data.width/3; x++) {
    for (int y = 0; y < data.height/3; y++) {
      color outc = data.get(x*3, y*3);
      outc = mix(outc, data.get((x*3)+1, (y*3)+1));
      outc = mix(outc, data.get((x*3)+2, (y*3)+2));
      outc = mix(outc, data.get((x*3)+0, (y*3)+0));
      outc = mix(outc, data.get((x*3)+1, (y*3)+1));
      outc = mix(outc, data.get((x*3)+2, (y*3)+2));
      outc = mix(outc, data.get((x*3)+0, (y*3)+0));
      outc = mix(outc, data.get((x*3)+1, (y*3)+1));
      outc = mix(outc, data.get((x*3)+2, (y*3)+2));
      sett(out, x, y, outc);
    }
  }
  out.updatePixels();
  return out;
}
PImage s(PImage data) {
  PImage out = createImage(data.width, data.height, ARGB);
  out.loadPixels();
  for (int x = 0; x < data.width; x++) {
    for (int y = 0; y < data.height; y++) {
      color outc = data.get(x, y);
      outc = mix(outc, data.get(x+1, y));
      outc = mix(outc, data.get(x, y));
      outc = mix(outc, data.get(x, y));
      outc = mix(outc, data.get(x-1, y));
      outc = mix(outc, data.get(x, y));
      outc = mix(outc, data.get(x, y));
      outc = mix(outc, data.get(x, y+1));
      outc = mix(outc, data.get(x, y));
      outc = mix(outc, data.get(x, y));
      outc = mix(outc, data.get(x, y-1));
      outc = mix(outc, data.get(x, y));
      outc = mix(outc, data.get(x, y));
      sett(out, x, y, outc);
    }
  }
  out.updatePixels();
  return out;
}
color sabun(color i, color f) {
  return color(red(i)-red(f), green(i)-green(f), blue(i)-blue(f));
}
color plus(color i, color f) {
  return color(red(i)+red(f), green(i)+green(f), blue(i)+blue(f));
}
color plus(color i, color f, float w) {
  return color(red(i)+(red(f)/w), green(i)+(green(f)/w), blue(i)+(blue(f)/w));
}
PImage sharp(PImage data) {
  PImage out = data;
  for (int x = 0; x < data.width; x++) {
    for (int y = 0; y < data.height; y++) {
      color m = data.get(x, y);
      color a = data.get(x-1, y);
      color b = data.get(x, y+1);
      color c = data.get(x, y-1);
      color d = data.get(x+1, y);
      a = sabun(m, a);
      b = sabun(m, b);
      c = sabun(m, c);
      d = sabun(m, d);
      m = plus(m, a, 12);
      m = plus(m, b, 12);
      m = plus(m, c, 12);
      m = plus(m, d, 12);
      out.set(x, y, m);
    }
  }
  return out;
}
void conv(String pass) {
  PImage data = loadImage(pass);
  if (data != null) {
    println("pass:"+pass);
    print("一時データパス取得");
    String tempfolder = dataPath(".bmp");
    String[] ka = splitTokens(pass, ".");
    String k = "."+ka[ka.length-1];
    /*
    Mat matrixDst = new Mat();
     Mat matrixin = new Mat();
     println("-OK");
     print("CVノイズキャンセリング");
     //sharp(loadImage(pass)).save(pass+".sharp.png");
     matrixin = Highgui.imread(pass);
     if (denoise) {
     Imgproc.bilateralFilter(matrixin, matrixDst, 30, 40, 10);
     } else {
     matrixDst = matrixin;
     }
     println("-OK");
     //Highgui.imwrite(tempfolder, matrixDst);
     print("CVスケーリング");
     Size sca = new Size(int(data.width*(s*4)), int(data.height*(s*4)));
     Imgproc.resize(matrixDst, matrixDst, sca, 0, 0, Imgproc.INTER_NEAREST);
     println("-OK");
     //opencv.medianBlur(opencv,opencv,19);
     //print("CV画像拡大");
     Size sz = new Size(int(data.width*(s*6)), int(data.height*(s*6)));
     //Imgproc.resize(matrixDst, matrixDst, sz, 0, 0, Imgproc.INTER_AREA);
     //println("-OK");
     //opencv.medianBlur(opencv,opencv,19);
     print("CVメディアンブラー適用");
     Imgproc.medianBlur(matrixDst, matrixDst, (int((s*2))*3)+1);
     println("-OK");
     print("CV画像縮小");
     sz = new Size(data.width*(s*2), data.height*(s*2));
     Imgproc.resize(matrixDst, matrixDst, sz, 0, 0, Imgproc.INTER_AREA);
     println("-OK");
     print("CV画像保存");
     Highgui.imwrite(tempfolder, matrixDst);
     println("-OK");
     print("最終処理");
     //PImage out = createImage(1, 1, ARGB);
     //save(tempfolder);
     delay(100);
     byte[] a = {0};
     if (uwagaki)k = "";
     PImage one = loadImage(tempfolder);
     one = sharp(one);
     //one = one.get((one.width/3), (one.height/3), (one.width/3), (one.height/3));
     //one = h2(one);
     Save(one, pass+k);
     //saveBytes(tempfolder, a);
     //saveBytes(tempfolder+".a.bmp", a);
     println("-OK");
     println("完了");
     */
    data = x2(data);
    data = x2(data);
    h2(med(data)).save(pass+k);
    println("完了");
  } else {
    println("No Found File");
  }
}
void loadImages(String p) {
  File selection = new File(p);
  if (selection.isDirectory()) {
    File[] files = selection.listFiles();   //ディレクトリ内の全てのファイル(ディレクトリも含む)を取得
    for (int i = 0; i < files.length; i++) {
      println(i+"/"+files.length);
      conv(files[i].getAbsolutePath());
      println((i+1)+"/"+files.length);
    }
  } else {
    println("error:"+p);
  }
}
void draw() {
  if (pass != null) {
    passs = pass;
    PImage data = loadImage(pass);
    if (data != null) {
      conv(pass);
    } else {
      loadImages(pass+"/");
    }
    pass = null;
  }
}

import com.sun.image.codec.jpeg.*;
/*
public void Save(PImage in, String filename) { 
  java.awt.image.BufferedImage img=(BufferedImage)in.getImage();
  String extn=filename.substring(filename.lastIndexOf('.')+1).toLowerCase(); 
  if (extn.equals("jpg")) {

    try {
      ByteArrayOutputStream out = new ByteArrayOutputStream();
      JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
      JPEGEncodeParam p = encoder.getDefaultJPEGEncodeParam(img);
      // set JPEG quality to 95% with baseline optimization
      p.setQuality(0.95, true);
      encoder.setJPEGEncodeParam(p);
      encoder.encode(img);

      File file = new File(savePath(filename));
      FileOutputStream fo = new FileOutputStream(file);
      out.writeTo(fo);
    }
    catch(FileNotFoundException e) {
      System.out.println(e);
    }
    catch(IOException ioe) {
      System.out.println(ioe);
    }
  } else { 
    in.save(filename);
  }
}
*/

これは、「メディアンフィルタ」という処理をしています。

例えば、5ドット×5ドットの四角形の中の一番明るい色と一番暗い色の中間を表示するようなプログラムです。

「メディアンフィルタ」で検索してみてください。

This is processing “median filter”.

For example, it is a program that displays the middle between the brightest and darkest colors in a 5 dot × 5 dot rectangle.

Please search with “median filter”.

私が作ったソフトの特徴は、一枚の写真だけでなく、フォルダごとドラッグ&ドロップすることにより、フォルダ内全部の画像を一括処理できることです。

The feature of the software I made is that you can collectively process all the images in a folder by dragging and dropping each folder as well as one photo.

ソースコード内の57行目と58行目↓The 57th and 58th lines in the source code ↓

 for (int i = -4; i < 5; i++) { for (int f = -4; f < 5; f++) {

↑この数値を変えることにより、滑らかにする度合いを変えることができます。

注意点は、()内の左のマイナスの数字より、右のプラスの数字を1つ大きくしてください。

この「メディアンフィルタ」を使うと、ノイズを除去して、画像が滑らかに見えます。

↑By changing this value, you can change the degree of smoothing.

Please be careful to increase the number of the right plus one by one from the minus number on the left in ().

With this “median filter”, noise is removed and the image looks smooth.

Download zip file here ↓

Zipダウンロードは、こちら

 

 

コメントを残す