-
-
Save hitode909/665238 to your computer and use it in GitHub Desktop.
近デジ3:4でトリミング
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 近代デジタルの画像をページ分割するヤツ(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(¢er_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) + 10)); | |
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; | |
iimg = integral(num, files, num/2, num/2+20); | |
ret = page_split_34(iimg, &left_page, &right_page); | |
if (ret != 0) { | |
return -1; | |
} | |
for(i = 0; i < num; i++) { | |
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