Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
近デジ3:4でトリミング
/* 近代デジタルの画像をページ分割するヤツ(page_split)
   opencvを使う.
*/
#if 0
#include <cv.h>
#include <highgui.h>
#include <math.h>
#else
#include <opencv/cv.h>
#include <opencv/highgui.h>
#include <math.h>
#endif
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#define _DEBUG 0
/* 処理するときの縦サイズ(px).
小さいほうが速いけど、小さすぎると小さい線が見つからなくなる.
cvHoughLines2のパラメータもこれをベースに決める.
*/
#define SCALE_SIZE 640.0f
/* 最低オフセットが存在することを想定する(px).
真ん中にある謎の黒い線の上下をtop,bottomに選択しないためのテキトウな処理で使う. */
#define HARD_OFFSET 32
/* ページ中心のズレの許容範囲(px / 2).
余白を切った矩形の中心からこの距離範囲の間でページの中心を決める.
*/
#define CENTER_RANGE 64
/* 中心を決める際に使う線の最大数.
*/
#define CENTER_SAMPLE_MAX 1024
/* 中心決めるときのクラスタ数 */
#define CENTER_K 3
int
float_cmp(const void *p1, const void *p2)
{
if (*(const float *)p1 > *(const float *)p2) {
return 1;
} else if (*(const float *)p1 < *(const float *)p2) {
return -1;
}
return 0;
}
/* 3:4になるようページの範囲を返す */
int
page_split_34(IplImage *img,
CvRect *left_page, CvRect *right_page)
{
int i, j;
IplImage *edges, *small_img;
float scale;
CvMemStorage *storage;
CvSeq *lines = 0;
int top, left, right, bottom;
if (img == NULL) {
return -1;
}
/* リサイズしたエッジ画像から確率的ハフ変換で線を検出する */
scale = SCALE_SIZE / img->height;
small_img = cvCreateImage(
cvSize((int)(img->width * scale),
(int)(img->height * scale)), img->depth, img->nChannels);
edges = cvCreateImage(cvGetSize(small_img), small_img->depth, 1);
cvResize(img, small_img, 1);
cvCvtColor(small_img, edges, CV_BGR2GRAY);
cvCanny(edges, edges, 50, 200, 3);
lines = 0;
storage = cvCreateMemStorage(0);
lines = cvHoughLines2(edges, storage,
CV_HOUGH_PROBABILISTIC, 1, CV_PI / 2, 50, 32, 16);
/* 余白を切る範囲を決める */
top = INT_MAX;
bottom = 0;
left = INT_MAX;
right = 0;
for (i = 0; i < lines->total; i++) {
CvPoint *points = (CvPoint *) cvGetSeqElem (lines, i);
for (j = 0; j < 2; ++j) {
if (top > points[j].y
&& points[j].y > HARD_OFFSET)
{
top = points[j].y;
}
if (bottom < points[j].y
&& points[j].y < img->height - HARD_OFFSET)
{
bottom = points[j].y;
}
if (left > points[j].x
&& points[j].x > HARD_OFFSET)
{
left = points[j].x;
}
if (right < points[j].x
&& points[j].x < img->width - HARD_OFFSET)
{
right = points[j].x;
}
}
}
left_page->x = (int)(left / scale);
left_page->y = (int)(top / scale);
left_page->height = (int)((bottom - top) / scale);
left_page->width = (int)(left_page->height * 0.75);
right_page->y = (int)(top / scale);
right_page->height = (int)((bottom - top) / scale);
right_page->x = (int)(right / scale - right_page->height * 0.75);
right_page->width = (int)(right_page->height * 0.75);
return 0;
}
/* imgに近デジの画像を渡すと左右のページの範囲を返す処理 */
int
page_split(IplImage *img,
CvRect *left_page, CvRect *right_page)
{
int i, j;
IplImage *edges, *small_img;
float scale;
CvMemStorage *storage;
CvSeq *lines = 0;
int top, left, right, bottom,
center, center_base, samples;
CvMat *center_samples = cvCreateMat(CENTER_SAMPLE_MAX, 1, CV_32FC1);
CvMat *labels = cvCreateMat(CENTER_SAMPLE_MAX, 1, CV_32SC1);
if (img == NULL) {
return -1;
}
/* リサイズしたエッジ画像から確率的ハフ変換で線を検出する */
scale = SCALE_SIZE / img->height;
small_img = cvCreateImage(
cvSize((int)(img->width * scale),
(int)(img->height * scale)), img->depth, img->nChannels);
edges = cvCreateImage(cvGetSize(small_img), small_img->depth, 1);
cvResize(img, small_img, 1);
cvCvtColor(small_img, edges, CV_BGR2GRAY);
cvCanny(edges, edges, 50, 200, 3);
lines = 0;
storage = cvCreateMemStorage(0);
lines = cvHoughLines2(edges, storage,
CV_HOUGH_PROBABILISTIC, 1, CV_PI / 2, 50, 32, 16);
/* 余白を切る範囲を決める */
top = INT_MAX;
bottom = 0;
left = INT_MAX;
right = 0;
for (i = 0; i < lines->total; i++) {
CvPoint *points = (CvPoint *) cvGetSeqElem (lines, i);
if (abs(points[0].x - points[1].x) > abs(points[0].y - points[1].y)) {
/* 横線 */
for (j = 0; j < 2; ++j) {
if (top > points[j].y
&& points[j].y > HARD_OFFSET)
{
top = points[j].y;
}
if (bottom < points[j].y
&& points[j].y < img->height - HARD_OFFSET)
{
bottom = points[j].y;
}
}
} else {
/* 縦線 */
for (j = 0; j < 2; ++j) {
if (left > points[j].x
&& points[j].x > HARD_OFFSET)
{
left = points[j].x;
}
if (right < points[j].x
&& points[j].x < img->width - HARD_OFFSET)
{
right = points[j].x;
}
}
}
#if _DEBUG
{ /* 検出した線をimgに描く(debug用) */
CvPoint tmp[2];
tmp[0] = points[0];
tmp[1] = points[1];
tmp[0].x = (int)(points[0].x / scale);
tmp[0].y = (int)(points[0].y / scale);
tmp[1].x = (int)(points[1].x / scale);
tmp[1].y = (int)(points[1].y / scale);
cvLine(img, tmp[0], tmp[1], CV_RGB (255, 0, 0), 1, 8, 0);
if (abs(points[0].x - points[1].x) > abs(points[0].y - points[1].y)) {
cvLine(img, tmp[0], tmp[1], CV_RGB (0, 0, 255), 1, 8, 0);
} else {
cvLine(img, tmp[0], tmp[1], CV_RGB (0, 255, 0), 1, 8, 0);
}
}
#endif
}
/* 左右の分割点を決める */
/* 中心あたりをサンプリング */
center_base = left + (right - left) / 2;
center = center_base;
samples = 0;
cvZero(center_samples);
cvZero(labels);
for (i = 0; i < lines->total; i++) {
CvPoint *points = (CvPoint *)cvGetSeqElem(lines, i);
if (abs(points[0].x - points[1].x) < abs(points[0].y - points[1].y))
{
/* 縦線 */
for (j = 0; j < 2; ++j) {
int dist = abs(points[j].x - center_base);
if (dist < CENTER_RANGE) {
CV_MAT_ELEM(*center_samples, float, samples, 0) = (float)points[j].x;
if (++samples >= CENTER_SAMPLE_MAX) {
goto _break;
}
#if _DEBUG
{ /* サンプリングした線をimgに描く(debug用) */
CvPoint tmp[2];
tmp[0] = points[0];
tmp[1] = points[1];
tmp[0].x = (int)(points[0].x / scale);
tmp[0].y = (int)(points[0].y / scale);
tmp[1].x = (int)(points[1].x / scale);
tmp[1].y = (int)(points[1].y / scale);
cvLine(img, tmp[0], tmp[1], CV_RGB (255, 0, 0), 1, 8, 0);
}
#endif
}
}
}
}
_break:
/* クラスタリングして中心を決める */
if (samples > CENTER_K) {
float label_centers[CENTER_K] = {0};
int label_counts[CENTER_K] = {0};
center_samples->rows = samples;
labels->rows = samples;
cvKMeans2(center_samples, CENTER_K, labels, cvTermCriteria(CV_TERMCRIT_ITER, 50, 1.0),
1, 0, 0, 0, 0);
for (i = 0; i < samples; ++i) {
int label = CV_MAT_ELEM(*labels, int, i, 0);
++label_counts[label];
label_centers[label] += CV_MAT_ELEM(*center_samples, float, i, 0);
}
for (i = 0; i < CENTER_K; ++i) {
if (label_counts[i] > 0) {
label_centers[i] /= label_counts[i];
}
}
qsort(label_centers, CENTER_K, sizeof(float), float_cmp);
center = (int)label_centers[CENTER_K / 2];
} else {
center = center_base;
}
left_page->x = (int)(left / scale);
left_page->y = (int)(top / scale);
left_page->width = (int)(center / scale) - left_page->x;
left_page->height = (int)(bottom / scale) - left_page->y;
right_page->x = (int)(center / scale);
right_page->y = (int)(top / scale);
right_page->width = (int)(right / scale) - right_page->x;
right_page->height = left_page->height;
center_samples->rows = CENTER_SAMPLE_MAX;
labels->rows = CENTER_SAMPLE_MAX;
cvReleaseImage (&small_img);
cvReleaseImage (&edges);
cvReleaseMemStorage (&storage);
cvReleaseMat(&center_samples);
cvReleaseMat(&labels);
return 0;
}
IplImage*
integral(int c, char** v, int from, int to) {
int i, j;
IplImage *img_a, *img_b;
img_a = cvLoadImage(v[from], CV_LOAD_IMAGE_COLOR);
if (img_a == NULL) {
return NULL;
}
if (c < to) {
to = c;
}
for(i = from+1; i < to; i++) {
img_b = cvLoadImage(v[i], CV_LOAD_IMAGE_COLOR);
if (img_b == NULL) {
return NULL;
}
for(j = 0; j < img_a->imageSize; j++) {
img_a->imageData[j] = (img_a->imageData[j] + img_b->imageData[j]) / 2;
}
cvReleaseImage (&img_b);
}
return img_a;
}
/* 保存用ファイル名 */
char*
file_with_suffix(char* file_in, char* suffix) {
char *file_save, *basename, *extname;
int i, len_in;
len_in = strlen(file_in);
basename = extname = NULL;
for(i = len_in; i >= 0; i--) {
if (file_in[i] == '.') {
basename = malloc(sizeof(char) * (i));
if (basename == NULL) {
return NULL;
}
strncpy(basename, file_in, i);
basename[i] = '\0';
extname = malloc(sizeof(char) * (len_in - i));
if (extname == NULL) {
return NULL;
}
strncpy(extname, file_in + i + 1, len_in - i - 1);
extname[len_in - i - 1] = '\0';
break;
}
}
file_save = malloc(sizeof(char) * (strlen(file_in) + strlen(suffix) + 1));
if (file_save == NULL) {
return NULL;
}
if (basename != NULL) {
sprintf(file_save, "%s-%s.%s", basename, suffix, extname);
free(basename);
free(extname);
} else {
sprintf(file_save, "%s-%s", file_in, suffix);
}
return file_save;
}
int
trim_image(char* file, IplImage *img, CvRect* left_page, CvRect* right_page) {
char* filename;
cvSetImageROI(img, *right_page);
filename = file_with_suffix(file, "0");
cvSaveImage(filename, img, 0);
free(filename);
cvSetImageROI(img, *left_page);
filename = file_with_suffix(file, "1");
cvSaveImage(filename, img, 0);
free(filename);
return 0;
}
int
trimming(char* file) {
int ret;
IplImage *img;
CvRect left_page, right_page;
img = cvLoadImage(file, CV_LOAD_IMAGE_COLOR);
if (img == NULL) {
return -1;
}
/* ページの範囲を取得 */
ret = page_split_34(img, &left_page, &right_page);
if (ret != 0) {
return -1;
}
trim_image(file, img, &left_page, &right_page);
cvReleaseImage (&img);
return 0;
}
/* 何枚か重ねてトリミング範囲を決めてトリミング */
int trimming_with_integral(int num, char **files) {
int ret, i;
IplImage *iimg, *img;
CvRect left_page, right_page;
iimg = NULL;
for(i = 0; i < num; i++) {
if (i == 0) {
if (iimg != NULL) {
cvReleaseImage (&iimg);
}
iimg = integral(num, files, i, i+20);
ret = page_split_34(iimg, &left_page, &right_page);
if (ret != 0) {
return -1;
}
}
img = cvLoadImage(files[i], CV_LOAD_IMAGE_COLOR);
if (img == NULL) {
return -1;
}
trim_image(files[i], img, &left_page, &right_page);
cvReleaseImage (&img);
}
return 0;
}
int
main (int argc, char **argv)
{
trimming_with_integral(argc-1, argv+1);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment